summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm/type1.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/dvipdfm/type1.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm/type1.c')
-rw-r--r--Build/source/texk/dvipdfm/type1.c1372
1 files changed, 1372 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm/type1.c b/Build/source/texk/dvipdfm/type1.c
new file mode 100644
index 00000000000..77eb1e46e2d
--- /dev/null
+++ b/Build/source/texk/dvipdfm/type1.c
@@ -0,0 +1,1372 @@
+/* $Header$
+
+ This is dvipdfm, a DVI to PDF translator.
+ Copyright (C) 1998, 1999 by Mark A. Wicks
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The author may be contacted via the e-mail address
+
+ mwicks@kettering.edu
+*/
+
+/* This is tailored for PDF */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <ctype.h>
+#include "system.h"
+#include "mem.h"
+#include "error.h"
+#include "mfileio.h"
+#include "pdfobj.h"
+#include "numbers.h"
+#include "type1.h"
+#include "tfm.h"
+#include "pdfparse.h"
+#include "pdflimits.h"
+#include "t1crypt.h"
+#include "twiddle.h"
+#include "encodings.h"
+
+static unsigned char verbose = 0;
+
+void type1_set_verbose(void)
+{
+ if (verbose < 255) {
+ verbose += 1;
+ }
+}
+
+
+static unsigned long get_low_endian_quad (FILE *file)
+{
+ unsigned long result;
+ static unsigned bytes[4];
+ int ch, i;
+ for (i=0; i<4; i++) {
+ if ((ch = fgetc (file)) < 0) {
+ ERROR ("get_low_endian_quad: Error reading file");
+ }
+ bytes[i] = ch;
+ }
+ result = bytes[3];
+ for (i=2; i>=0; i--) {
+ result = result*256u + bytes[i];
+ }
+ return result;
+}
+
+/* PFB section */
+
+static char partial_enabled = 1;
+
+void type1_disable_partial (void)
+{
+ partial_enabled = 0;
+}
+
+static unsigned num_pfbs = 0;
+static unsigned max_pfbs = 0;
+struct a_pfb
+{
+ char *pfb_name;
+ char *fontname;
+ pdf_obj *direct, *indirect, *descriptor;
+ char **used_glyphs;
+ char **int_encoding;
+ char *used_def_enc_chars; /* The positions used from the default
+ encoding. When a default encoding
+ is used, the glyph names will not
+ be known until the font is actually
+ read. Since the glyph names are
+ unknown, only the positions of the
+ used chars are stored when the
+ default encoding is used */
+ unsigned n_used_glyphs, max_used_glyphs;
+} *pfbs = NULL;
+
+static void init_a_pfb (struct a_pfb *pfb)
+{
+ int i;
+ pfb -> n_used_glyphs = 0;
+ pfb -> max_used_glyphs = 0;
+ pfb -> used_glyphs = NULL;
+ pfb->pfb_name = NULL;
+ pfb->fontname = NULL;
+ pfb->direct = NULL;
+ pfb->indirect = NULL;
+ pfb->descriptor = NULL;
+ pfb -> used_def_enc_chars = NULL;
+ if (partial_enabled) {
+ pfb -> int_encoding = NEW (256, char *);
+ for (i=0; i<256; i++) {
+ (pfb -> int_encoding)[i] = NULL;
+ }
+ } else {
+ pfb -> int_encoding = NULL;
+ }
+}
+
+#include "standardenc.h"
+
+static void do_a_standard_enc(char **glyphs, char **encoding)
+{
+ int i;
+ for (i=0; i<256; i++) {
+ RELEASE (glyphs[i]);
+ glyphs[i] = NEW (strlen(encoding[i])+1, char);
+ strcpy (glyphs[i], encoding[i]);
+ }
+}
+#define FIXED_WIDTH 1
+#define SERIF 2
+#define STANDARD 32
+#define ITALIC 64
+#define SYMBOLIC 4 /* Fonts that don't have Adobe encodings (e.g.,
+ cmr, should be set to be symbolic */
+#define STEMV 80
+
+
+int CDECL glyph_cmp (const void *v1, const void *v2)
+{
+ char *s1, *s2;
+ s1 = *((char **) v1);
+ s2 = *((char **) v2);
+ return (strcmp (s1, s2));
+}
+
+int CDECL glyph_match (const void *key, const void *v)
+{
+ char *s;
+ s = *((char **) v);
+ return (strcmp (key, s));
+}
+
+static unsigned long parse_header (unsigned char *filtered, unsigned char *buffer,
+ unsigned long length, int pfb_id)
+{
+ /* If the encoding has been overridden, this routine should eliminate any
+ built-in encoding (other than things like StandardEncoding) in
+ the header to save space */
+
+ /* On second thought, that's the way it _should_ work, but the
+ reader doesn't seem to agree. The reader is happy if you don't
+ specify an encoding as long as you actually change the locations
+ in the overriding encoding. The reader is unhappy if you don't
+ specify an encoding and don't change the location of the
+ characters in the overriding encoding. Ghostscript doesn't
+ have a problem with it. */
+
+ unsigned char *filtered_pointer;
+ int state = 0;
+ char *start, *end, *lead;
+ int copy = 1;
+ int last_number = 0;
+ char *glyph = NULL;
+#ifdef MEM_DEBUG
+ MEM_START
+#endif
+ /* This routine uses a state machine parser rather than trying to
+ interpret Postcript the way mpost.c does. There are only
+ a few key parameters it is trying to find */
+
+ /* State definitions
+ state 0: Initial state
+ state 1: Saw /FontName
+ state 2: Saw /Encoding
+ state 3: Saw "dup" in state 2
+ state 4: Saw a number in state 3
+ state 5: Saw /glyphname in state 4
+ state 6: Saw /FontBBox in state 0
+ state 7: Saw a '{' or a '[' in state 6
+ state 8: Saw /ItalicAngle in state 0 */
+ /* As the parser operates, start always points to the next point in
+ the buffer */
+ start = (char *) buffer;
+ end = start+length;
+ filtered_pointer = filtered;
+ /* When the parser decides to keep text, the text
+ between lead and start is copied to the output buffer. To
+ keep a block of text from being copied, the parser simply
+ sets copy = 0 */
+ lead = start;
+ skip_white (&start, end);
+ if (filtered && lead != start) {
+ memcpy (filtered_pointer, lead, start-lead);
+ filtered_pointer += start-lead;
+ lead = start;
+ }
+ while (start < end) {
+ char *ident;
+ pdf_obj *pdfobj;
+ copy = 1; /* By default, we copy most things */
+ switch (state) {
+ /* First three states are very similar. In most cases we just
+ ignore other postscript junk and don't change state */
+ case 0:
+ case 1:
+ case 2:
+ switch (*start) {
+ /* Ignore arrays and procedures */
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ start += 1;
+ if (state >= 2)
+ state = 2;
+ break;
+ case '(':
+ pdfobj = parse_pdf_string (&start, end);
+ if (pdfobj == NULL) {
+ ERROR ("parse_header: Error parsing a string in pfb header");
+ }
+ pdf_release_obj (pdfobj);
+ if (state == 1) {
+ if (filtered)
+ sprintf ((char *)filtered_pointer, "/%s ", pfbs[pfb_id].fontname);
+ filtered_pointer += strlen ((char *)filtered_pointer);
+ copy = 0; /* Don't copy old string to output */
+ lead = start; /* Forget what we've seen */
+ state = 0;
+ }
+ if (state >= 2)
+ state = 2;
+ break;
+ case '/':
+ start += 1;
+ ident = parse_ident (&start, end);
+ if (state == 0 && !strcmp (ident, "FontName")) {
+ state = 1;
+ } else if (state == 0 && !strcmp (ident, "Encoding")) {
+ state = 2;
+ if (filtered && !pfbs[pfb_id].used_def_enc_chars) {
+ sprintf ((char *)filtered_pointer, "/Encoding StandardEncoding readonly ");
+ filtered_pointer += strlen ((char *)filtered_pointer);
+ }
+ } else if (state == 0 && !strcmp (ident, "FontBBox")) {
+ state = 6;
+ } else if (state == 0 && !strcmp (ident, "ItalicAngle")) {
+ state = 8;
+ } else if (state == 1) {
+ if (filtered) {
+ sprintf ((char *)filtered_pointer, "/%s ", pfbs[pfb_id].fontname);
+ filtered_pointer += strlen ((char *)filtered_pointer);
+ }
+ copy = 0; /* Don't copy old string to putput */
+ lead = start; /* Forget the name we've seen */
+ state = 0;
+ }
+ RELEASE (ident);
+ break;
+ default:
+ ident = parse_ident (&start, end);
+ if (state == 2 && !strcmp (ident, "def")) {
+ /* Assume this is the end of the encoding */
+ state = 0;
+ } else if (state == 2 &&
+ !strcmp (ident, "dup")) {
+ copy = 0; /* Don't copy this to output buffer until we
+ know if we want to keep it */
+ state = 3;
+ } else if (state == 2 &&
+ !strcmp (ident, "StandardEncoding") &&
+ pfbs[pfb_id].int_encoding) {
+ do_a_standard_enc(pfbs[pfb_id].int_encoding, standardencoding);
+ } else if (state == 2 &&
+ !strcmp (ident, "ISOLatin1Encoding") &&
+ pfbs[pfb_id].int_encoding) {
+ do_a_standard_enc(pfbs[pfb_id].int_encoding, isoencoding);
+ }
+ RELEASE (ident);
+ break;
+ }
+ break;
+ case 3:
+ ident = parse_ident (&start, end);
+ if (is_an_int (ident)) {
+ last_number = (int) atof (ident);
+ copy = 0; /* We still don't know if we want to keep it */
+ state = 4;
+ } else {
+ state = 2; /* Contents back to "lead" will be flushed */
+ }
+ RELEASE (ident);
+ break;
+ case 4:
+ if (*start == '/') {
+ start += 1;
+ glyph = parse_ident (&start, end);
+ copy = 0; /* We still don't know if we want to keep it.
+ Wait for a complete sequence before making
+ that decision */
+ state = 5;
+ } else {
+ state = 2;
+ }
+ break;
+ case 5:
+ ident = parse_ident (&start, end);
+ /* Here we either decide to keep or remove the encoding entry */
+ if (ident != NULL && !strcmp (ident, "put") &&
+ (int) last_number < 256 && (int) last_number >= 0) {
+ skip_white(&start, end); /* Remove white space */
+ lead = start; /* Remove this entry (it may or may not be
+ replaced with a rewritten entry) */
+ copy = 0;
+ if (filtered &&
+ pfbs[pfb_id].used_def_enc_chars &&
+ (pfbs[pfb_id].used_def_enc_chars)[last_number]) {
+ sprintf((char *) filtered_pointer, "dup %d /%s put\n",
+ last_number,
+ glyph);
+ filtered_pointer += strlen ((char *) filtered_pointer);
+ }
+ /* Add this glyph to the internal encoding table for the pfb
+ */
+ if (pfbs[pfb_id].int_encoding &&
+ (!(pfbs[pfb_id].int_encoding)[last_number])) {
+ if ((pfbs[pfb_id].int_encoding)[last_number]) {
+ RELEASE ((pfbs[pfb_id].int_encoding)[last_number]);
+ }
+ (pfbs[pfb_id].int_encoding)[last_number] = glyph;
+ glyph = NULL; /* Prevent glyph from being released */
+ }
+ }
+ if (glyph)
+ RELEASE (glyph);
+ if (ident != NULL)
+ RELEASE (ident);
+ state = 2;
+ break;
+ case 6:
+ switch (*start) {
+ case '[':
+ case '{':
+ start += 1 ;
+ state = 7;
+ break;
+ default:
+ state = 0; /* Something's probably wrong */
+ fprintf (stderr, "\nUnexpected token after FontBBox. Struggling along\n");
+ dump (start, end);
+ }
+ break;
+ case 7:
+ switch (*start) {
+ case ']':
+ case '}':
+ start += 1 ;
+ state = 0;
+ break;
+ case '{':
+ case '[':
+ case '(':
+ case '/':
+ state = 0; /* Something's probably wrong */
+ fprintf (stderr, "\nUnexpected token in FontBBox array. Struggling along\n");
+ dump (start, end);
+ break;
+ default:
+ ident = parse_ident (&start, end);
+ if ((ident) && is_a_number (ident)) {
+ pdf_obj *tmp = pdf_lookup_dict (pfbs[pfb_id].descriptor,
+ "FontBBox");
+ pdf_add_array (tmp, pdf_new_number (atof (ident)));
+ }
+ if (ident)
+ RELEASE (ident);
+ }
+ break;
+ case 8:
+ switch (*start) {
+ case '{': case '}': case '[': case ']': case '/':
+ state = 0;
+ break;
+ default:
+ ident = parse_ident (&start, end);
+ if ((ident) && is_a_number (ident)) {
+ double italic = atof(ident);
+ if (italic != 0.0) {
+ int flags = (int) pdf_number_value(pdf_lookup_dict (pfbs[pfb_id].descriptor,
+ "Flags"));
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("ItalicAngle"),
+ pdf_new_number (italic));
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("Flags"),
+ pdf_new_number (flags+ITALIC));
+ }
+ }
+ if (ident)
+ RELEASE (ident);
+ state = 0;
+ }
+ }
+ skip_white (&start, end);
+ if (state >=2 && state <= 5 && !pfbs[pfb_id].used_def_enc_chars) {
+ lead = start;
+ }
+ if (copy && start != lead) { /* Flush everything back to "lead" */
+ if (filtered) {
+ memcpy (filtered_pointer, lead, start-lead);
+ filtered_pointer += start-lead;
+ }
+ lead = start;
+ }
+ }
+#ifdef MEM_DEBUG
+ MEM_END
+#endif /* MEM_DEBUG */
+ return filtered? filtered_pointer-filtered: length;
+}
+
+static void dump_glyphs( char **glyphs, int n, int show_index)
+{
+ int i;
+ for (i=0; i<n; i++) {
+ if (show_index)
+ fprintf (stderr, "(%d", i);
+ if (glyphs[i])
+ fprintf (stderr, "/%s", glyphs[i]);
+ else
+ fprintf (stderr, "(null)");
+ if (show_index)
+ fprintf (stderr, ")");
+ }
+ return;
+}
+static void dump_used( char *used_chars)
+{
+ int i;
+ for (i=0; i<256; i++)
+ fprintf (stderr, "(%d/%d)", i, used_chars[i]);
+ return;
+}
+
+
+#define ASCII 1
+#define BINARY 2
+
+static int is_a_pfb (FILE *file)
+{
+ int result, ch;
+ if ((ch = fgetc (file)) == 128 &&
+ ((ch = fgetc (file)) == ASCII ||
+ (ch == BINARY))) {
+ result = 1;
+ } else
+ result = 0;
+ rewind (file);
+ return result;
+}
+
+static unsigned char *get_pfb_segment (unsigned long *length,
+ FILE *file, int expected_type)
+{
+ unsigned char *buffer = NULL;
+ unsigned long nread;
+ unsigned long new_length;
+ int stream_type, ch;
+
+ *length = 0;
+ /* Unfortunately, there can be several segments that need to be
+ concatenated, so we loop through all of them */
+ for (;;) {
+ if ((ch = fgetc (file)) < 0) /* Some files don't terminate
+ properly */
+ break;
+ if (ch != 128){
+ sprintf (work_buffer, "get_pfb_segment: Not a pfb file.\n");
+ sprintf (work_buffer, "get_pfb_segment: pfb header has %d, expecting 128\n", ch);
+ ERROR (work_buffer);
+ }
+ if ((stream_type = fgetc (file)) < 0 || stream_type != expected_type) {
+ seek_relative (file, -2); /* Backup up two (yuck!) */
+ break;
+ }
+ new_length = get_low_endian_quad (file);
+ if (verbose > 4) {
+ fprintf (stderr, "Length of next PFB segment: %ld\n",
+ new_length);
+ }
+ buffer = RENEW (buffer, (*length)+new_length, unsigned char);
+ if ((nread = fread(buffer+(*length), sizeof(unsigned char), new_length, file)) !=
+ new_length) {
+ fprintf (stderr, "Found only %ld/%ld bytes\n", nread, new_length);
+ ERROR ("type1_do_pfb_segment: Are you sure this is a pfb?");
+ }
+ *length += new_length;
+ }
+ if (*length == 0) {
+ ERROR ("type1_get_pfb_segment: Segment length is zero");
+ }
+ if (expected_type == ASCII) {
+ int i;
+ for (i=0; i<(*length); i++) {
+ if (buffer[i] == '\r')
+ buffer[i] = '\n'; /* Show my Unix prejudice */
+ }
+ }
+ return buffer;
+}
+
+static unsigned int glyph_length (char **glyphs)
+{
+ int i;
+ unsigned result = 0;
+ for (i=0; i<256; i++) {
+ result += strlen (glyphs[i]);
+ }
+ return result;
+}
+
+static char *pfb_find_name (FILE *pfb_file)
+{
+ unsigned char *buffer;
+ unsigned long length = 0;
+ char *start, *end, *fontname;
+ int state = 0;
+#ifdef MEM_DEBUG
+ MEM_START
+#endif
+ rewind (pfb_file);
+ buffer = get_pfb_segment (&length, pfb_file, ASCII);
+ /* State definitions
+ state 0: Initial state
+ state 1: Saw /FontName */
+ start = (char *) buffer;
+ end = start+length;
+ skip_white (&start, end);
+ fontname = NULL;
+ while (start < end && fontname == NULL) {
+ char *ident;
+ pdf_obj *pdfobj;
+ switch (*start) {
+ /* Ignore arrays and procedures */
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ start += 1;
+ if (state == 1) {
+ ERROR ("Garbage following /FontName");
+ }
+ break;
+ case '(':
+ pdfobj = parse_pdf_string (&start, end);
+ if (pdfobj == NULL) {
+ ERROR ("parse_header: Error parsing a string in pfb header");
+ }
+ if (state == 1) { /* This string must be the font name */
+ char *tmp = pdf_string_value (pdfobj);
+ fontname = NEW (strlen(tmp)+1, char);
+ memcpy (fontname, tmp, strlen(tmp)+1);
+ }
+ pdf_release_obj (pdfobj);
+ break;
+ case '/':
+ start += 1;
+ ident = parse_ident (&start, end);
+ if (state == 0 && !strcmp (ident, "FontName")) {
+ state = 1;
+ } else if (state == 1) {
+ fontname = NEW (strlen(ident)+1, char);
+ memcpy (fontname, ident, strlen(ident)+1);
+ }
+ RELEASE (ident);
+ break;
+ default:
+ ident = parse_ident (&start, end);
+ RELEASE (ident);
+ break;
+ }
+ skip_white (&start, end);
+ }
+ RELEASE (buffer);
+#ifdef MEM_DEBUG
+ MEM_END
+#endif /* MEM_DEBUG */
+ return fontname;
+}
+
+static void pfb_add_to_used_glyphs (int pfb_id, char *glyph)
+{
+ if (pfb_id >= 0 && pfb_id < num_pfbs && glyph) {
+ if (pfbs[pfb_id].n_used_glyphs == 0 ||
+ !bsearch (glyph, pfbs[pfb_id].used_glyphs,
+ pfbs[pfb_id].n_used_glyphs,
+ sizeof (char *), glyph_match)) {
+ if (pfbs[pfb_id].n_used_glyphs+1 >=
+ pfbs[pfb_id].max_used_glyphs) {
+ pfbs[pfb_id].max_used_glyphs += 16;
+ pfbs[pfb_id].used_glyphs = RENEW (pfbs[pfb_id].used_glyphs,
+ pfbs[pfb_id].max_used_glyphs,
+ char *);
+ }
+ (pfbs[pfb_id].used_glyphs)[pfbs[pfb_id].n_used_glyphs] =
+ NEW (strlen(glyph)+1, char);
+ strcpy((pfbs[pfb_id].used_glyphs)[pfbs[pfb_id].n_used_glyphs],
+ glyph);
+ pfbs[pfb_id].n_used_glyphs += 1;
+ qsort (pfbs[pfb_id].used_glyphs, pfbs[pfb_id].n_used_glyphs,
+ sizeof (char *), glyph_cmp);
+ }
+ }
+}
+
+static char *new_used_chars (void)
+{
+ char *result;
+ int i;
+ result = NEW (256, char);
+ for (i=0; i<256; i++) {
+ result[i] = 0;
+ }
+ return result;
+}
+
+/* Mark the character at position "code" as used in the pfb font
+ corresponding to "pfb_id" */
+static void pfb_add_to_used_chars (int pfb_id, unsigned code)
+{
+ if (pfb_id >= 0 && pfb_id < num_pfbs && code < 256) {
+ if (!pfbs[pfb_id].used_def_enc_chars) {
+ pfbs[pfb_id].used_def_enc_chars = new_used_chars();
+ }
+ (pfbs[pfb_id].used_def_enc_chars)[code] = 1;
+ }
+ if (code >= 256)
+ ERROR ("pfb_add_to_used_chars(): code >= 256");
+ return;
+}
+
+static unsigned long do_pfb_header (FILE *file, int pfb_id)
+{
+ unsigned char *buffer, *filtered = NULL;
+ unsigned long length = 0;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ buffer = get_pfb_segment (&length, file, ASCII);
+ if (partial_enabled) {
+ filtered = NEW (length+strlen(pfbs[pfb_id].fontname)+1+1024, unsigned
+ char);
+ }
+ /* We must parse the header even if not doing font subsetting so
+ that we can determine the parameters for the font descriptor.
+ parse_head() won't write to a null pointer */
+ length = parse_header (filtered, buffer, length, pfb_id);
+ if (filtered) {
+ pdf_add_stream (pfbs[pfb_id].direct, (char *) filtered, length);
+ RELEASE (filtered);
+ } else {
+ pdf_add_stream (pfbs[pfb_id].direct, (char *) buffer, length);
+ }
+ RELEASE (buffer);
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+ return length;
+}
+
+
+static unsigned long parse_body (unsigned char *filtered, unsigned char
+ *unfiltered, unsigned long length,
+ char **used_glyphs, unsigned *n_used,
+ pdf_obj *descriptor)
+{
+ char *start, *end, *tail, *ident;
+ unsigned char *filtered_pointer;
+ double last_number = 0.0;
+ int state = 0;
+ if (verbose > 2) {
+ fprintf (stderr, "\nSearching for following glyphs in font:\n");
+ dump_glyphs (used_glyphs, *n_used, 0);
+ }
+ start = (char *) unfiltered, end = (char *) unfiltered+length;
+ /* Skip first four bytes */
+ tail = start; filtered_pointer = filtered;
+ start += 4;
+ /* Skip everything up to the charstrings section */
+ while (start < end) {
+ pdf_obj *pdfobj;
+ skip_white (&start, end);
+ /* Very simple state machine
+ state = 0: nothing special
+ state = 1: Saw StdVW, but waiting for number after it */
+ switch (*start) {
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ start += 1;
+ continue;
+ case '(':
+ pdfobj = parse_pdf_string (&start, end);
+ if (pdfobj == NULL) {
+ ERROR ("Error processing a string in a PFB file.");
+ }
+ pdf_release_obj (pdfobj);
+ continue;
+ case '/':
+ start += 1;
+ if ((ident = parse_ident (&start, end)) &&
+ !strcmp ((char *) ident, "CharStrings")) {
+ RELEASE (ident);
+ break;
+ } else if (ident && !strcmp ((char *) ident, "StdVW")) {
+ state = 1; /* Saw StdVW */
+ }
+ if (ident) {
+ RELEASE (ident);
+ } else {
+ fprintf (stderr, "\nError processing identifier in PFB file.\n");
+ dump (start, end);
+ }
+ continue;
+ default:
+ ident = parse_ident (&start, end);
+ if (ident == NULL)
+ ERROR ("Error processing a symbol in the PFB file.");
+ if (is_an_int(ident))
+ if (state == 1) {
+ pdf_add_dict (descriptor, pdf_new_name ("StemV"),
+ pdf_new_number (atof (ident)));
+ state = 0; /* Return to normal processing */
+ } else
+ last_number = atof (ident); /* Might be start of RD */
+ else {
+ if (!strcmp (ident, "RD") ||
+ !strcmp (ident, "-|")) {
+ start += ((unsigned long) last_number) + 1;
+ }
+ }
+ RELEASE (ident);
+ continue;
+ }
+ break;
+ }
+ if (start >= end)
+ ERROR ("Unexpected end of binary portion of PFB file");
+ /* Copy what we have so far over to the new buffer */
+ if (filtered) {
+ memcpy (filtered_pointer, tail, start-tail);
+ /* Advance pointer into new buffer */
+ filtered_pointer += (start-tail);
+ }
+ /* At this point, start is positioned just before the beginning of the glyphs, just after
+ the word /CharStrings. The earlier portion of the input buffer has
+ been copied to the output. The remainder of the routine need not
+ be executed if not doing font subsetting */
+ if (filtered) {
+ char **this_glyph;
+ sprintf ((char *) filtered_pointer, " %d", *n_used);
+ filtered_pointer += strlen ((char *) filtered_pointer);
+ skip_white(&start, end);
+ /* The following ident *should* be the number of glyphs in this
+ file */
+ ident = parse_ident (&start, end);
+ if (verbose>1) {
+ fprintf (stderr, "\n Embedding %d of %s glyphs\n", *n_used, ident);
+ }
+ if (ident == NULL || !is_an_int (ident) || *n_used > atof (ident))
+ ERROR ("More glyphs needed than present in file");
+ RELEASE (ident);
+ tail = start;
+ while (start < end && *start != '/') start++;
+ if (filtered) {
+ memcpy (filtered_pointer, tail, start-tail);
+ filtered_pointer += (start-tail);
+ }
+ /* Now we are exactly at the beginning of the glyphs */
+ while (start < end && *start == '/') {
+ char *glyph;
+ tail = start;
+ start += 1;
+ glyph = parse_ident (&start, end);
+ /* Get the number that should follow the glyph name */
+ skip_white(&start, end);
+ ident = parse_ident (&start, end);
+ if (!is_an_int (ident))
+ ERROR ("Expecting an integer after glyph name");
+ last_number = atof (ident);
+ RELEASE (ident);
+ /* The next identifier should be a "RD" or a "-|". We don't
+ really care what it is */
+ skip_white(&start, end);
+ ident = parse_ident (&start, end);
+ RELEASE (ident);
+ /* Skip a blank */
+ start += 1;
+ /* Skip the binary stream */
+ start += (unsigned long) last_number;
+ /* Skip the "ND" or "|-" terminator */
+ skip_white(&start, end);
+ ident = parse_ident (&start, end);
+ RELEASE (ident);
+ skip_white (&start, end);
+ if (*n_used != 0 && (this_glyph = bsearch (glyph, used_glyphs, *n_used, sizeof (char *),
+ glyph_match))) {
+ memcpy (filtered_pointer, tail, start-tail);
+ filtered_pointer += start-tail;
+ (*n_used)--;
+ /* Remove glyph and rearrange used_glyphs array */
+ {
+ RELEASE (*this_glyph);
+ (*this_glyph) = used_glyphs[*n_used];
+ qsort (used_glyphs, *n_used, sizeof (char *), glyph_cmp);
+ }
+ }
+ RELEASE (glyph);
+ }
+ if (*n_used != 0) {
+ fprintf (stderr, "Didn't find following required glyphs in the font:\n");
+ dump_glyphs (used_glyphs, *n_used, 0);
+ ERROR ("Possibly the encoding is incorrect.");
+ }
+ /* Include the rest of the file verbatim */
+ if (start < end){
+ memcpy (filtered_pointer, start, end-start);
+ filtered_pointer += end-start;
+ }
+ if (verbose>1) {
+ fprintf (stderr, " (subsetting eliminated %ld bytes)", length-(filtered_pointer-filtered));
+ }
+ }
+ return (filtered? filtered_pointer-filtered: length);
+}
+
+static unsigned long do_pfb_body (FILE *file, int pfb_id)
+{
+ int i;
+ unsigned char *buffer=NULL, *filtered=NULL;
+ unsigned long length=0;
+#ifdef MEM_DEBUG
+ MEM_START
+#endif
+ buffer = get_pfb_segment (&length, file, BINARY);
+ /* We need to decrypt the binary
+ portion of the pfb */
+ t1_crypt_init(EEKEY);
+ for (i=0; i<length; i++) {
+ buffer[i] = t1_decrypt(buffer[i]);
+ }
+ if (partial_enabled) {
+ filtered = NEW (length, unsigned char);
+ }
+ length = parse_body (filtered, buffer, length,
+ pfbs[pfb_id].used_glyphs,
+ &pfbs[pfb_id].n_used_glyphs,
+ pfbs[pfb_id].descriptor);
+ /* And reencrypt the whole thing */
+ t1_crypt_init (EEKEY);
+ for (i=0; i<length; i++) {
+ buffer[i] = t1_encrypt(partial_enabled? filtered[i]: buffer[i]);
+ }
+ if (filtered)
+ RELEASE (filtered);
+ pdf_add_stream (pfbs[pfb_id].direct, (char *) buffer, length);
+ RELEASE (buffer);
+#ifdef MEM_DEBUG
+ MEM_START
+#endif
+ return length;
+}
+
+static unsigned long do_pfb_trailer (FILE *file, pdf_obj *stream)
+{
+ unsigned char *buffer;
+ unsigned long length;
+ buffer = get_pfb_segment (&length, file, ASCII);
+ pdf_add_stream (stream, (char *) buffer, length);
+ RELEASE (buffer);
+ return length;
+}
+
+
+static pdf_obj *type1_fontfile (int pfb_id)
+{
+ if (pfb_id >= 0 && pfb_id < num_pfbs)
+ return pdf_link_obj(pfbs[pfb_id].indirect);
+ else
+ return NULL;
+}
+
+static char *type1_fontname (int pfb_id)
+{
+ if (pfb_id >= 0 && pfb_id < num_pfbs)
+ return pfbs[pfb_id].fontname;
+ else
+ return NULL;
+}
+
+/* Mangle_fontname mangles the name in place. fontname
+ must be big enough to add seven characters */
+
+static void mangle_fontname(char *fontname)
+{
+ int i;
+ char ch;
+ static char first = 1;
+ memmove (fontname+7, fontname, strlen(fontname)+1);
+ /* The following procedure isn't very random, but it
+ doesn't need to be for this application. */
+ if (first) {
+ srand (time(NULL));
+ first = 0;
+ }
+ for (i=0; i<6; i++) {
+ ch = rand() % 26;
+ fontname[i] = ch+'A';
+ }
+ fontname[6] = '+';
+}
+
+
+/* This routine builds a default font descriptor with dummy values
+ filled in for the required keys. As the pfb file is parsed,
+ any values that are found are rewritten. By doing this,
+ all the required fields are found in the font descriptor
+ even if the pfb is somewhat defective. This approach is
+ conservative, with the cost of keeping the names around in memory
+ for a while.
+*/
+
+static void type1_start_font_descriptor (int pfb_id)
+{
+ pdf_obj *tmp1;
+ pfbs[pfb_id].descriptor = pdf_new_dict ();
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("Type"),
+ pdf_new_name ("FontDescriptor"));
+ /* For now, insert dummy values */
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("CapHeight"),
+ pdf_new_number (850.0)); /* This number is arbitrary */
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("Ascent"),
+ pdf_new_number (850.0)); /* This number is arbitrary */
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("Descent"),
+ pdf_new_number (-200.0));
+ tmp1 = pdf_new_array ();
+ pdf_add_dict (pfbs[pfb_id].descriptor, pdf_new_name ("FontBBox"), tmp1);
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("FontName"),
+ pdf_new_name (type1_fontname(pfb_id)));
+
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("ItalicAngle"),
+ pdf_new_number(0.0));
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("StemV"), /* StemV is required, StemH
+ is not */
+ pdf_new_number (STEMV)); /* Use a default value */
+ /* You don't need a fontfile for the standard fonts */
+ if (pfb_id >= 0)
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("FontFile"),
+ type1_fontfile (pfb_id));
+
+ pdf_add_dict (pfbs[pfb_id].descriptor,
+ pdf_new_name ("Flags"),
+ pdf_new_number (SYMBOLIC)); /* Treat all fonts as symbolic */
+ return;
+}
+
+static int pfb_get_id (const char *pfb_name)
+{
+ int i;
+ for (i=0; i<num_pfbs; i++) {
+ if (pfbs[i].pfb_name && !strcmp (pfbs[i].pfb_name, pfb_name))
+ break;
+ }
+ if (i == num_pfbs) { /* This font not previously called for */
+ FILE *pfb_file = NULL;
+ char *full_pfb_name, *short_fontname;
+ if (!(full_pfb_name = kpse_find_file (pfb_name, kpse_type1_format,
+ 1)) ||
+ !(pfb_file = MFOPEN (full_pfb_name, FOPEN_RBIN_MODE)) ||
+ !(is_a_pfb (pfb_file))) {
+ if (pfb_file)
+ MFCLOSE (pfb_file);
+ i = -1;
+ } else {
+ short_fontname = pfb_find_name (pfb_file);
+ MFCLOSE (pfb_file);
+ if (num_pfbs >= max_pfbs) {
+ max_pfbs += MAX_FONTS;
+ pfbs = RENEW (pfbs, max_pfbs, struct a_pfb);
+ }
+ num_pfbs += 1;
+ init_a_pfb (pfbs+i);
+ pfbs[i].pfb_name = NEW (strlen(pfb_name)+1, char);
+ strcpy (pfbs[i].pfb_name, pfb_name);
+ pfbs[i].direct = pdf_new_stream(STREAM_COMPRESS);
+ pfbs[i].indirect = pdf_ref_obj (pfbs[i].direct);
+ if (partial_enabled) {
+ pfbs[i].fontname = NEW (strlen(short_fontname)+8, char);
+ strcpy (pfbs[i].fontname, short_fontname);
+ mangle_fontname(pfbs[i].fontname);
+ }
+ else {
+ pfbs[i].fontname = NEW (strlen(short_fontname)+1, char);
+ strcpy (pfbs[i].fontname, short_fontname);
+ }
+ type1_start_font_descriptor(i);
+ if (short_fontname)
+ RELEASE (short_fontname);
+ }
+ }
+ return i;
+}
+
+static void pfb_release (int id)
+{
+ if (id >= 0 && id < num_pfbs) {
+ pdf_release_obj (pfbs[id].direct);
+ RELEASE (pfbs[id].pfb_name);
+ pdf_release_obj (pfbs[id].indirect);
+ RELEASE (pfbs[id].fontname);
+ if (pfbs[id].used_def_enc_chars)
+ RELEASE (pfbs[id].used_def_enc_chars);
+ if (pfbs[id].int_encoding){
+ int i;
+ for (i=0; i<256; i++) {
+ if ((pfbs[id].int_encoding)[i])
+ RELEASE ((pfbs[id].int_encoding)[i]);
+ }
+ RELEASE (pfbs[id].int_encoding);
+ }
+ if (pfbs[id].used_glyphs) {
+ unsigned i;
+ for (i=0; i<pfbs[id].n_used_glyphs; i++) {
+ RELEASE ((pfbs[id].used_glyphs)[i]);
+ }
+ RELEASE (pfbs[id].used_glyphs);
+ }
+ }
+}
+
+static void release_glyphs (char **glyphs)
+{
+ int i;
+ for (i=0; i<256; i++) {
+ RELEASE (glyphs[i]);
+ }
+}
+
+static void do_pfb (int pfb_id)
+{
+ char *full_pfb_name;
+ FILE *type1_binary_file;
+ pdf_obj *stream_dict;
+ unsigned long length1, length2, length3;
+ int ch;
+ full_pfb_name = kpse_find_file (pfbs[pfb_id].pfb_name, kpse_type1_format,
+ 1);
+ if (verbose == 1)
+ fprintf (stderr, "(PFB:%s", pfbs[pfb_id].pfb_name);
+ if (verbose > 1)
+ fprintf (stderr, "(PFB:%s", full_pfb_name);
+ if (full_pfb_name == NULL ||
+ (type1_binary_file = MFOPEN (full_pfb_name, FOPEN_RBIN_MODE)) == NULL) {
+ fprintf (stderr, "type1_fontfile: Unable to find or open binary font file (%s)",
+ pfbs[pfb_id].pfb_name);
+ ERROR ("This existed when I checked it earlier!");
+ return;
+ }
+ /* Following section doesn't hide PDF stream structure very well */
+ length1 = do_pfb_header (type1_binary_file, pfb_id);
+ /* The following section seems determines which, if any,
+ glyphs were used via the internal encoding, which hasn't
+ been known until now.*/
+ if (partial_enabled) {
+ int j;
+ if (verbose > 2) {
+ fprintf (stderr, "Default encoding:\n");
+ dump_glyphs (pfbs[pfb_id].int_encoding, 256, 1);
+ }
+ if (pfbs[pfb_id].used_def_enc_chars) {
+ if (verbose > 2)
+ fprintf (stderr, "\nRetaining portion of default encoding:\n");
+ for (j=0; j<256; j++) {
+ if ((pfbs[pfb_id].used_def_enc_chars)[j]) {
+ if (verbose > 2)
+ fprintf (stderr, "(%d/%s)", j, (pfbs[pfb_id].int_encoding)[j]);
+ pfb_add_to_used_glyphs (pfb_id, (pfbs[pfb_id].int_encoding)[j]);
+ }
+ }
+ }
+ }
+ length2 = do_pfb_body (type1_binary_file, pfb_id);
+ length3 = do_pfb_trailer (type1_binary_file, pfbs[pfb_id].direct);
+ if ((ch = fgetc (type1_binary_file)) != 128 ||
+ (ch = fgetc (type1_binary_file)) != 3) {
+ fprintf (stderr, "\n\nWarning: PFB file may be improperly terminated\n");
+ }
+ /* Got entire file! */
+ if (verbose > 1)
+ fprintf (stderr, "\n Embedded size: %ld bytes\n", length1+length2+length3);
+ if (verbose) {
+ fprintf (stderr, ")");
+ }
+ MFCLOSE (type1_binary_file);
+ stream_dict = pdf_stream_dict (pfbs[pfb_id].direct);
+ pdf_add_dict (stream_dict, pdf_new_name("Length1"),
+ pdf_new_number (length1));
+ pdf_add_dict (stream_dict, pdf_new_name("Length2"),
+ pdf_new_number (length2));
+ pdf_add_dict (stream_dict, pdf_new_name("Length3"),
+ pdf_new_number (length3));
+ /* Finally, flush the descriptor */
+ pdf_release_obj (pfbs[pfb_id].descriptor);
+ return;
+}
+
+void pfb_flush_all (void)
+{
+ int i;
+ for (i=0; i<num_pfbs; i++) {
+ do_pfb(i);
+ pfb_release (i);
+ }
+ if (pfbs)
+ RELEASE (pfbs);
+}
+
+struct a_type1_font
+{
+ pdf_obj *indirect, *encoding;
+ long pfb_id;
+ int encoding_id;
+ char *used_chars;
+} *type1_fonts = NULL;
+int num_type1_fonts = 0, max_type1_fonts = 0;
+
+
+static void init_a_type1_font (struct a_type1_font *this_type1_font)
+{
+ if (partial_enabled) {
+ this_type1_font -> used_chars = new_used_chars ();
+ } else {
+ this_type1_font -> used_chars = NULL;
+ }
+}
+
+pdf_obj *type1_font_resource (int type1_id)
+{
+ if (type1_id>=0 && type1_id<max_type1_fonts)
+ return pdf_link_obj(type1_fonts[type1_id].indirect);
+ else {
+ ERROR ("Invalid font id in type1_font_resource");
+ return NULL;
+ }
+}
+
+char *type1_font_used (int type1_id)
+{
+ char *result;
+ if (type1_id>=0 && type1_id<max_type1_fonts) {
+ result = type1_fonts[type1_id].used_chars;
+ } else {
+ fprintf (stderr, "type1_font_used: type1_id=%d\n", type1_id);
+ ERROR ("Invalid font id in type1_font_used");
+ }
+ return result;
+}
+
+
+static int is_a_base_font (const char *name)
+{
+ static char *basefonts[] = {
+ "Courier", "Courier-Bold", "Courier-Oblique",
+ "Courier-BoldOblique", "Helvetica", "Helvetica-Bold",
+ "Helvetica-Oblique", "Helvetica-BoldOblique", "Symbol",
+ "Times-Roman", "Times-Bold", "Times-Italic",
+ "Times-BoldItalic", "ZapfDingbats"
+ };
+ int i;
+ for (i=0; i<14; i++) {
+ if (!strcmp (name, basefonts[i]))
+ return 1;
+ }
+ return 0;
+}
+
+int type1_font (const char *map_name, int tfm_font_id, char
+ *resource_name, int encoding_id, int remap)
+{
+ int i, result = -1;
+ int tfm_firstchar, tfm_lastchar;
+ int pdf_firstchar, pdf_lastchar;
+ int pfb_id = -1;
+ pdf_obj *font_resource, *tmp1, *font_encoding_ref;
+
+ if (num_type1_fonts >= max_type1_fonts) {
+ max_type1_fonts = MAX (max_type1_fonts+MAX_FONTS, num_type1_fonts+1);
+ type1_fonts = RENEW (type1_fonts, max_type1_fonts, struct
+ a_type1_font);
+ }
+
+ if ((map_name && is_a_base_font(map_name)) ||
+ (pfb_id = pfb_get_id(map_name)) >= 0) {
+ /* Looks like we have a physical font (either a reader font or a
+ Type 1 font binary file). */
+ init_a_type1_font (type1_fonts+num_type1_fonts);
+ type1_fonts[num_type1_fonts].pfb_id = pfb_id;
+ type1_fonts[num_type1_fonts].encoding_id = encoding_id;
+ /* Allocate a dictionary for the physical font */
+ font_resource = pdf_new_dict ();
+ if (type1_fonts[num_type1_fonts].encoding_id >= 0) {
+ font_encoding_ref = encoding_ref (encoding_id);
+ pdf_add_dict (font_resource,
+ pdf_new_name ("Encoding"),
+ font_encoding_ref);
+ }
+ pdf_add_dict (font_resource,
+ pdf_new_name ("Type"),
+ pdf_new_name ("Font"));
+ pdf_add_dict (font_resource,
+ pdf_new_name ("Subtype"),
+ pdf_new_name ("Type1"));
+ pdf_add_dict (font_resource,
+ pdf_new_name ("Name"),
+ pdf_new_name (resource_name));
+ if (type1_fonts[num_type1_fonts].pfb_id >= 0) {
+ pdf_add_dict (font_resource,
+ pdf_new_name ("FontDescriptor"),
+ pdf_ref_obj(pfbs[type1_fonts[num_type1_fonts].pfb_id].descriptor));
+ }
+ /* If we are embedding this font, it may have been used by another virtual
+ font and we need to use the same mangled name. Mangled
+ names are known only to the pfb module, so we call it to get
+ the name */
+ if (type1_fonts[num_type1_fonts].pfb_id >= 0) {
+ pdf_add_dict (font_resource,
+ pdf_new_name ("BaseFont"),
+ pdf_new_name
+ (type1_fontname(type1_fonts[num_type1_fonts].pfb_id)));
+ /* Otherwise we use the base name */
+ } else {
+ pdf_add_dict (font_resource,
+ pdf_new_name ("BaseFont"),
+ pdf_new_name (map_name));
+ }
+ if (!(map_name && is_a_base_font (map_name))) {
+ tfm_firstchar = tfm_get_firstchar(tfm_font_id);
+ tfm_lastchar = tfm_get_lastchar(tfm_font_id);
+ if (remap) {
+ unsigned char t;
+ pdf_firstchar=255; pdf_lastchar=0;
+ for (i=tfm_firstchar; i<=tfm_lastchar; i++) {
+ if ((t=twiddle(i)) < pdf_firstchar)
+ pdf_firstchar = t;
+ if (t > pdf_lastchar)
+ pdf_lastchar = t;
+ }
+ } else {
+ pdf_firstchar = tfm_firstchar;
+ pdf_lastchar = tfm_lastchar;
+ }
+ pdf_add_dict (font_resource,
+ pdf_new_name ("FirstChar"),
+ pdf_new_number (pdf_firstchar));
+ pdf_add_dict (font_resource,
+ pdf_new_name ("LastChar"),
+ pdf_new_number (pdf_lastchar));
+ tmp1 = pdf_new_array ();
+ for (i=pdf_firstchar; i<=pdf_lastchar; i++) {
+ if (remap) {
+ int t;
+ if ((t=untwiddle(i)) <= tfm_lastchar && t>=tfm_firstchar)
+ pdf_add_array (tmp1,
+ pdf_new_number(ROUND(tfm_get_width
+ (tfm_font_id,t)*1000.0,0.1)));
+ else
+ pdf_add_array (tmp1,
+ pdf_new_number(0.0));
+ } else
+ pdf_add_array (tmp1,
+ pdf_new_number(ROUND(tfm_get_width
+ (tfm_font_id, i)*1000.0,0.1)));
+ }
+ pdf_add_dict (font_resource,
+ pdf_new_name ("Widths"),
+ tmp1);
+ }
+ type1_fonts[num_type1_fonts].indirect = pdf_ref_obj(font_resource);
+ pdf_release_obj (font_resource);
+ result = num_type1_fonts;
+ num_type1_fonts += 1;
+ } else { /* Don't have a physical font */
+ result = -1;
+ }
+ return result;
+}
+
+
+void type1_close_all (void)
+{
+ int i, j;
+ /* Three arrays are created by this module and need to be released */
+ /* First, each TeX font name that ends up as a postscript font gets
+ added to type1_fonts (yes, even Times-Roman, etc.) */
+ /* The first thing to do is to resolve all character references to
+ actual glyph references. If an external encoding is specified,
+ we simply look up the glyph name in the encoding. If the internal
+ encoding is being used, we add it to the used_chars array of
+ the internal encoding */
+ for (i=0; i<num_type1_fonts; i++) {
+ /* If font subsetting is enabled, each used character needs
+ to be added to the used_glyphs array in the corresponding pfb
+ */
+ if (partial_enabled) {
+ /* We always consider .notdef to be used */
+ pfb_add_to_used_glyphs (type1_fonts[i].pfb_id, ".notdef");
+ for (j=0; j<256; j++) {
+ char *glyph;
+ if (type1_fonts[i].pfb_id >= 0 &&
+ type1_fonts[i].encoding_id >= 0 &&
+ (type1_fonts[i].used_chars)[j]) {
+ glyph = encoding_glyph (type1_fonts[i].encoding_id,
+ j);
+ pfb_add_to_used_glyphs (type1_fonts[i].pfb_id, glyph);
+ }
+ if (type1_fonts[i].pfb_id >= 0 &&
+ type1_fonts[i].encoding_id < 0 &&
+ (type1_fonts[i].used_chars)[j])
+ pfb_add_to_used_chars (type1_fonts[i].pfb_id, j);
+ }
+ }
+ if (type1_fonts[i].used_chars)
+ RELEASE (type1_fonts[i].used_chars);
+ pdf_release_obj (type1_fonts[i].indirect);
+ }
+ RELEASE (type1_fonts);
+ /* Second every distinct pfb name ends up in pfbs. It is possible
+ that two distinct tex names map to the same pfb name. That's why
+ there is a separate array for pfbs */
+
+ /* Read any necessary font files and flush them */
+ pfb_flush_all();
+
+}
+