/* Copyright (C) 1999, 2000 artofcode LLC. All rights reserved. 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. */ /*$Id: gdevpdfw.c,v 1.12.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* Font writing for pdfwrite driver. */ #include "memory_.h" #include "string_.h" #include "gx.h" #include "gsalloc.h" /* for patching font memory */ #include "gsbittab.h" #include "gserrors.h" #include "gsmatrix.h" #include "gsutil.h" /* for bytes_compare */ #include "gxfcid.h" #include "gxfcmap.h" #include "gxfont.h" #include "gxfont0.h" #include "gdevpdfx.h" #include "gdevpdff.h" #include "gdevpsf.h" #include "scommon.h" /* Get the ID of various kinds of resources, with type checking. */ inline private long pdf_font_id(const pdf_font_t *pdfont) { return pdf_resource_id((const pdf_resource_t *)pdfont); } inline private long pdf_font_descriptor_id(const pdf_font_descriptor_t *pfd) { return pdf_resource_id((const pdf_resource_t *)pfd); } inline private long pdf_char_proc_id(const pdf_char_proc_t *pcp) { return pdf_resource_id((const pdf_resource_t *)pcp); } /* Write the Widths for a font. */ private int pdf_write_Widths(gx_device_pdf *pdev, int first, int last, const int widths[256]) { stream *s = pdev->strm; int i; pprintd2(s, "/FirstChar %d/LastChar %d/Widths[", first, last); for (i = first; i <= last; ++i) pprintd1(s, (i & 15 ? " %d" : "\n%d"), widths[i]); stream_puts(s, "]\n"); return 0; } /* Write a FontBBox dictionary element. */ private int pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox) { stream *s = pdev->strm; /* AR 4 doesn't like fonts with empty FontBBox which * happens when the font contains only space characters. * Small bbox causes AR 4 to display a hairline. So we use * the full BBox. */ int x= pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0); int y= pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0); pprintd4(s, "/FontBBox[%d %d %d %d]", pbox->p.x, pbox->p.y, x, y); return 0; } /* Write a synthesized bitmap font resource. */ private int pdf_write_synthesized_type3(gx_device_pdf *pdev, const pdf_font_t *pef) { stream *s; gs_int_rect bbox; int widths[256]; memset(&bbox, 0, sizeof(bbox)); memset(widths, 0, sizeof(widths)); pdf_open_separate(pdev, pdf_font_id(pef)); s = pdev->strm; pprints1(s, "<rname); pprintld1(s, "/Encoding %ld 0 R/CharProcs", pdev->embedded_encoding_id); /* Write the CharProcs. */ { const pdf_char_proc_t *pcp; int w; stream_puts(s, "<<"); /* Write real characters. */ for (pcp = pef->char_procs; pcp; pcp = pcp->char_next) { bbox.p.y = min(bbox.p.y, pcp->y_offset); bbox.q.x = max(bbox.q.x, pcp->width); bbox.q.y = max(bbox.q.y, pcp->height + pcp->y_offset); widths[pcp->char_code] = pcp->x_width; pprintld2(s, "/a%ld\n%ld 0 R", (long)pcp->char_code, pdf_char_proc_id(pcp)); } /* Write space characters. */ for (w = 0; w < countof(pef->spaces); ++w) { byte ch = pef->spaces[w]; if (ch) { pprintld2(s, "/a%ld\n%ld 0 R", (long)ch, pdev->space_char_ids[w]); widths[ch] = w + X_SPACE_MIN; } } stream_puts(s, ">>"); } pdf_write_font_bbox(pdev, &bbox); stream_puts(s, "/FontMatrix[1 0 0 1 0 0]"); pdf_write_Widths(pdev, 0, pef->num_chars - 1, widths); stream_puts(s, ">>\n"); pdf_end_separate(pdev); return 0; } /* Write a font descriptor. */ int pdf_write_FontDescriptor(gx_device_pdf *pdev, const pdf_font_descriptor_t *pfd) { gs_font *font = pfd->base_font; bool is_subset = pdf_has_subset_prefix(pfd->FontName.chars, pfd->FontName.size); long cidset_id = 0; /* pro forma initialization */ stream *s; int code = 0; /* If this is a CIDFont subset, write the CIDSet now. */ if (font && is_subset) { switch (pfd->values.FontType) { case ft_CID_encrypted: case ft_CID_TrueType: { pdf_data_writer_t writer; cidset_id = pdf_begin_separate(pdev); s = pdev->strm; stream_puts(s, "<<"); code = pdf_begin_data(pdev, &writer); if (code >= 0) { stream_write(writer.binary.strm, pfd->chars_used.data, pfd->chars_used.size); code = pdf_end_data(&writer); } else /* code < 0 */ pdf_end_separate(pdev); } default: break; } } pdf_open_separate(pdev, pdf_font_descriptor_id(pfd)); s = pdev->strm; stream_puts(s, "<FontName.chars, pfd->FontName.size); if (font) { /* not a built-in font */ param_printer_params_t params; printer_param_list_t rlist; gs_param_list *const plist = (gs_param_list *)&rlist; pdf_write_font_bbox(pdev, &pfd->values.FontBBox); params = param_printer_params_default; code = s_init_param_printer(&rlist, ¶ms, s); if (code >= 0) { #define DESC_INT(str, memb)\ {str, gs_param_type_int, offset_of(pdf_font_descriptor_t, values.memb)} static const gs_param_item_t required_items[] = { DESC_INT("Ascent", Ascent), DESC_INT("CapHeight", CapHeight), DESC_INT("Descent", Descent), DESC_INT("ItalicAngle", ItalicAngle), DESC_INT("StemV", StemV), gs_param_item_end }; static const gs_param_item_t optional_items[] = { DESC_INT("AvgWidth", AvgWidth), DESC_INT("Leading", Leading), DESC_INT("MaxWidth", MaxWidth), DESC_INT("MissingWidth", MissingWidth), DESC_INT("StemH", StemH), DESC_INT("XHeight", XHeight), gs_param_item_end }; #undef DESC_INT int Flags = pfd->values.Flags; pdf_font_descriptor_t defaults; /* * Hack: make all embedded subset TrueType fonts "symbolic" to * work around undocumented assumptions in Acrobat Reader. */ if (pfd->values.FontType == ft_TrueType && pdf_has_subset_prefix(pfd->FontName.chars, pfd->FontName.size)) Flags = (Flags & ~(FONT_IS_ADOBE_ROMAN)) | FONT_IS_SYMBOLIC; param_write_int(plist, "Flags", &Flags); gs_param_write_items(plist, pfd, NULL, required_items); memset(&defaults, 0, sizeof(defaults)); gs_param_write_items(plist, pfd, &defaults, optional_items); s_release_param_printer(&rlist); } if (is_subset) { switch (pfd->values.FontType) { case ft_CID_encrypted: case ft_CID_TrueType: pprintld1(s, "/CIDSet %ld 0 R\n", cidset_id); break; case ft_composite: return_error(gs_error_rangecheck); case ft_encrypted: { gs_glyph subset_glyphs[256]; uint subset_size = psf_subset_glyphs(subset_glyphs, font, pfd->chars_used.data); int i; stream_puts(s, "/CharSet("); for (i = 0; i < subset_size; ++i) { uint len; const char *str = font->procs.callbacks.glyph_name (subset_glyphs[i], &len); /* Don't include .notdef. */ if (bytes_compare((const byte *)str, len, (const byte *)".notdef", 7)) pdf_put_name(pdev, (const byte *)str, len); } stream_puts(s, ")\n"); } default: break; } } if (pfd->FontFile_id) { const char *FontFile_key; switch (pfd->values.FontType) { case ft_TrueType: case ft_CID_TrueType: FontFile_key = "/FontFile2"; break; default: code = gs_note_error(gs_error_rangecheck); /* falls through */ case ft_encrypted: if (pdev->CompatibilityLevel < 1.2) { FontFile_key = "/FontFile"; break; } /* For PDF 1.2 and later, write Type 1 fonts as Type1C. */ case ft_encrypted2: case ft_CID_encrypted: FontFile_key = "/FontFile3"; break; } stream_puts(s, FontFile_key); pprintld1(s, " %ld 0 R", pfd->FontFile_id); } } stream_puts(s, ">>\n"); pdf_end_separate(pdev); return code; } /* Write CIDSystemInfo for a CIDFont or CMap. */ private int pdf_write_cid_system_info(gx_device_pdf *pdev, const gs_cid_system_info_t *pcidsi) { stream *s = pdev->strm; stream_puts(s, "<<\n/Registry"); s_write_ps_string(s, pcidsi->Registry.data, pcidsi->Registry.size, PRINT_HEX_NOT_OK); stream_puts(s, "\n/Ordering"); s_write_ps_string(s, pcidsi->Ordering.data, pcidsi->Ordering.size, PRINT_HEX_NOT_OK); pprintd1(s, "\n/Supplement %d\n>>\n", pcidsi->Supplement); return 0; } int pdf_write_CIDFont_system_info(gx_device_pdf *pdev, const gs_font *pcidfont) { const gs_cid_system_info_t *pcidsi; switch (pcidfont->FontType) { case ft_CID_encrypted: pcidsi = &((const gs_font_cid0 *)pcidfont)-> cidata.common.CIDSystemInfo; break; case ft_CID_TrueType: pcidsi = &((const gs_font_cid2 *)pcidfont)-> cidata.common.CIDSystemInfo; break; default: return_error(gs_error_rangecheck); } return pdf_write_cid_system_info(pdev, pcidsi); } int pdf_write_CMap_system_info(gx_device_pdf *pdev, const gs_cmap_t *pcmap) { return pdf_write_cid_system_info(pdev, pcmap->CIDSystemInfo); } /* * Write the [D]W[2] entries for a CIDFont. *pfont is known to be a * CIDFont (type 0 or 2). */ private int pdf_write_CIDFont_widths(gx_device_pdf *pdev, const pdf_font_t *ppf) { /* * The values of the CIDFont width keys are as follows: * DW = w (default 0) * W = [{c [w ...] | cfirst clast w}*] * DW2 = [vy w1y] (default [880 -1000]) * W2 = [{c [w1y vx vy ...] | cfirst clast w1y vx vy}*] * Currently we only write DW and W. */ stream *s = pdev->strm; psf_glyph_enum_t genum; gs_glyph glyph; int dw = 0; psf_enumerate_bits_begin(&genum, NULL, ppf->widths_known, ppf->FontDescriptor->chars_count, GLYPH_SPACE_INDEX); /* Use the most common width as DW. */ { ushort counts[1500*2]; /* histogram of (-1500..1500) */ int dw_count = 0, i; memset(counts, 0, sizeof(counts)); while (!psf_enumerate_glyphs_next(&genum, &glyph)) { int cid = glyph - gs_min_cid_glyph; int width = ppf->Widths[cid]; counts[max(0,min(width+countof(counts)/2,countof(counts)-1))]++; } for (i = 0; i < countof(counts); i++) if (counts[i] > dw_count) dw = i - countof(counts)/2, dw_count = counts[i]; if (dw != 0) pprintd1(s, "/DW %d\n", dw); } /* * Now write all widths different from DW. Currently we make no * attempt to optimize this: we write every width individually. */ psf_enumerate_glyphs_reset(&genum); { int prev = -2; while (!psf_enumerate_glyphs_next(&genum, &glyph)) { int cid = glyph - gs_min_cid_glyph; int width = ppf->Widths[cid]; if (cid == prev + 1) pprintd1(s, "\n%d", width); else if (width == dw) continue; else { if (prev >= 0) stream_puts(s, "]\n"); else stream_puts(s, "/W["); pprintd2(s, "%d[%d", cid, width); } prev = cid; } if (prev >= 0) stream_puts(s, "]]"); } return 0; } /* * Write the CIDToGIDMap for a CIDFontType 2 font. We only need to worry * about mapping CIDs that have actually been used. */ private int pdf_write_CIDToGIDMap(gx_device_pdf *pdev, pdf_font_t *ppf, long *pcidmap_id) { stream *s = pdev->strm; psf_glyph_enum_t genum; gs_glyph glyph; psf_enumerate_bits_begin(&genum, NULL, ppf->widths_known, ppf->FontDescriptor->chars_count, GLYPH_SPACE_INDEX); /* Check for the identity map. */ while (!psf_enumerate_glyphs_next(&genum, &glyph)) { int cid = glyph - gs_min_cid_glyph; int gid = ppf->CIDToGIDMap[cid]; if (gid != cid) { /* * The map isn't Identity. Assign an object ID for writing the * map later. */ *pcidmap_id = pdf_obj_ref(pdev); pprintld1(s, "/CIDToGIDMap %ld 0 R\n", *pcidmap_id); return 1; } } /* All CIDs and GIDs match. */ stream_puts(s, "/CIDToGIDMap/Identity\n"); *pcidmap_id = 0; return 0; } private int pdf_write_CIDMap(stream *s, pdf_font_t *ppf) { psf_glyph_enum_t genum; int next = 0, count = ppf->FontDescriptor->chars_count; gs_glyph glyph; psf_enumerate_bits_begin(&genum, NULL, ppf->widths_known, count, GLYPH_SPACE_INDEX); while (!psf_enumerate_glyphs_next(&genum, &glyph)) { int cid = glyph - gs_min_cid_glyph; int gid = ppf->CIDToGIDMap[cid]; for (; next < cid; ++next) { stream_putc(s, 0); stream_putc(s, 0); } stream_putc(s, (byte)(gid >> 8)); stream_putc(s, (byte)gid); next = cid + 1; } for (; next < count; ++next) { stream_putc(s, 0); stream_putc(s, 0); } return 0; } /* * Write a font resource, including Widths and/or Encoding if relevant, * but not the FontDescriptor or embedded font data. Note that the * font itself may not be available. * * If the font is a subset, we must write the Widths even if it is a * built-in font. */ private int pdf_write_font_resource(gx_device_pdf *pdev, pdf_font_t *pef, const gs_const_string *pfname) { stream *s; const pdf_font_descriptor_t *pfd = pef->FontDescriptor; static const char *const encoding_names[] = { KNOWN_REAL_ENCODING_NAMES }; /* write_Widths = 0 if not writing, 1 if Widths, -1 if [D]W[2]. */ int write_Widths = (pef->write_Widths || pdf_has_subset_prefix(pfname->data, pfname->size) ? 1 : 0); long cidmap_id = 0; gs_const_string fname; byte fnchars[MAX_PDF_FONT_NAME]; fname = *pfname; pdf_open_separate(pdev, pdf_font_id(pef)); s = pdev->strm; switch (pef->FontType) { case ft_composite: { byte *chars = pef->fname.chars; uint size = pef->fname.size; stream_puts(s, "<sub_font_type == ft_CID_encrypted || pef->sub_font_type == ft_CID_TrueType) && pef->cmapname[0] == '/' ) { stream_putc(s, '-'); pdf_put_name_chars(pdev, (const byte*) (pef->cmapname + 1), strlen(pef->cmapname + 1)); } pprints1(s, (pef->cmapname[0] == '/') ? "/Encoding%s" : "/Encoding %s", pef->cmapname); pprintld1(s, "/DescendantFonts[%ld 0 R]", pdf_resource_id((const pdf_resource_t *)pef->DescendantFont)); write_Widths = 0; goto out; } case ft_encrypted: case ft_encrypted2: /* * Multiple Master instances must be identified as such iff they * are not embedded. */ if (pef->is_MM_instance && !pfd->FontFile_id) { /* Replace spaces in the name by underscores. */ uint i; stream_puts(s, "< sizeof(fnchars)) return_error(gs_error_rangecheck); for (i = 0; i < fname.size; ++i) fnchars[i] = (fname.data[i] == ' ' ? '_' : fname.data[i]); fname.data = fnchars; } else { stream_puts(s, "<CIDSystemInfo_id); write_Widths = -write_Widths; goto bfname; case ft_CID_TrueType: pprintld1(s, "<CIDSystemInfo_id); write_Widths = -write_Widths; goto ttname; case ft_TrueType: stream_puts(s, "<index < 0 || pfd->base_font || pfd->FontFile_id) pprintld1(s, "/FontDescriptor %ld 0 R", pdf_font_descriptor_id(pfd)); switch (write_Widths) { case 0: break; case 1: pdf_write_Widths(pdev, pef->FirstChar, pef->LastChar, pef->Widths); break; case -1: pdf_write_CIDFont_widths(pdev, pef); if (pef->FontType == ft_CID_TrueType) { if (pef->embed != FONT_EMBED_NO) pdf_write_CIDToGIDMap(pdev, pef, &cidmap_id); } break; } if (pef->Differences) { long diff_id = pdf_obj_ref(pdev); int prev = 256; int i; pprintld1(s, "/Encoding %ld 0 R>>\n", diff_id); pdf_end_separate(pdev); pdf_open_separate(pdev, diff_id); s = pdev->strm; stream_puts(s, "<BaseEncoding != ENCODING_INDEX_UNKNOWN) pprints1(s, "/BaseEncoding/%s", encoding_names[pef->BaseEncoding]); stream_puts(s, "/Differences["); for (i = 0; i < 256; ++i) if (pef->Differences[i].str.data != 0) { if (i != prev + 1) pprintd1(s, "\n%d", i); pdf_put_name(pdev, pef->Differences[i].str.data, pef->Differences[i].str.size); prev = i; } stream_puts(s, "]"); } else if (pef->BaseEncoding != ENCODING_INDEX_UNKNOWN) { pprints1(s, "/Encoding/%s", encoding_names[pef->BaseEncoding]); } if (cidmap_id) { pdf_data_writer_t writer; stream_puts(pdev->strm, ">>\n"); pdf_end_separate(pdev); pdf_open_separate(pdev, cidmap_id); stream_puts(pdev->strm, "<<"); pdf_begin_data(pdev, &writer); pdf_write_CIDMap(writer.binary.strm, pef); return pdf_end_data(&writer); } out: stream_puts(s, ">>\n"); return pdf_end_separate(pdev); } /* Register a font for eventual writing (embedded or not). */ private GS_NOTIFY_PROC(pdf_font_notify_proc); typedef struct pdf_font_notify_s { gs_memory_t *memory; /* used to allocate this object */ gx_device_pdf *pdev; pdf_font_t *pdfont; /* 0 if only for base_font */ pdf_font_descriptor_t *pfd; /* 0 if not for base_font */ } pdf_font_notify_t; gs_private_st_ptrs3(st_pdf_font_notify, pdf_font_notify_t, "pdf_font_notify_t", pdf_font_notify_enum_ptrs, pdf_font_notify_reloc_ptrs, pdev, pdfont, pfd); int pdf_register_font(gx_device_pdf *pdev, gs_font *font, pdf_font_t *ppf) { /* * Note that we do this allocation in stable font->memory, not * pdev->pdf_memory. */ pdf_font_descriptor_t *pfd = ppf->FontDescriptor; gs_font *base_font = (pfd ? pfd->base_font : NULL); gs_memory_t *fn_memory = gs_memory_stable(font->memory); pdf_font_notify_t *pfn = gs_alloc_struct(fn_memory, pdf_font_notify_t, &st_pdf_font_notify, "pdf_register_font"); int code; if (pfn == 0) return_error(gs_error_VMerror); pfn->memory = fn_memory; pfn->pdev = pdev; pfn->pfd = pfd; if (base_font == 0 || pfd->notified) pfn->pfd = 0; else if (base_font != font) { /* * We need two notifications: one for ppf->font, one for * base_font. Create the latter first. */ pfn->pdfont = 0; if_debug4('_', "[_]register 0x%lx: pdf_font_descriptor_t 0x%lx => 0x%lx, id %ld\n", (ulong)pfn, (ulong)pfd, (ulong)base_font, base_font->id); code = gs_font_notify_register(base_font, pdf_font_notify_proc, pfn); if (code < 0) return 0; pfn = gs_alloc_struct(fn_memory, pdf_font_notify_t, &st_pdf_font_notify, "pdf_register_font"); if (pfn == 0) return_error(gs_error_VMerror); pfn->memory = fn_memory; pfn->pdev = pdev; pfn->pfd = 0; } if (pfd) pfd->notified = true; pfn->pdfont = ppf; #ifdef DEBUG if_debug4('_', "[_]register 0x%lx: pdf_font_t 0x%lx => 0x%lx, id %ld\n", (ulong)pfn, (ulong)ppf, (ulong)font, font->id); if (pfn->pfd) if_debug3('_', " pdf_font_descriptor_t 0x%lx => 0x%lx, id %ld\n", (ulong)pfd, (ulong)pfd->base_font, pfd->base_font->id); #endif ppf->font = font; return gs_font_notify_register(font, pdf_font_notify_proc, pfn); } private int pdf_finalize_font_descriptor(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd) { gs_font *font = pfd->base_font; int code = (font ? pdf_compute_font_descriptor(pdev, pfd, font, NULL) : 0); if (code >= 0) { if (pfd->FontFile_id) code = pdf_write_embedded_font(pdev, pfd); else code = pdf_write_FontDescriptor(pdev, pfd); pfd->written = true; } pfd->base_font = 0; /* clean up for GC */ return code; } private int pdf_font_notify_proc(void *vpfn /*proc_data*/, void *event_data) { pdf_font_notify_t *const pfn = vpfn; gx_device_pdf *const pdev = pfn->pdev; pdf_font_t *const ppf = pfn->pdfont; /* may be 0 */ pdf_font_descriptor_t *pfd = pfn->pfd; /* may be 0 */ int code = 0; if (event_data) return 0; /* unknown event */ if (ppf) { /* * The font of this pdf_font is about to be freed. Just clear the * pointer. */ gs_font *const font = ppf->font; if_debug4('_', "[_] notify 0x%lx: pdf_font_t 0x%lx => 0x%lx, id %ld\n", (ulong)pfn, (ulong)ppf, (ulong)font, font->id); gs_font_notify_unregister(font, pdf_font_notify_proc, vpfn); ppf->font = 0; } if (pfd) { /* * The base_font of this FontDescriptor is about to be freed. * Write the FontDescriptor and, if relevant, the FontFile. */ gs_font *const font = pfd->base_font; if_debug4('_', "[_] notify 0x%lx: pdf_font_descriptor_t 0x%lx => 0x%lx, id %ld\n", (ulong)pfn, (ulong)pfd, (ulong)font, font->id); gs_font_notify_unregister(font, pdf_font_notify_proc, vpfn); /* * HACK: temporarily patch the font's memory to one that we know is * available even during GC or restore. (Eventually we need to fix * this by prohibiting implementations of font_info and glyph_info * from doing any allocations.) However, don't use gs_memory_default * directly, because some malloc/free implementations (such as the * default one in some versions of Linux) don't recognize LIFO * behavior and can wind up allocation huge amounts of address space. */ { gs_memory_t *save_memory = font->memory; gs_ref_memory_t *fmem = ialloc_alloc_state((gs_raw_memory_t *)&gs_memory_default, 5000); if (fmem == 0) return_error(gs_error_VMerror); font->memory = (gs_memory_t *)fmem; code = pdf_finalize_font_descriptor(pdev, pfd); gs_memory_free_all((gs_memory_t *)fmem, FREE_ALL_EVERYTHING, "pdf_font_notify_proc"); font->memory = save_memory; } } gs_free_object(pfn->memory, vpfn, "pdf_font_notify_proc"); return code; } /* Write out the font resources when wrapping up the output. */ private void pdf_font_unreg_proc(void *vpfn /*proc_data*/) { pdf_font_notify_t *const pfn = vpfn; gs_free_object(pfn->memory, vpfn, "pdf_font_unreg_proc"); } /* Write out the font resources when wrapping up the output. */ int pdf_write_font_resources(gx_device_pdf *pdev) { int j; /* Write the fonts and descriptors. */ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) { pdf_font_t *ppf; pdf_font_descriptor_t *pfd; for (ppf = (pdf_font_t *)pdev->resources[resourceFont].chains[j]; ppf != 0; ppf = ppf->next ) { if (PDF_FONT_IS_SYNTHESIZED(ppf)) pdf_write_synthesized_type3(pdev, ppf); else { gs_const_string font_name; pfd = ppf->FontDescriptor; if (pfd) { font_name.data = pfd->FontName.chars; font_name.size = pfd->FontName.size; } else { /* must be composite */ font_name.data = 0; font_name.size = 0; } pdf_write_font_resource(pdev, ppf, &font_name); if (ppf->font) gs_notify_unregister_calling(&ppf->font->notify_list, pdf_font_notify_proc, NULL, pdf_font_unreg_proc); } } for (ppf = (pdf_font_t *)pdev->resources[resourceCIDFont].chains[j]; ppf != 0; ppf = ppf->next ) { gs_const_string font_name; pfd = ppf->FontDescriptor; font_name.data = pfd->FontName.chars; font_name.size = pfd->FontName.size; pdf_write_font_resource(pdev, ppf, &font_name); if (ppf->font) gs_notify_unregister_calling(&ppf->font->notify_list, pdf_font_notify_proc, NULL, pdf_font_unreg_proc); } for (pfd = (pdf_font_descriptor_t *)pdev->resources[resourceFontDescriptor].chains[j]; pfd != 0; pfd = pfd->next ) { if (!pfd->written) { pdf_finalize_font_descriptor(pdev, pfd); if (pfd->base_font) gs_notify_unregister_calling(&pfd->base_font->notify_list, pdf_font_notify_proc, NULL, pdf_font_unreg_proc); } } } /* If required, write the Encoding for Type 3 bitmap fonts. */ if (pdev->embedded_encoding_id) { stream *s; int i; pdf_open_separate(pdev, pdev->embedded_encoding_id); s = pdev->strm; /* * Even though the PDF reference documentation says that a * BaseEncoding key is required unless the encoding is * "based on the base font's encoding" (and there is no base * font in this case), Acrobat 2.1 gives an error if the * BaseEncoding key is present. */ stream_puts(s, "<max_embedded_code; ++i) { if (!(i & 15)) stream_puts(s, "\n"); pprintd1(s, "/a%d", i); } stream_puts(s, "\n] >>\n"); pdf_end_separate(pdev); } return 0; }