diff options
Diffstat (limited to 'gs/base/gdevpsf2.c')
-rw-r--r-- | gs/base/gdevpsf2.c | 1836 |
1 files changed, 1836 insertions, 0 deletions
diff --git a/gs/base/gdevpsf2.c b/gs/base/gdevpsf2.c new file mode 100644 index 000000000..abdf1623c --- /dev/null +++ b/gs/base/gdevpsf2.c @@ -0,0 +1,1836 @@ +/* Copyright (C) 2001-2006 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* $Id$ */ +/* Write an embedded CFF font with either Type 1 or Type 2 CharStrings */ +#include "math_.h" /* for fabs */ +#include "memory_.h" +#include "gx.h" +#include "gxarith.h" +#include "gscencs.h" +#include "gserrors.h" +#include "gsccode.h" +#include "gscrypt1.h" +#include "gsmatrix.h" +#include "gsutil.h" +#include "gxfixed.h" +#include "gxfont.h" +#include "gxfont1.h" +#include "gxfcid.h" +#include "stream.h" +#include "sfilter.h" +#include "gdevpsf.h" + +/* Define additional opcodes used in Dicts, but not in CharStrings. */ +#define CD_LONGINT 29 +#define CD_REAL 30 + +/* Define the count of standard strings. */ +#define NUM_STD_STRINGS 391 + +/* Define whether or not to skip writing an empty Subrs Index. */ +#define SKIP_EMPTY_SUBRS + +/* Define the structure for the string table. */ +typedef struct cff_string_item_s { + gs_const_string key; + int index1; /* index + 1, 0 means empty */ +} cff_string_item_t; +typedef struct cff_string_table_s { + cff_string_item_t *items; + int count; + int size; + uint total; + int reprobe; +} cff_string_table_t; + +/* Define the state of the CFF writer. */ +typedef struct cff_writer_s { + int options; + stream *strm; + gs_font_base *pfont; /* type1 or cid0 */ + glyph_data_proc_t glyph_data; + int offset_size; + long start_pos; + cff_string_table_t std_strings; + cff_string_table_t strings; + gs_int_rect FontBBox; +} cff_writer_t; +typedef struct cff_glyph_subset_s { + psf_outline_glyphs_t glyphs; + int num_encoded; /* glyphs.subset_data[1..num_e] are encoded */ + int num_encoded_chars; /* Encoding has num_e_chars defined entries */ +} cff_glyph_subset_t; + +/* ---------------- Output utilities ---------------- */ + +/* ------ String tables ------ */ + +/* Initialize a string table. */ +static void +cff_string_table_init(cff_string_table_t *pcst, cff_string_item_t *items, + int size) +{ + int reprobe = 17; + + memset(items, 0, size * sizeof(*items)); + pcst->items = items; + pcst->count = 0; + pcst->size = size; + while (reprobe != 1 && igcd(size, reprobe) != 1) + reprobe = (reprobe * 2 + 1) % size; + pcst->total = 0; + pcst->reprobe = reprobe; +} + +/* Add a string to a string table. */ +static int +cff_string_add(cff_string_table_t *pcst, const byte *data, uint size) +{ + int index; + + if (pcst->count >= pcst->size) + return_error(gs_error_limitcheck); + index = pcst->count++; + pcst->items[index].key.data = data; + pcst->items[index].key.size = size; + pcst->total += size; + return index; +} + +/* Look up a string, optionally adding it. */ +/* Return 1 if the string was added. */ +static int +cff_string_index(cff_string_table_t *pcst, const byte *data, uint size, + bool enter, int *pindex) +{ + /****** FAILS IF TABLE FULL AND KEY MISSING ******/ + int j = (size == 0 ? 0 : data[0] * 23 + data[size - 1] * 59 + size); + int index, c = 0; + + while ((index = pcst->items[j %= pcst->size].index1) != 0) { + --index; + if (!bytes_compare(pcst->items[index].key.data, + pcst->items[index].key.size, data, size)) { + *pindex = index; + return 0; + } + j += pcst->reprobe; + if (++c >= pcst->size) + break; + } + if (!enter) + return_error(gs_error_undefined); + index = cff_string_add(pcst, data, size); + if (index < 0) + return index; + pcst->items[j].index1 = index + 1; + *pindex = index; + return 1; +} + +/* Get the SID for a string or a glyph. */ +static int +cff_string_sid(cff_writer_t *pcw, const byte *data, uint size) +{ + int index; + int code = cff_string_index(&pcw->std_strings, data, size, false, &index); + + if (code < 0) { + code = cff_string_index(&pcw->strings, data, size, true, &index); + if (code < 0) + return code; + index += NUM_STD_STRINGS; + } + return index; +} +static int +cff_glyph_sid(cff_writer_t *pcw, gs_glyph glyph) +{ + gs_const_string str; + int code = + pcw->pfont->procs.glyph_name((gs_font *)pcw->pfont, glyph, &str); + + if (code < 0) + return code; + return cff_string_sid(pcw, str.data, str.size); +} + +/* ------ Low level ------ */ + +static void +put_card16(cff_writer_t *pcw, uint c16) +{ + sputc(pcw->strm, (byte)(c16 >> 8)); + sputc(pcw->strm, (byte)c16); +} +static int +offset_size(uint offset) +{ + int size = 1; + + while (offset > 255) + offset >>= 8, ++size; + return size; +} +static void +put_offset(cff_writer_t *pcw, int offset) +{ + int i; + + for (i = pcw->offset_size - 1; i >= 0; --i) + sputc(pcw->strm, (byte)(offset >> (i * 8))); +} +static int +put_bytes(stream * s, const byte *ptr, uint count) +{ + uint used; + + sputs(s, ptr, count, &used); + return (int)used; +} +static int +check_ioerror(stream * s) +{ + uint used; + + return sputs(s, (byte *)&used, 0, &used); +} + +/* ------ Data types ------ */ + +#define CE_OFFSET 32 +static void +cff_put_op(cff_writer_t *pcw, int op) +{ + if (op >= CE_OFFSET) { + sputc(pcw->strm, cx_escape); + sputc(pcw->strm, (byte)(op - CE_OFFSET)); + } else + sputc(pcw->strm, (byte)op); +} +static void +cff_put_int(cff_writer_t *pcw, int i) +{ + stream *s = pcw->strm; + + if (i >= -107 && i <= 107) + sputc(s, (byte)(i + 139)); + else if (i <= 1131 && i >= 0) + put_card16(pcw, (c_pos2_0 << 8) + i - 108); + else if (i >= -1131 && i < 0) + put_card16(pcw, (c_neg2_0 << 8) - i - 108); + else if (i >= -32768 && i <= 32767) { + sputc(s, c2_shortint); + put_card16(pcw, i & 0xffff); + } else { + sputc(s, CD_LONGINT); + put_card16(pcw, i >> 16); + put_card16(pcw, i & 0xffff); + } +} +static void +cff_put_int_value(cff_writer_t *pcw, int i, int op) +{ + cff_put_int(pcw, i); + cff_put_op(pcw, op); +} +static void +cff_put_int_if_ne(cff_writer_t *pcw, int i, int i_default, int op) +{ + if (i != i_default) + cff_put_int_value(pcw, i, op); +} +static void +cff_put_bool(cff_writer_t *pcw, bool b) +{ + cff_put_int(pcw, (b ? 1 : 0)); +} +static void +cff_put_bool_value(cff_writer_t *pcw, bool b, int op) +{ + cff_put_bool(pcw, b); + cff_put_op(pcw, op); +} +static void +cff_put_real(cff_writer_t *pcw, floatp f) +{ + if (f == (int)f) + cff_put_int(pcw, (int)f); + else { + /* Use decimal representation. */ + char str[50]; + byte b = 0xff; + const char *p; + + sprintf(str, "%g", f); + sputc(pcw->strm, CD_REAL); + for (p = str; ; ++p) { + int digit; + + switch (*p) { + case 0: + goto done; + case '.': + digit = 0xa; break; + case '+': + continue; + case '-': + digit = 0xe; break; + case 'e': case 'E': + if (p[1] == '-') + digit = 0xc, ++p; + else + digit = 0xb; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + digit = *p - '0'; + break; + default: /* can't happen */ + digit = 0xd; /* invalid */ + break; + } + if (b == 0xff) + b = (digit << 4) + 0xf; + else { + sputc(pcw->strm, (byte)((b & 0xf0) + digit)); + b = 0xff; + } + } + done: + sputc(pcw->strm, b); + } +} +static void +cff_put_real_value(cff_writer_t *pcw, floatp f, int op) +{ + cff_put_real(pcw, f); + cff_put_op(pcw, op); +} +static void +cff_put_real_if_ne(cff_writer_t *pcw, floatp f, floatp f_default, int op) +{ + if ((float)f != (float)f_default) + cff_put_real_value(pcw, f, op); +} +static void +cff_put_real_deltarray(cff_writer_t *pcw, const float *pf, int count, int op) +{ + float prev = 0; + int i; + + if (count <= 0) + return; + for (i = 0; i < count; ++i) { + float f = pf[i]; + + cff_put_real(pcw, f - prev); + prev = f; + } + cff_put_op(pcw, op); +} +static int +cff_put_string(cff_writer_t *pcw, const byte *data, uint size) +{ + int sid = cff_string_sid(pcw, data, size); + + if (sid < 0) + return sid; + cff_put_int(pcw, sid); + return 0; +} +static int +cff_put_string_value(cff_writer_t *pcw, const byte *data, uint size, int op) +{ + int code = cff_put_string(pcw, data, size); + + if (code >= 0) + cff_put_op(pcw, op); + return code; +} +static int +cff_extra_lenIV(const cff_writer_t *pcw, const gs_font_type1 *pfont) +{ + return (pcw->options & WRITE_TYPE2_NO_LENIV ? + max(pfont->data.lenIV, 0) : 0); +} +static bool +cff_convert_charstrings(const cff_writer_t *pcw, const gs_font_base *pfont) +{ + return (pfont->FontType != ft_encrypted2 && + (pcw->options & WRITE_TYPE2_CHARSTRINGS) != 0); +} +static int +cff_put_CharString(cff_writer_t *pcw, const byte *data, uint size, + gs_font_type1 *pfont) +{ + int lenIV = pfont->data.lenIV; + stream *s = pcw->strm; + + if (cff_convert_charstrings(pcw, (gs_font_base *)pfont)) { + gs_glyph_data_t gdata; + int code; + + gdata.memory = pfont->memory; + gs_glyph_data_from_string(&gdata, data, size, NULL); + code = psf_convert_type1_to_type2(s, &gdata, pfont); + if (code < 0) + return code; + } else if (lenIV < 0 || !(pcw->options & WRITE_TYPE2_NO_LENIV)) + put_bytes(s, data, size); + else if (size >= lenIV) { + /* Remove encryption. */ + crypt_state state = crypt_charstring_seed; + byte buf[50]; /* arbitrary */ + uint left, n; + + for (left = lenIV; left > 0; left -= n) { + n = min(left, sizeof(buf)); + gs_type1_decrypt(buf, data + lenIV - left, n, &state); + } + for (left = size - lenIV; left > 0; left -= n) { + n = min(left, sizeof(buf)); + gs_type1_decrypt(buf, data + size - left, n, &state); + put_bytes(s, buf, n); + } + } + return 0; +} +static uint +cff_Index_size(uint count, uint total) +{ + return (count == 0 ? 2 : + 3 + offset_size(total + 1) * (count + 1) + total); +} +static void +cff_put_Index_header(cff_writer_t *pcw, uint count, uint total) +{ + put_card16(pcw, count); + if (count > 0) { + pcw->offset_size = offset_size(total + 1); + sputc(pcw->strm, (byte)pcw->offset_size); + put_offset(pcw, 1); + } +} +static void +cff_put_Index(cff_writer_t *pcw, const cff_string_table_t *pcst) +{ + uint j, offset; + + if (pcst->count == 0) { + put_card16(pcw, 0); + return; + } + cff_put_Index_header(pcw, pcst->count, pcst->total); + for (j = 0, offset = 1; j < pcst->count; ++j) { + offset += pcst->items[j].key.size; + put_offset(pcw, offset); + } + for (j = 0; j < pcst->count; ++j) + put_bytes(pcw->strm, pcst->items[j].key.data, pcst->items[j].key.size); +} + +/* ---------------- Main code ---------------- */ + +/* ------ Header ------ */ + +/* Write the header, setting offset_size. */ +static int +cff_write_header(cff_writer_t *pcw, uint end_offset) +{ + pcw->offset_size = (end_offset > 0x7fff ? 3 : 2); + put_bytes(pcw->strm, (const byte *)"\001\000\004", 3); + sputc(pcw->strm, (byte)pcw->offset_size); + return 0; +} + +/* ------ Top Dict ------ */ + +/* + * There are 3 variants of this: Type 1 / Type 2 font, CIDFontType 0 + * CIDFont, and FDArray entry for CIDFont. + */ + +typedef enum { + TOP_version = 0, + TOP_Notice = 1, + TOP_FullName = 2, + TOP_FamilyName = 3, + TOP_Weight = 4, + TOP_FontBBox = 5, + TOP_UniqueID = 13, + TOP_XUID = 14, + TOP_charset = 15, /* (offset or predefined index) */ +#define charset_ISOAdobe 0 +#define charset_Expert 1 +#define charset_ExpertSubset 2 +#define charset_DEFAULT 0 + TOP_Encoding = 16, /* (offset or predefined index) */ +#define Encoding_Standard 0 +#define Encoding_Expert 1 +#define Encoding_DEFAULT 0 + TOP_CharStrings = 17, /* (offset) */ + TOP_Private = 18, /* (offset) */ + TOP_Copyright = 32, + TOP_isFixedPitch = 33, +#define isFixedPitch_DEFAULT false + TOP_ItalicAngle = 34, +#define ItalicAngle_DEFAULT 0 + TOP_UnderlinePosition = 35, +#define UnderlinePosition_DEFAULT (-100) + TOP_UnderlineThickness = 36, +#define UnderlineThickness_DEFAULT 50 + TOP_PaintType = 37, +#define PaintType_DEFAULT 0 + TOP_CharstringType = 38, +#define CharstringType_DEFAULT 2 + TOP_FontMatrix = 39, /* default is [0.001 0 0 0.001 0 0] */ + TOP_StrokeWidth = 40, +#define StrokeWidth_DEFAULT 0 + TOP_ROS = 62, + TOP_CIDFontVersion = 63, +#define CIDFontVersion_DEFAULT 0 + TOP_CIDFontRevision = 64, +#define CIDFontRevision_DEFAULT 0 + TOP_CIDFontType = 65, +#define CIDFontType_DEFAULT 0 + TOP_CIDCount = 66, +#define CIDCount_DEFAULT 8720 + TOP_UIDBase = 67, + TOP_FDArray = 68, /* (offset) */ + TOP_FDSelect = 69, /* (offset) */ + TOP_FontName = 70 /* only used in FDArray "fonts" */ +} Top_op; + +static int +cff_get_Top_info_common(cff_writer_t *pcw, gs_font_base *pbfont, + bool full_info, gs_font_info_t *pinfo) +{ + pinfo->Flags_requested = FONT_IS_FIXED_WIDTH; + /* Preset defaults */ + pinfo->members = 0; + pinfo->Flags = pinfo->Flags_returned = 0; + pinfo->ItalicAngle = ItalicAngle_DEFAULT; + pinfo->UnderlinePosition = UnderlinePosition_DEFAULT; + pinfo->UnderlineThickness = UnderlineThickness_DEFAULT; + return pbfont->procs.font_info + ((gs_font *)pbfont, NULL, + (full_info ? + FONT_INFO_FLAGS | FONT_INFO_ITALIC_ANGLE | + FONT_INFO_UNDERLINE_POSITION | + FONT_INFO_UNDERLINE_THICKNESS : 0) | + (FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE | + FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME), + pinfo); +} +static void +cff_write_Top_common(cff_writer_t *pcw, gs_font_base *pbfont, + bool write_FontMatrix, const gs_font_info_t *pinfo) +{ + /* + * The Adobe documentation doesn't make it at all clear that if the + * FontMatrix is missing (defaulted) in a CFF CIDFont, all of the + * FontMatrices of the subfonts in FDArray are multiplied by 1000. + * (This is documented for ordinary CIDFonts, but not for CFF CIDFonts.) + * Because of this, the FontMatrix for a CFF CIDFont must be written + * even if if is the default. write_FontMatrix controls this. + */ + /* (version) */ + if (pinfo->members & FONT_INFO_NOTICE) + cff_put_string_value(pcw, pinfo->Notice.data, pinfo->Notice.size, + TOP_Notice); + if (pinfo->members & FONT_INFO_FULL_NAME) + cff_put_string_value(pcw, pinfo->FullName.data, pinfo->FullName.size, + TOP_FullName); + if (pinfo->members & FONT_INFO_FAMILY_NAME) + cff_put_string_value(pcw, pinfo->FamilyName.data, + pinfo->FamilyName.size, TOP_FamilyName); + if (pcw->FontBBox.p.x != 0 || pcw->FontBBox.p.y != 0 || + pcw->FontBBox.q.x != 0 || pcw->FontBBox.q.y != 0 + ) { + /* An omitted FontBBox is equivalent to an empty one. */ + /* + * Since Acrobat Reader 4 on Solaris doesn't like + * an omitted FontBBox, we copy it here from + * the font descriptor, because the base font + * is allowed to omit it's FontBBox. + */ + cff_put_real(pcw, pcw->FontBBox.p.x); + cff_put_real(pcw, pcw->FontBBox.p.y); + cff_put_real(pcw, pcw->FontBBox.q.x); + cff_put_real(pcw, pcw->FontBBox.q.y); + cff_put_op(pcw, TOP_FontBBox); + } + if (uid_is_UniqueID(&pbfont->UID)) + cff_put_int_value(pcw, pbfont->UID.id, TOP_UniqueID); + else if (uid_is_XUID(&pbfont->UID)) { + int j; + + for (j = 0; j < uid_XUID_size(&pbfont->UID); ++j) + cff_put_int(pcw, uid_XUID_values(&pbfont->UID)[j]); + cff_put_op(pcw, TOP_XUID); + } + /* + * Acrobat Reader 3 gives an error if a CFF font includes any of the + * following opcodes. + */ + if (!(pcw->options & WRITE_TYPE2_AR3)) { + if (pinfo->members & FONT_INFO_COPYRIGHT) + cff_put_string_value(pcw, pinfo->Copyright.data, + pinfo->Copyright.size, TOP_Copyright); + if (pinfo->Flags & pinfo->Flags_returned & FONT_IS_FIXED_WIDTH) + cff_put_bool_value(pcw, true, TOP_isFixedPitch); + cff_put_real_if_ne(pcw, pinfo->ItalicAngle, ItalicAngle_DEFAULT, + TOP_ItalicAngle); + cff_put_int_if_ne(pcw, pinfo->UnderlinePosition, + UnderlinePosition_DEFAULT, TOP_UnderlinePosition); + cff_put_int_if_ne(pcw, pinfo->UnderlineThickness, + UnderlineThickness_DEFAULT, TOP_UnderlineThickness); + cff_put_int_if_ne(pcw, pbfont->PaintType, PaintType_DEFAULT, + TOP_PaintType); + } + { + static const gs_matrix fm_default = { + constant_matrix_body(0.001, 0, 0, 0.001, 0, 0) + }; + + if (write_FontMatrix || + pbfont->FontMatrix.xx != fm_default.xx || + pbfont->FontMatrix.xy != 0 || pbfont->FontMatrix.yx != 0 || + pbfont->FontMatrix.yy != fm_default.yy || + pbfont->FontMatrix.tx != 0 || pbfont->FontMatrix.ty != 0 + ) { + cff_put_real(pcw, pbfont->FontMatrix.xx); + cff_put_real(pcw, pbfont->FontMatrix.xy); + cff_put_real(pcw, pbfont->FontMatrix.yx); + cff_put_real(pcw, pbfont->FontMatrix.yy); + cff_put_real(pcw, pbfont->FontMatrix.tx); + cff_put_real(pcw, pbfont->FontMatrix.ty); + cff_put_op(pcw, TOP_FontMatrix); + } + } + cff_put_real_if_ne(pcw, pbfont->StrokeWidth, StrokeWidth_DEFAULT, + TOP_StrokeWidth); +} + +/* Type 1 or Type 2 font */ +static void +cff_write_Top_font(cff_writer_t *pcw, uint Encoding_offset, + uint charset_offset, uint CharStrings_offset, + uint Private_offset, uint Private_size) +{ + gs_font_base *pbfont = (gs_font_base *)pcw->pfont; + gs_font_info_t info; + + cff_get_Top_info_common(pcw, pbfont, true, &info); + cff_write_Top_common(pcw, pbfont, false, &info); + cff_put_int(pcw, Private_size); + cff_put_int_value(pcw, Private_offset, TOP_Private); + cff_put_int_value(pcw, CharStrings_offset, TOP_CharStrings); + cff_put_int_if_ne(pcw, charset_offset, charset_DEFAULT, TOP_charset); + cff_put_int_if_ne(pcw, Encoding_offset, Encoding_DEFAULT, TOP_Encoding); + { + int type = (pcw->options & WRITE_TYPE2_CHARSTRINGS ? 2 : + pbfont->FontType == ft_encrypted2 ? 2 : 1); + + cff_put_int_if_ne(pcw, type, CharstringType_DEFAULT, + TOP_CharstringType); + } +} + +/* CIDFontType 0 CIDFont */ +static void +cff_write_ROS(cff_writer_t *pcw, const gs_cid_system_info_t *pcidsi) +{ + cff_put_string(pcw, pcidsi->Registry.data, pcidsi->Registry.size); + cff_put_string(pcw, pcidsi->Ordering.data, pcidsi->Ordering.size); + cff_put_int_value(pcw, pcidsi->Supplement, TOP_ROS); +} +static void +cff_write_Top_cidfont(cff_writer_t *pcw, uint charset_offset, + uint CharStrings_offset, uint FDSelect_offset, + uint Font_offset, const gs_font_info_t *pinfo) +{ + gs_font_base *pbfont = (gs_font_base *)pcw->pfont; + gs_font_cid0 *pfont = (gs_font_cid0 *)pbfont; + + cff_write_ROS(pcw, &pfont->cidata.common.CIDSystemInfo); + cff_write_Top_common(pcw, pbfont, true, pinfo); /* full_info = true */ + cff_put_int_if_ne(pcw, charset_offset, charset_DEFAULT, TOP_charset); + cff_put_int_value(pcw, CharStrings_offset, TOP_CharStrings); + /* + * CIDFontVersion and CIDFontRevision aren't used consistently, + * so we don't currently write them. CIDFontType is always 0. + */ + cff_put_int_if_ne(pcw, pfont->cidata.common.CIDCount, CIDCount_DEFAULT, + TOP_CIDCount); + /* We don't use UIDBase. */ + cff_put_int_value(pcw, Font_offset, TOP_FDArray); + cff_put_int_value(pcw, FDSelect_offset, TOP_FDSelect); +} + +/* FDArray Index for CIDFont (offsets only) */ +static void +cff_write_FDArray_offsets(cff_writer_t *pcw, uint *FDArray_offsets, + int num_fonts) +{ + int j; + + cff_put_Index_header(pcw, num_fonts, + FDArray_offsets[num_fonts] - FDArray_offsets[0]); + for (j = 1; j <= num_fonts; ++j) + put_offset(pcw, FDArray_offsets[j] - FDArray_offsets[0] + 1); +} + +/* FDArray entry for CIDFont */ +static void +cff_write_Top_fdarray(cff_writer_t *pcw, gs_font_base *pbfont, + uint Private_offset, uint Private_size) +{ + const gs_font_name *pfname = &pbfont->font_name; + gs_font_info_t info; + + cff_get_Top_info_common(pcw, pbfont, false, &info); + cff_write_Top_common(pcw, pbfont, false, &info); + cff_put_int(pcw, Private_size); + cff_put_int_value(pcw, Private_offset, TOP_Private); + if (pfname->size == 0) + pfname = &pbfont->key_name; + if (pfname->size) { + cff_put_string(pcw, pfname->chars, pfname->size); + cff_put_op(pcw, TOP_FontName); + } +} + +/* ------ Private Dict ------ */ + +/* Defaults are noted in comments. */ +typedef enum { + PRIVATE_BlueValues = 6, /* (deltarray) */ + PRIVATE_OtherBlues = 7, /* (deltarray) */ + PRIVATE_FamilyBlues = 8, /* (deltarray) */ + PRIVATE_FamilyOtherBlues = 9, /* (deltarray) */ + PRIVATE_StdHW = 10, + PRIVATE_StdVW = 11, + PRIVATE_Subrs = 19, /* (offset, relative to Private Dict) */ + PRIVATE_defaultWidthX = 20, +#define defaultWidthX_DEFAULT fixed_0 + PRIVATE_nominalWidthX = 21, +#define nominalWidthX_DEFAULT fixed_0 + PRIVATE_BlueScale = 41, +#define BlueScale_DEFAULT 0.039625 + PRIVATE_BlueShift = 42, +#define BlueShift_DEFAULT 7 + PRIVATE_BlueFuzz = 43, +#define BlueFuzz_DEFAULT 1 + PRIVATE_StemSnapH = 44, /* (deltarray) */ + PRIVATE_StemSnapV = 45, /* (deltarray) */ + PRIVATE_ForceBold = 46, +#define ForceBold_DEFAULT false + PRIVATE_ForceBoldThreshold = 47, +#define ForceBoldThreshold_DEFAULT 0 + PRIVATE_lenIV = 48, +#define lenIV_DEFAULT (-1) + PRIVATE_LanguageGroup = 49, +#define LanguageGroup_DEFAULT 0 + PRIVATE_ExpansionFactor = 50, +#define ExpansionFactor_DEFAULT 0.06 + PRIVATE_initialRandomSeed = 51 +#define initialRandomSeed_DEFAULT 0 +} Private_op; + +const long default_defaultWidthX = defaultWidthX_DEFAULT; /* For gdevpsfx.c */ + +static void +cff_write_Private(cff_writer_t *pcw, uint Subrs_offset, + const gs_font_type1 *pfont) +{ +#define PUT_FLOAT_TABLE(member, op)\ + cff_put_real_deltarray(pcw, pfont->data.member.values,\ + pfont->data.member.count, op) + + PUT_FLOAT_TABLE(BlueValues, PRIVATE_BlueValues); + PUT_FLOAT_TABLE(OtherBlues, PRIVATE_OtherBlues); + PUT_FLOAT_TABLE(FamilyBlues, PRIVATE_FamilyBlues); + PUT_FLOAT_TABLE(FamilyOtherBlues, PRIVATE_FamilyOtherBlues); + if (pfont->data.StdHW.count > 0) + cff_put_real_value(pcw, pfont->data.StdHW.values[0], PRIVATE_StdHW); + if (pfont->data.StdVW.count > 0) + cff_put_real_value(pcw, pfont->data.StdVW.values[0], PRIVATE_StdVW); + if (Subrs_offset) + cff_put_int_value(pcw, Subrs_offset, PRIVATE_Subrs); + if (pfont->FontType != ft_encrypted) { + if (pfont->data.defaultWidthX != defaultWidthX_DEFAULT) + cff_put_real_value(pcw, fixed2float(pfont->data.defaultWidthX), + PRIVATE_defaultWidthX); + if (pfont->data.nominalWidthX != nominalWidthX_DEFAULT) + cff_put_real_value(pcw, fixed2float(pfont->data.nominalWidthX), + PRIVATE_nominalWidthX); + cff_put_int_if_ne(pcw, pfont->data.initialRandomSeed, + initialRandomSeed_DEFAULT, + PRIVATE_initialRandomSeed); + } + cff_put_real_if_ne(pcw, pfont->data.BlueScale, BlueScale_DEFAULT, + PRIVATE_BlueScale); + cff_put_real_if_ne(pcw, pfont->data.BlueShift, BlueShift_DEFAULT, + PRIVATE_BlueShift); + cff_put_int_if_ne(pcw, pfont->data.BlueFuzz, BlueFuzz_DEFAULT, + PRIVATE_BlueFuzz); + PUT_FLOAT_TABLE(StemSnapH, PRIVATE_StemSnapH); + PUT_FLOAT_TABLE(StemSnapV, PRIVATE_StemSnapV); + if (pfont->data.ForceBold != ForceBold_DEFAULT) + cff_put_bool_value(pcw, pfont->data.ForceBold, + PRIVATE_ForceBold); + /* (ForceBoldThreshold) */ + if (!(pcw->options & WRITE_TYPE2_NO_LENIV)) + cff_put_int_if_ne(pcw, pfont->data.lenIV, lenIV_DEFAULT, + PRIVATE_lenIV); + cff_put_int_if_ne(pcw, pfont->data.LanguageGroup, LanguageGroup_DEFAULT, + PRIVATE_LanguageGroup); + cff_put_real_if_ne(pcw, pfont->data.ExpansionFactor, + ExpansionFactor_DEFAULT, PRIVATE_ExpansionFactor); + /* initialRandomSeed was handled above */ + +#undef PUT_FLOAT_TABLE +} + +/* ------ CharStrings Index ------ */ + +/* These are separate procedures only for readability. */ +static int +cff_write_CharStrings_offsets(cff_writer_t *pcw, psf_glyph_enum_t *penum, + uint *pcount) +{ + gs_font_base *pfont = pcw->pfont; + int offset; + gs_glyph glyph; + uint count; + stream poss; + int code; + + s_init(&poss, NULL); + psf_enumerate_glyphs_reset(penum); + for (glyph = gs_no_glyph, count = 0, offset = 1; + (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1; + ++count) { + gs_glyph_data_t gdata; + gs_font_type1 *pfd; + int gcode; + + gdata.memory = pfont->memory; + if (code == 0 && + (gcode = pcw->glyph_data(pfont, glyph, &gdata, &pfd)) >= 0 + ) { + int extra_lenIV; + + if (gdata.bits.size >= (extra_lenIV = cff_extra_lenIV(pcw, pfd))) { + if (cff_convert_charstrings(pcw, (gs_font_base *)pfd)) { + swrite_position_only(&poss); + code = psf_convert_type1_to_type2(&poss, &gdata, pfd); + if (code < 0) + return code; + offset += stell(&poss); + } else + offset += gdata.bits.size - extra_lenIV; + } + gs_glyph_data_free(&gdata, "cff_write_CharStrings_offsets"); + } + put_offset(pcw, offset); + } + *pcount = count; + return offset - 1; +} +static void +cff_write_CharStrings(cff_writer_t *pcw, psf_glyph_enum_t *penum, + uint charstrings_count, uint charstrings_size) +{ + gs_font_base *pfont = pcw->pfont; + uint ignore_count; + gs_glyph glyph; + int code; + + cff_put_Index_header(pcw, charstrings_count, charstrings_size); + cff_write_CharStrings_offsets(pcw, penum, &ignore_count); + psf_enumerate_glyphs_reset(penum); + for (glyph = gs_no_glyph; + (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1; + ) { + gs_glyph_data_t gdata; + gs_font_type1 *pfd; + + gdata.memory = pfont->memory; + if (code == 0 && + (code = pcw->glyph_data(pfont, glyph, &gdata, &pfd)) >= 0 + ) { + cff_put_CharString(pcw, gdata.bits.data, gdata.bits.size, pfd); + gs_glyph_data_free(&gdata, "cff_write_CharStrings"); + } + } +} + +/* ------ [G]Subrs Index ------ */ + +/* + * Currently, we always write all the Subrs, even for subsets. + * We will fix this someday. + */ + +static uint +cff_write_Subrs_offsets(cff_writer_t *pcw, uint *pcount, gs_font_type1 *pfont, + bool global) +{ + int extra_lenIV = cff_extra_lenIV(pcw, pfont); + int j, offset; + int code; + gs_glyph_data_t gdata; + + gdata.memory = pfont->memory; + for (j = 0, offset = 1; + (code = pfont->data.procs.subr_data(pfont, j, global, &gdata)) != + gs_error_rangecheck; + ++j) { + if (code >= 0 && gdata.bits.size >= extra_lenIV) + offset += gdata.bits.size - extra_lenIV; + put_offset(pcw, offset); + if (code >= 0) + gs_glyph_data_free(&gdata, "cff_write_Subrs_offsets"); + } + *pcount = j; + return offset - 1; +} + +static void +cff_write_Subrs(cff_writer_t *pcw, uint subrs_count, uint subrs_size, + gs_font_type1 *pfont, bool global) +{ + int j; + uint ignore_count; + gs_glyph_data_t gdata; + int code; + + gdata.memory = pfont->memory; + cff_put_Index_header(pcw, subrs_count, subrs_size); + cff_write_Subrs_offsets(pcw, &ignore_count, pfont, global); + for (j = 0; + (code = pfont->data.procs.subr_data(pfont, j, global, &gdata)) != + gs_error_rangecheck; + ++j) { + if (code >= 0) { + cff_put_CharString(pcw, gdata.bits.data, gdata.bits.size, pfont); + gs_glyph_data_free(&gdata, "cff_write_Subrs"); + } + } +} + +/* ------ Encoding/charset ------ */ + +static uint +cff_Encoding_size(int num_encoded, int num_encoded_chars) +{ + int n = min(num_encoded, 255); + + return 2 + n + + (num_encoded_chars > n ? + 1 + (num_encoded_chars - n) * 3 : 0); +} + +static int +cff_write_Encoding(cff_writer_t *pcw, cff_glyph_subset_t *pgsub) +{ + stream *s = pcw->strm; + /* This procedure is only used for Type 1 / Type 2 fonts. */ + gs_font_type1 *pfont = (gs_font_type1 *)pcw->pfont; + byte used[255], index[255], supplement[256]; + int num_enc = min(pgsub->num_encoded, sizeof(index)); + int nsupp = 0; + int j; + + memset(used, 0, num_enc); + for (j = 0; j < 256; ++j) { + gs_glyph glyph = pfont->procs.encode_char((gs_font *)pfont, + (gs_char)j, + GLYPH_SPACE_NAME); + int i; + + if (glyph == gs_no_glyph || glyph == pgsub->glyphs.notdef) + continue; + i = psf_sorted_glyphs_index_of(pgsub->glyphs.subset_data + 1, + pgsub->num_encoded, glyph); + if (i < 0) + continue; /* encoded but not in subset */ + if (i >= sizeof(used) || used[i]) + supplement[nsupp++] = j; + else + index[i] = j, used[i] = 1; + } + sputc(s, (byte)(nsupp ? 0x80 : 0)); + sputc(s, (byte)num_enc); +#ifdef DEBUG + { int num_enc_chars = pgsub->num_encoded_chars; + + if (nsupp != num_enc_chars - num_enc) + lprintf3("nsupp = %d, num_enc_chars = %d, num_enc = %d\n", + nsupp, num_enc_chars, num_enc); + for (j = 0; j < num_enc; ++j) + if (!used[j]) + lprintf2("glyph %d = 0x%lx not used\n", j, + pgsub->glyphs.subset_data[j + 1]); + } +#endif + put_bytes(s, index, num_enc); + if (nsupp) { + /* Write supplementary entries for multiply-encoded glyphs. */ + sputc(s, (byte)nsupp); + for (j = 0; j < nsupp; ++j) { + byte chr = supplement[j]; + + sputc(s, chr); + put_card16(pcw, + cff_glyph_sid(pcw, + pfont->procs.encode_char((gs_font *)pfont, + (gs_char)chr, + GLYPH_SPACE_NAME))); + } + } + return 0; +} + +static int +cff_write_charset(cff_writer_t *pcw, cff_glyph_subset_t *pgsub) +{ + int j; + + sputc(pcw->strm, 0); + for (j = 1; j < pgsub->glyphs.subset_size; ++j) + put_card16(pcw, cff_glyph_sid(pcw, pgsub->glyphs.subset_data[j])); + return 0; +} +static int +cff_write_cidset(cff_writer_t *pcw, psf_glyph_enum_t *penum) +{ + gs_glyph glyph; + int code; + + sputc(pcw->strm, 0); + psf_enumerate_glyphs_reset(penum); + while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) { + /* Skip glyph 0 (the .notdef glyph), which is always first. */ + if (glyph != gs_min_cid_glyph) + put_card16(pcw, (uint)(glyph - gs_min_cid_glyph)); + } + return min(code, 0); +} + +/* ------ FDSelect ------ */ + +/* Determine the size of FDSelect. */ +static uint +cff_FDSelect_size(cff_writer_t *pcw, psf_glyph_enum_t *penum, uint *pformat) +{ + gs_font_cid0 *const pfont = (gs_font_cid0 *)pcw->pfont; + gs_font_base *const pbfont = (gs_font_base *)pfont; + gs_glyph glyph; + int prev = -1; + uint linear_size = 1, range_size = 5; + int code; + + /* Determine whether format 0 or 3 is more efficient. */ + psf_enumerate_glyphs_reset(penum); + while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) { + int font_index; + + code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index); + if (code >= 0) { + if (font_index != prev) + range_size += 3, prev = font_index; + ++linear_size; + } + } + if (range_size < linear_size) { + *pformat = 3; + return range_size; + } else { + *pformat = 0; + return linear_size; + } +} + +/* Write FDSelect. size and format were returned by cff_FDSelect_size. */ +static int +cff_write_FDSelect(cff_writer_t *pcw, psf_glyph_enum_t *penum, uint size, + int format) +{ + stream *s = pcw->strm; + gs_font_cid0 *const pfont = (gs_font_cid0 *)pcw->pfont; + gs_font_base *const pbfont = (gs_font_base *)pfont; + gs_glyph glyph; + int prev = -1; + uint cid_count = 0; + int code; + + spputc(s, (byte)format); + psf_enumerate_glyphs_reset(penum); + switch (format) { + case 3: /* ranges */ + put_card16(pcw, (size - 5) / 3); + while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) { + int font_index; + + code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index); + if (code >= 0) { + if (font_index != prev) { + put_card16(pcw, cid_count); + sputc(s, (byte)font_index); + prev = font_index; + } + ++cid_count; + } + } + put_card16(pcw, cid_count); + break; + case 0: /* linear table */ + while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) { + int font_index; + + code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index); + if (code >= 0) + sputc(s, (byte)font_index); + } + break; + default: /* not possible */ + return_error(gs_error_rangecheck); + } + return 0; +} + +/* ------ Main procedure ------ */ + +/* Write the CFF definition of a Type 1 or Type 2 font. */ +int +psf_write_type2_font(stream *s, gs_font_type1 *pfont, int options, + gs_glyph *subset_glyphs, uint subset_size, + const gs_const_string *alt_font_name, + gs_int_rect *FontBBox) +{ + gs_font_base *const pbfont = (gs_font_base *)pfont; + cff_writer_t writer; + cff_glyph_subset_t subset; + cff_string_item_t *std_string_items; + cff_string_item_t *string_items; + gs_const_string font_name; + stream poss; + uint charstrings_count, charstrings_size; + uint subrs_count, subrs_size; + uint gsubrs_count, gsubrs_size, encoding_size, charset_size; + uint number_of_glyphs = 0, number_of_strings; + /* + * Set the offsets and sizes to the largest reasonable values + * (see below). + */ + uint + Top_size = 0x7fffff, + GSubrs_offset, + Encoding_offset, + charset_offset, + CharStrings_offset, + Private_offset, + Private_size = 0x7fffff, + Subrs_offset, + End_offset = 0x7fffff; + int j; + psf_glyph_enum_t genum; + gs_glyph glyph; + long start_pos; + uint offset; + int code; + + /* Allocate the string tables. */ + psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, + NULL, 0, GLYPH_SPACE_NAME); + while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1) + number_of_glyphs++; + subset.glyphs.subset_data = (gs_glyph *)gs_alloc_bytes(pfont->memory, + number_of_glyphs * sizeof(glyph), "psf_write_type2_font"); + number_of_strings = number_of_glyphs + MAX_CFF_MISC_STRINGS; + std_string_items = (cff_string_item_t *)gs_alloc_bytes(pfont->memory, + (MAX_CFF_STD_STRINGS + number_of_strings) * sizeof(cff_string_item_t), + "psf_write_type2_font"); + if (std_string_items == NULL || subset.glyphs.subset_data == NULL) + return_error(gs_error_VMerror); + string_items = std_string_items + MAX_CFF_STD_STRINGS; + + /* Get subset glyphs. */ + code = psf_get_type1_glyphs(&subset.glyphs, pfont, subset_glyphs, + subset_size); + if (code < 0) + return code; + if (subset.glyphs.notdef == gs_no_glyph) + return_error(gs_error_rangecheck); /* notdef is required */ + + /* If we're writing Type 2 CharStrings, don't encrypt them. */ + if (options & WRITE_TYPE2_CHARSTRINGS) { + options |= WRITE_TYPE2_NO_LENIV; + if (pfont->FontType != ft_encrypted2) + pfont->data.defaultWidthX = pfont->data.nominalWidthX = 0; + } + writer.options = options; + s_init(&poss, NULL); + swrite_position_only(&poss); + writer.strm = &poss; + writer.pfont = pbfont; + writer.glyph_data = psf_type1_glyph_data; + writer.offset_size = 1; /* arbitrary */ + writer.start_pos = stell(s); + writer.FontBBox = *FontBBox; + + /* Initialize the enumeration of the glyphs. */ + psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, + subset.glyphs.subset_glyphs, + (subset.glyphs.subset_glyphs ? + subset.glyphs.subset_size : 0), + GLYPH_SPACE_NAME); + + /* Shuffle the glyphs into the order .notdef, encoded, unencoded. */ + { + gs_glyph encoded[256]; + int num_enc, num_enc_chars; + + /* Get the list of encoded glyphs. */ + for (j = 0, num_enc_chars = 0; j < 256; ++j) { + glyph = pfont->procs.encode_char((gs_font *)pfont, (gs_char)j, + GLYPH_SPACE_NAME); + if (glyph != gs_no_glyph && glyph != subset.glyphs.notdef && + (subset.glyphs.subset_glyphs == 0 || + psf_sorted_glyphs_include(subset.glyphs.subset_data, + subset.glyphs.subset_size, glyph))) + encoded[num_enc_chars++] = glyph; + } + subset.num_encoded_chars = num_enc_chars; + subset.num_encoded = num_enc = + psf_sort_glyphs(encoded, num_enc_chars); + + /* Get the complete list of glyphs if we don't have it already. */ + if (!subset.glyphs.subset_glyphs) { + int num_glyphs = 0; + + psf_enumerate_glyphs_reset(&genum); + while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1) + if (code == 0) { + if (num_glyphs == number_of_glyphs) + return_error(gs_error_limitcheck); + subset.glyphs.subset_data[num_glyphs++] = glyph; + } + subset.glyphs.subset_size = + psf_sort_glyphs(subset.glyphs.subset_data, num_glyphs); + subset.glyphs.subset_glyphs = subset.glyphs.subset_data; + } + + /* Move the unencoded glyphs to the top of the list. */ + /* + * We could do this in time N rather than N log N with a two-finger + * algorithm, but it doesn't seem worth the trouble right now. + */ + { + int from = subset.glyphs.subset_size; + int to = from; + + while (from > 0) { + glyph = subset.glyphs.subset_data[--from]; + if (glyph != subset.glyphs.notdef && + !psf_sorted_glyphs_include(encoded, num_enc, glyph)) + subset.glyphs.subset_data[--to] = glyph; + } +#ifdef DEBUG + if (to != num_enc + 1) + lprintf2("to = %d, num_enc + 1 = %d\n", to, num_enc + 1); +#endif + } + + /* Move .notdef and the encoded glyphs to the bottom of the list. */ + subset.glyphs.subset_data[0] = subset.glyphs.notdef; + memcpy(subset.glyphs.subset_data + 1, encoded, + sizeof(encoded[0]) * num_enc); + } + + /* Set the font name. */ + if (alt_font_name) + font_name = *alt_font_name; + else + font_name.data = pfont->font_name.chars, + font_name.size = pfont->font_name.size; + + /* Initialize the string tables. */ + cff_string_table_init(&writer.std_strings, std_string_items, + MAX_CFF_STD_STRINGS); + for (j = 0; (glyph = gs_c_known_encode((gs_char)j, + ENCODING_INDEX_CFFSTRINGS)) != gs_no_glyph; + ++j) { + gs_const_string str; + int ignore; + + gs_c_glyph_name(glyph, &str); + cff_string_index(&writer.std_strings, str.data, str.size, true, + &ignore); + } + cff_string_table_init(&writer.strings, string_items, number_of_strings); + + /* Enter miscellaneous strings in the string table. */ + cff_write_Top_font(&writer, 0, 0, 0, 0, 0); + + /* Enter the glyph names in the string table. */ + /* (Note that we have changed the glyph list.) */ + psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, + subset.glyphs.subset_data, + subset.glyphs.subset_size, + GLYPH_SPACE_NAME); + while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1) + if (code == 0) { + code = cff_glyph_sid(&writer, glyph); + if (code < 0) + return code; + } + + /* + * The CFF specification says that the Encoding, charset, CharStrings, + * Private, and Local Subr sections may be in any order. To minimize + * the risk of incompatibility with Adobe software, we produce them in + * the order just mentioned. + */ + + /* + * Compute the size of the GSubrs Index, if not omitted. + */ + if ((options & WRITE_TYPE2_NO_GSUBRS) != 0 || + cff_convert_charstrings(&writer, pbfont) /* we expand all Subrs */ + ) + gsubrs_count = 0, gsubrs_size = 0; + else + gsubrs_size = cff_write_Subrs_offsets(&writer, &gsubrs_count, + pfont, true); + + /* + * Compute the size of the Encoding. For simplicity, we currently + * always store the Encoding explicitly. Note that because CFF stores + * the Encoding in an "inverted" form, we need to count the number of + * glyphs that occur at more than one place in the Encoding. + */ + encoding_size = cff_Encoding_size(subset.num_encoded, + subset.num_encoded_chars); + + /* + * Compute the size of the charset. For simplicity, we currently + * always store the charset explicitly. + */ + charset_size = 1 + (subset.glyphs.subset_size - 1) * 2; + + /* Compute the size of the CharStrings Index. */ + code = cff_write_CharStrings_offsets(&writer, &genum, &charstrings_count); + if (code < 0) + return code; + charstrings_size = (uint)code; + + /* Compute the size of the (local) Subrs Index. */ +#ifdef SKIP_EMPTY_SUBRS + subrs_size = + (cff_convert_charstrings(&writer, pbfont) ? 0 : + cff_write_Subrs_offsets(&writer, &subrs_count, pfont, false)); +#else + if (cff_convert_charstrings(&writer, pbfont)) + subrs_count = 0; /* we expand all Subrs */ + subrs_size = cff_write_Subrs_offsets(&writer, &subrs_count, pfont, false); +#endif + + /* + * The offsets of the Private Dict and the CharStrings Index + * depend on the size of the Top Dict; the offset of the Subrs also + * depends on the size of the Private Dict. However, the size of the + * Top Dict depends on the offsets of the CharStrings Index, the + * charset, and the Encoding, and on the offset and size of the Private + * Dict, because of the variable-length encoding of the offsets and + * size; for the same reason, the size of the Private Dict depends on + * the offset of the Subrs. Fortunately, the relationship between the + * value of an offset or size and the size of its encoding is monotonic. + * Therefore, we start by assuming the largest reasonable value for all + * the sizes and iterate until everything converges. + */ + iter: + swrite_position_only(&poss); + writer.strm = &poss; + + /* Compute the offsets. */ + GSubrs_offset = 4 + cff_Index_size(1, font_name.size) + + cff_Index_size(1, Top_size) + + cff_Index_size(writer.strings.count, writer.strings.total); + Encoding_offset = GSubrs_offset + + cff_Index_size(gsubrs_count, gsubrs_size); + charset_offset = Encoding_offset + encoding_size; + CharStrings_offset = charset_offset + charset_size; + Private_offset = CharStrings_offset + + cff_Index_size(charstrings_count, charstrings_size); + Subrs_offset = Private_size; /* relative to Private Dict */ + + write: + if(check_ioerror(writer.strm)) + return_error(gs_error_ioerror); + start_pos = stell(writer.strm); + /* Write the header, setting offset_size. */ + cff_write_header(&writer, End_offset); + + /* Write the names Index. */ + cff_put_Index_header(&writer, 1, font_name.size); + put_offset(&writer, font_name.size + 1); + put_bytes(writer.strm, font_name.data, font_name.size); + + /* Write the Top Index. */ + cff_put_Index_header(&writer, 1, Top_size); + put_offset(&writer, Top_size + 1); + offset = stell(writer.strm) - start_pos; + cff_write_Top_font(&writer, Encoding_offset, charset_offset, + CharStrings_offset, + Private_offset, Private_size); + Top_size = stell(writer.strm) - start_pos - offset; + + /* Write the strings Index. */ + cff_put_Index(&writer, &writer.strings); + if(check_ioerror(writer.strm)) + return_error(gs_error_ioerror); + + /* Write the GSubrs Index, if any, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]GSubrs = %u => %u\n", GSubrs_offset, offset); + if (offset > GSubrs_offset) + return_error(gs_error_rangecheck); + GSubrs_offset = offset; + if (gsubrs_count == 0 || cff_convert_charstrings(&writer, pbfont)) + cff_put_Index_header(&writer, 0, 0); + else + cff_write_Subrs(&writer, gsubrs_count, gsubrs_size, pfont, true); + + /* Write the Encoding. */ + cff_write_Encoding(&writer, &subset); + + /* Write the charset. */ + cff_write_charset(&writer, &subset); + + /* Write the CharStrings Index, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if (offset > CharStrings_offset) + return_error(gs_error_rangecheck); + CharStrings_offset = offset; + cff_write_CharStrings(&writer, &genum, charstrings_count, + charstrings_size); + if(check_ioerror(writer.strm)) + return_error(gs_error_ioerror); + + /* Write the Private Dict, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if (offset > Private_offset) + return_error(gs_error_rangecheck); + Private_offset = offset; + cff_write_Private(&writer, (subrs_size == 0 ? 0 : Subrs_offset), pfont); + Private_size = stell(writer.strm) - start_pos - offset; + + /* Write the Subrs Index, checking the offset. */ + offset = stell(writer.strm) - (start_pos + Private_offset); + if (offset > Subrs_offset) + return_error(gs_error_rangecheck); + Subrs_offset = offset; + if (cff_convert_charstrings(&writer, pbfont)) + cff_put_Index_header(&writer, 0, 0); + else if (subrs_size != 0) + cff_write_Subrs(&writer, subrs_count, subrs_size, pfont, false); + + /* Check the final offset. */ + if(check_ioerror(writer.strm)) + return_error(gs_error_ioerror); + offset = stell(writer.strm) - start_pos; + if (offset > End_offset) + return_error(gs_error_rangecheck); + if (offset == End_offset) { + /* The iteration has converged. Write the result. */ + if (writer.strm == &poss) { + writer.strm = s; + goto write; + } + } else { + /* No convergence yet. */ + End_offset = offset; + goto iter; + } + + /* All done. */ + gs_free_object(pfont->memory, std_string_items, "psf_write_type2_font"); + gs_free_object(pfont->memory, subset.glyphs.subset_data, "psf_write_type2_font"); + return 0; +} + +/* Write the CFF definition of a CIDFontType 0 font (CIDFont). */ +static int +cid0_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *pgd, + gs_font_type1 **ppfont) +{ + gs_font_cid0 *const pfont = (gs_font_cid0 *)pbfont; + int font_index; + int code = pfont->cidata.glyph_data(pbfont, glyph, pgd, &font_index); + + if (code >= 0) + *ppfont = pfont->cidata.FDArray[font_index]; + return code; +} +#ifdef DEBUG +static int +offset_error(const char *msg) +{ + if_debug1('l', "[l]%s offset error\n", msg); + return gs_error_rangecheck; +} +#else +# define offset_error(msg) gs_error_rangecheck +#endif +int +psf_write_cid0_font(stream *s, gs_font_cid0 *pfont, int options, + const byte *subset_cids, uint subset_size, + const gs_const_string *alt_font_name) +{ + /* + * CIDFontType 0 fonts differ from ordinary Type 1 / Type 2 fonts + * as follows: + * The TOP Dict starts with a ROS operator. + * The TOP Dict must include FDArray and FDSelect operators. + * The TOP Dict may include CIDFontVersion, CIDFontRevision, + * CIDFontType, CIDCount, and UIDBase operators. + * The TOP Dict must not include an Encoding operator. + * The charset is defined in terms of CIDs rather than SIDs. + * FDArray references a Font Index in which each element is a Dict + * defining a font without charset, Encoding, or CharStrings. + * FDSelect references a structure mapping CIDs to font numbers. + */ + gs_font_base *const pbfont = (gs_font_base *)pfont; + cff_writer_t writer; + cff_string_item_t std_string_items[500]; /* 391 entries used */ + /****** HOW TO DETERMINE THE SIZE OF STRINGS? ******/ + cff_string_item_t string_items[500 /* character names */ + + 40 /* misc. values */]; + gs_const_string font_name; + stream poss; + uint charstrings_count, charstrings_size; + uint gsubrs_count, gsubrs_size; + uint charset_size, fdselect_size, fdselect_format; + uint subrs_count[256], subrs_size[256]; + /* + * Set the offsets and sizes to the largest reasonable values + * (see below). + */ + uint + Top_size = 0x7fffff, + GSubrs_offset = 0x7fffff, + charset_offset = 0x7fffff, + FDSelect_offset = 0x7fffff, + CharStrings_offset = 0x7fffff, + Font_offset = 0x7fffff, + FDArray_offsets[257], + Private_offsets[257], + Subrs_offsets[257], + End_offset = 0x7fffff; + int j; + psf_glyph_enum_t genum; + gs_font_info_t info; + long start_pos; + uint offset; + int num_fonts = pfont->cidata.FDArray_size; + int code; + + + /* Initialize the enumeration of the glyphs. */ + psf_enumerate_cids_begin(&genum, (gs_font *)pfont, subset_cids, + subset_size); + + /* Check that the font can be written. */ + code = psf_check_outline_glyphs((gs_font_base *)pfont, &genum, + cid0_glyph_data); + if (code < 0) + return code; + /* The .notdef glyph (glyph 0) must be included. */ + if (subset_cids && subset_size > 0 && !(subset_cids[0] & 0x80)) + return_error(gs_error_rangecheck); + + writer.options = options; + s_init(&poss, NULL); + swrite_position_only(&poss); + writer.strm = &poss; + writer.pfont = pbfont; + writer.glyph_data = cid0_glyph_data; + writer.offset_size = 1; /* arbitrary */ + writer.start_pos = stell(s); + writer.FontBBox.p.x = writer.FontBBox.p.y = 0; + writer.FontBBox.q.x = writer.FontBBox.q.y = 0; + + /* Set the font name. */ + if (alt_font_name) + font_name = *alt_font_name; + else if (pfont->font_name.size) + font_name.data = pfont->font_name.chars, + font_name.size = pfont->font_name.size; + else + font_name.data = pfont->key_name.chars, + font_name.size = pfont->key_name.size; + + /* Initialize the string tables. */ + cff_string_table_init(&writer.std_strings, std_string_items, + countof(std_string_items)); + cff_string_table_init(&writer.strings, string_items, + countof(string_items)); + + /* Make all entries in the string table. */ + cff_write_ROS(&writer, &pfont->cidata.common.CIDSystemInfo); + for (j = 0; j < num_fonts; ++j) { + gs_font_type1 *pfd = pfont->cidata.FDArray[j]; + + cff_write_Top_fdarray(&writer, (gs_font_base *)pfd, 0, 0); + } + + /* + * The CFF specification says that sections after the initial Indexes + * may be in any order. To minimize the risk of incompatibility with + * Adobe software, we produce them in the order illustrated in the + * specification. + */ + + /* Initialize the offset arrays. */ + for (j = 0; j <= num_fonts; ++j) + FDArray_offsets[j] = Private_offsets[j] = Subrs_offsets[j] = + 0x7effffff / num_fonts * j + 0x1000000; + + /* + * Compute the size of the GSubrs Index, if not omitted. + * Arbitrarily use FDArray[0] to access the GSubrs and to determine + * the CharString type. + */ + if ((options & WRITE_TYPE2_NO_GSUBRS) != 0 || + cff_convert_charstrings(&writer, + (const gs_font_base *)pfont->cidata.FDArray[0]) + /* we expand all Subrs */ + ) + gsubrs_count = 0, gsubrs_size = 0; + else + gsubrs_size = cff_write_Subrs_offsets(&writer, &gsubrs_count, + pfont->cidata.FDArray[0], true); + + /* + * Compute the size of the charset. For simplicity, we currently + * always store the charset explicitly. + */ + swrite_position_only(&poss); + cff_write_cidset(&writer, &genum); + charset_size = stell(&poss); + + /* Compute the size of the FDSelect strucure. */ + fdselect_size = cff_FDSelect_size(&writer, &genum, &fdselect_format); + + /* Compute the size of the CharStrings Index. */ + charstrings_size = + cff_write_CharStrings_offsets(&writer, &genum, &charstrings_count); + + /* Compute the size of the (local) Subrs Indexes. */ + for (j = 0; j < num_fonts; ++j) { + gs_font_type1 *pfd = pfont->cidata.FDArray[j]; + +#ifdef SKIP_EMPTY_SUBRS + subrs_size[j] = + (cff_convert_charstrings(&writer, (gs_font_base *)pfd) ? 0 : + cff_write_Subrs_offsets(&writer, &subrs_count[j], pfd, false)); +#else + if (cff_convert_charstrings(&writer, (gs_font_base *)pfd)) + subrs_count[j] = 0; /* we expand all Subrs */ + subrs_size[j] = cff_write_Subrs_offsets(&writer, &subrs_count[j], pfd, false); +#endif + } + + /* Get the font_info once, since it may be expensive. */ + cff_get_Top_info_common(&writer, (gs_font_base *)pfont, true, &info); + + /* + * The offsets of the Private Dict and the CharStrings Index + * depend on the size of the Top Dict; the offset of the Subrs also + * depends on the size of the Private Dict. However, the size of the + * Top Dict depends on the offsets of the CharStrings Index and the + * charset, and on the offset and size of the Private Dict, + * because of the variable-length encoding of the offsets and + * size; for the same reason, the size of the Private Dict depends on + * the offset of the Subrs. Fortunately, the relationship between the + * value of an offset or size and the size of its encoding is monotonic. + * Therefore, we start by assuming the largest reasonable value for all + * the sizes and iterate until everything converges. + */ + iter: + swrite_position_only(&poss); + writer.strm = &poss; + + /* Compute the offsets. */ + GSubrs_offset = 4 + cff_Index_size(1, font_name.size) + + cff_Index_size(1, Top_size) + + cff_Index_size(writer.strings.count, writer.strings.total); + charset_offset = GSubrs_offset + + cff_Index_size(gsubrs_count, gsubrs_size); + FDSelect_offset = charset_offset + charset_size; + CharStrings_offset = FDSelect_offset + fdselect_size; + if_debug4('l', "[l]GSubrs at %u, charset at %u, FDSelect at %u, CharStrings at %u\n", + GSubrs_offset, charset_offset, FDSelect_offset, CharStrings_offset); + + write: + start_pos = stell(writer.strm); + if_debug1('l', "[l]start_pos = %ld\n", start_pos); + /* Write the header, setting offset_size. */ + cff_write_header(&writer, End_offset); + + /* Write the names Index. */ + cff_put_Index_header(&writer, 1, font_name.size); + put_offset(&writer, font_name.size + 1); + put_bytes(writer.strm, font_name.data, font_name.size); + + /* Write the Top Index. */ + cff_put_Index_header(&writer, 1, Top_size); + put_offset(&writer, Top_size + 1); + offset = stell(writer.strm) - start_pos; + cff_write_Top_cidfont(&writer, charset_offset, CharStrings_offset, + FDSelect_offset, Font_offset, &info); + Top_size = stell(writer.strm) - start_pos - offset; + if_debug1('l', "[l]Top_size = %u\n", Top_size); + + /* Write the strings Index. */ + cff_put_Index(&writer, &writer.strings); + + /* Write the GSubrs Index, if any, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]GSubrs = %u => %u\n", GSubrs_offset, offset); + if (offset > GSubrs_offset) + return_error(gs_error_rangecheck); + GSubrs_offset = offset; + if (gsubrs_count == 0 || + cff_convert_charstrings(&writer, + (const gs_font_base *)pfont->cidata.FDArray[0]) + ) + cff_put_Index_header(&writer, 0, 0); + else + cff_write_Subrs(&writer, gsubrs_count, gsubrs_size, + pfont->cidata.FDArray[0], true); + + /* Write the charset. */ + if_debug1('l', "[l]charset = %lu\n", stell(writer.strm) - start_pos); + cff_write_cidset(&writer, &genum); + + /* Write the FDSelect structure, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]FDSelect = %u => %u\n", FDSelect_offset, offset); + if (offset > FDSelect_offset) + return_error(offset_error("FDselect")); + FDSelect_offset = offset; + cff_write_FDSelect(&writer, &genum, fdselect_size, fdselect_format); + + /* Write the CharStrings Index, checking the offset. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]CharStrings = %u => %u\n", CharStrings_offset, offset); + if (offset > CharStrings_offset) + return_error(offset_error("CharStrings")); + CharStrings_offset = offset; + cff_write_CharStrings(&writer, &genum, charstrings_count, + charstrings_size); + + /* Write the Font Dict Index. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]Font = %u => %u\n", Font_offset, offset); + if (offset > Font_offset) + return_error(offset_error("Font")); + Font_offset = offset; + cff_write_FDArray_offsets(&writer, FDArray_offsets, num_fonts); + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]FDArray[0] = %u => %u\n", FDArray_offsets[0], offset); + if (offset > FDArray_offsets[0]) + return_error(offset_error("FDArray[0]")); + FDArray_offsets[0] = offset; + for (j = 0; j < num_fonts; ++j) { + gs_font_type1 *pfd = pfont->cidata.FDArray[j]; + + /* If we're writing Type 2 CharStrings, don't encrypt them. */ + if (options & WRITE_TYPE2_CHARSTRINGS) { + options |= WRITE_TYPE2_NO_LENIV; + if (pfd->FontType != ft_encrypted2) + pfd->data.defaultWidthX = pfd->data.nominalWidthX = 0; + } + cff_write_Top_fdarray(&writer, (gs_font_base *)pfd, Private_offsets[j], + Private_offsets[j + 1] - Private_offsets[j]); + offset = stell(writer.strm) - start_pos; + if_debug3('l', "[l]FDArray[%d] = %u => %u\n", j + 1, + FDArray_offsets[j + 1], offset); + if (offset > FDArray_offsets[j + 1]) + return_error(offset_error("FDArray")); + FDArray_offsets[j + 1] = offset; + } + + /* Write the Private Dicts, checking the offset. */ + for (j = 0; ; ++j) { + gs_font_type1 *pfd; + + offset = stell(writer.strm) - start_pos; + if_debug3('l', "[l]Private[%d] = %u => %u\n", + j, Private_offsets[j], offset); + if (offset > Private_offsets[j]) + return_error(offset_error("Private")); + Private_offsets[j] = offset; + if (j == num_fonts) + break; + pfd = pfont->cidata.FDArray[j]; + cff_write_Private(&writer, + (subrs_size[j] == 0 ? 0 : Subrs_offsets[j]), pfd); + } + + /* Write the Subrs Indexes, checking the offsets. */ + for (j = 0; ; ++j) { + gs_font_type1 *pfd; + + offset = stell(writer.strm) - (start_pos + Private_offsets[j]); + if_debug3('l', "[l]Subrs[%d] = %u => %u\n", + j, Subrs_offsets[j], offset); + if (offset > Subrs_offsets[j]) + return_error(offset_error("Subrs")); + Subrs_offsets[j] = offset; + if (j == num_fonts) + break; + pfd = pfont->cidata.FDArray[j]; + if (cff_convert_charstrings(&writer, (gs_font_base *)pfd)) + cff_put_Index_header(&writer, 0, 0); + else if (subrs_size[j] != 0) + cff_write_Subrs(&writer, subrs_count[j], subrs_size[j], pfd, false); + } + + /* Check the final offset. */ + offset = stell(writer.strm) - start_pos; + if_debug2('l', "[l]End = %u => %u\n", End_offset, offset); + if (offset > End_offset) + return_error(offset_error("End")); + if (offset == End_offset) { + /* The iteration has converged. Write the result. */ + if (writer.strm == &poss) { + writer.strm = s; + goto write; + } + } else { + /* No convergence yet. */ + End_offset = offset; + goto iter; + } + + /* All done. */ + return 0; +} |