/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2021 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. 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. */ #ifdef HAVE_CONFIG_H #include #endif #include "system.h" #include "mem.h" #include "error.h" #include "dpxfile.h" #include "numbers.h" #include "pdfobj.h" #include "pdfdev.h" /* pdf_rect */ #include "pdfencoding.h" #include "pdffont.h" #include "pkfont.h" #define ENABLE_GLYPHENC 1 #ifndef PKFONT_DPI_DEFAULT #define PKFONT_DPI_DEFAULT 600u #endif static unsigned base_dpi = PKFONT_DPI_DEFAULT; void PKFont_set_dpi (int dpi) { if (dpi <= 0) ERROR("Invalid DPI: %d\n", dpi); base_dpi = dpi; } /* (Only) This requires TFM to get design size... */ #include "tfm.h" static unsigned truedpi (const char *ident, double point_size, unsigned bdpi) { unsigned dpi = bdpi; double design_size; int tfm_id; tfm_id = tfm_open(ident, 0); if (tfm_id < 0) return dpi; design_size = tfm_get_design_size(tfm_id); if (design_size <= 0.0) WARN("DESGIN_SIZE <= 0.0? (TFM=\"%s\")", ident); else { dpi = (unsigned) ROUND(base_dpi * point_size / design_size, 1.0); } return dpi; } static FILE * dpx_open_pk_font_at (const char *ident, unsigned dpi) { FILE *fp; char *fqpn; kpse_glyph_file_type kpse_file_info; /* If pdftex.map is used, there are cases for example ident is like cmr10.pfb. Because of the suffix .pfb here, mktexpk does not work right. So we remove the suffix .pfb, if it exists. */ fqpn = strrchr(ident, '.'); if (fqpn && strcasecmp(fqpn, ".pfb") == 0) { *fqpn = '\0'; } fqpn = kpse_find_glyph(ident, dpi, kpse_pk_format, &kpse_file_info); if (!fqpn) return NULL; fp = MFOPEN(fqpn, FOPEN_RBIN_MODE); RELEASE(fqpn); return fp; } int pdf_font_open_pkfont (pdf_font *font, const char *ident, int index, int encoding_id, int embedding, double point_size) { unsigned dpi; FILE *fp; if (!ident || point_size <= 0.0) return -1; if (!embedding) { WARN("Ignoring no-embed option for PK font: %s", ident); } if (index != 0) { WARN("Ignoring font index option for PK font: %s", ident); } dpi = truedpi(ident, point_size, base_dpi); fp = dpx_open_pk_font_at(ident, dpi); if (!fp) return -1; MFCLOSE(fp); /* Type 3 fonts doesn't have FontName. * FontFamily is recommended for PDF 1.5. */ font->fontname = NEW(strlen(ident)+1, char); strcpy(font->fontname, ident); if (encoding_id >= 0) { pdf_encoding_used_by_type3(encoding_id); WARN("PK font is found for font \"%s\" but non built-in encoding \"%s\" is specified.", ident, pdf_encoding_get_name(encoding_id)); #if ENABLE_GLYPHENC WARN(">> Assuming this is for glyph name assignment."); #else WARN(">> I can't reencode PK font. (not enough information available)"); WARN(">> Maybe you need to install pfb/opentype/truetype font."); #endif } return 0; } /* We are using Mask Image. Fill black is bit clear. * Optimizing those codes doesn't improve things. */ static uint32_t fill_black_run (unsigned char *dp, uint32_t left, uint32_t run_count) { const static unsigned char mask[8] = { 127u, 191u, 223u, 239u, 247u, 251u, 253u, 254u }; uint32_t right = left + run_count - 1; for ( ; left <= right; left++) { dp[left / 8] &= mask[left % 8]; } return run_count; } /* Just skip bits. See decode_packed() */ static uint32_t fill_white_run (uint32_t run_count) { return run_count; } static uint32_t pk_packed_num (uint32_t *np, int dyn_f, unsigned char *dp, uint32_t pl) { uint32_t nmbr = 0, i = *np; int nyb, j; #define get_nyb() ((i % 2) ? dp[i/2] & 0x0f : (dp[i/2] >> 4) & 0x0f) if (i / 2 == pl) { WARN("EOD reached while unpacking pk_packed_num."); return 0; } nyb = get_nyb(); i++; if (nyb == 0) { j = 0; do { if (i / 2 == pl) { WARN("EOD reached while unpacking pk_packed_num."); break; } nyb = get_nyb(); i++; j++; } while (nyb == 0); nmbr = nyb; while (j-- > 0) { if (i / 2 == pl) { WARN("EOD reached while unpacking pk_packed_num."); break; } nyb = get_nyb(); i++; nmbr = nmbr * 16 + nyb; } nmbr += (13 - dyn_f) * 16 + dyn_f - 15; } else if (nyb <= dyn_f) { nmbr = nyb; } else if (nyb < 14) { if (i / 2 == pl) { WARN("EOD reached while unpacking pk_packed_num."); return 0; } nmbr = (nyb - dyn_f - 1) * 16 + get_nyb() + dyn_f + 1; i++; } *np = i; return nmbr; } static void send_out (unsigned char *rowptr, uint32_t rowbytes, pdf_obj *stream) { pdf_add_stream(stream, (void *)rowptr, rowbytes); } static int pk_decode_packed (pdf_obj *stream, uint32_t wd, uint32_t ht, int dyn_f, int run_color, unsigned char *dp, uint32_t pl) { unsigned char *rowptr; uint32_t rowbytes; uint32_t i, np = 0; uint32_t run_count = 0, repeat_count = 0; rowbytes = (wd + 7) / 8; rowptr = NEW(rowbytes, unsigned char); /* repeat count is applied to the *current* row. * "run" can span across rows. * If there are non-zero repeat count and if run * spans across row, first repeat and then continue. */ for (np = 0, i = 0; i < ht; i++) { uint32_t rowbits_left, nbits; repeat_count = 0; memset(rowptr, 0xff, rowbytes); /* 1 is white */ rowbits_left = wd; /* Fill run left over from previous row */ if (run_count > 0) { nbits = MIN(rowbits_left, run_count); switch (run_color) { case 0: rowbits_left -= fill_black_run(rowptr, 0, nbits); break; case 1: rowbits_left -= fill_white_run(nbits); break; } run_count -= nbits; } /* Read nybbles until we have a full row */ while (np / 2 < pl && rowbits_left > 0) { int nyb; nyb = (np % 2) ? dp[np/2] & 0x0f : (dp[np/2] >> 4) & 0x0f; if (nyb == 14) { /* packed number "repeat_count" follows */ if (repeat_count != 0) WARN("Second repeat count for this row!"); np++; /* Consume this nybble */ repeat_count = pk_packed_num(&np, dyn_f, dp, pl); } else if (nyb == 15) { if (repeat_count != 0) WARN("Second repeat count for this row!"); np++; /* Consume this nybble */ repeat_count = 1; } else { /* run_count */ /* Interprete current nybble as packed number */ run_count = pk_packed_num(&np, dyn_f, dp, pl); nbits = MIN(rowbits_left, run_count); run_color = !run_color; run_count -= nbits; switch (run_color) { case 0: rowbits_left -= fill_black_run(rowptr, wd - rowbits_left, nbits); break; case 1: rowbits_left -= fill_white_run(nbits); break; } } } /* We got bitmap row data. */ send_out(rowptr, rowbytes, stream); for ( ; i < ht && repeat_count > 0; repeat_count--, i++) send_out(rowptr, rowbytes, stream); } RELEASE(rowptr); return 0; } static int pk_decode_bitmap (pdf_obj *stream, uint32_t wd, uint32_t ht, int dyn_f, int run_color, unsigned char *dp, uint32_t pl) { unsigned char *rowptr, c; uint32_t i, j, rowbytes; const static unsigned char mask[8] = { 0x80u, 0x40u, 0x20u, 0x10u, 0x08u, 0x04u, 0x02u, 0x01u }; ASSERT( dyn_f == 14 ); if (run_color != 0) { WARN("run_color != 0 for bitmap pk data?"); } else if (pl < (wd * ht + 7) / 8) { WARN("Insufficient bitmap pk data. %ldbytes expected but only %ldbytes read.", (wd * ht + 7) / 8, pl); return -1; } rowbytes = (wd + 7) / 8; rowptr = NEW(rowbytes, unsigned char); memset(rowptr, 0, rowbytes); /* Flip. PK bitmap is not byte aligned for each rows. */ for (i = 0, j = 0; i < ht * wd; i++) { c = dp[i / 8] & mask[i % 8]; if (c == 0) rowptr[j / 8] |= mask[i % 8]; /* flip bit */ j++; if (j == wd) { send_out(rowptr, rowbytes, stream); memset(rowptr, 0, rowbytes); j = 0; } } return 0; } static void do_preamble (FILE *fp) { /* Check for id byte */ if (fgetc(fp) == 89) { /* Skip comment */ skip_bytes(get_unsigned_byte(fp), fp); /* Skip other header info. It's normally used for verifying this is the file wethink it is */ skip_bytes(16, fp); } else { ERROR("embed_pk_font: PK ID byte is incorrect. Are you sure this is a PK file?"); } return; } struct pk_header_ { uint32_t pkt_len; int32_t chrcode; int32_t wd, dx, dy; uint32_t bm_wd, bm_ht; int32_t bm_hoff, bm_voff; int dyn_f, run_color; }; static int read_pk_char_header (struct pk_header_ *h, unsigned char opcode, FILE *fp) { ASSERT(h); if ((opcode & 4) == 0) { /* short */ h->pkt_len = (opcode & 3) << 8 | get_unsigned_byte(fp); h->chrcode = get_unsigned_byte(fp); h->wd = get_unsigned_triple(fp); /* TFM width */ h->dx = get_unsigned_byte(fp) << 16; /* horizontal escapement */ h->dy = 0; h->bm_wd = get_unsigned_byte(fp); h->bm_ht = get_unsigned_byte(fp); h->bm_hoff = get_signed_byte(fp); h->bm_voff = get_signed_byte(fp); h->pkt_len -= 8; } else if ((opcode & 7) == 7) { /* long */ h->pkt_len = get_positive_quad(fp, "PK", "pkt_len"); h->chrcode = get_signed_quad(fp); h->wd = get_signed_quad(fp); h->dx = get_signed_quad(fp); /* 16.16 fixed point number in pixels */ h->dy = get_signed_quad(fp); h->bm_wd = get_positive_quad(fp, "PK", "bm_wd"); h->bm_ht = get_positive_quad(fp, "PK", "bm_ht"); h->bm_hoff = get_signed_quad(fp); h->bm_voff = get_signed_quad(fp); h->pkt_len -= 28; } else { /* extended short */ h->pkt_len = (opcode & 3) << 16 | get_unsigned_pair(fp); h->chrcode = get_unsigned_byte(fp); h->wd = get_unsigned_triple(fp); h->dx = get_unsigned_pair(fp) << 16; h->dy = 0; h->bm_wd = get_unsigned_pair(fp); h->bm_ht = get_unsigned_pair(fp); h->bm_hoff = get_signed_pair(fp); h->bm_voff = get_signed_pair(fp); h->pkt_len -= 13; } h->dyn_f = opcode / 16; h->run_color = (opcode & 8) ? 1 : 0; if ((uint32_t)h->chrcode > 0xff) { WARN("Unable to handle long characters in PK files: code=0x%04x", h->chrcode); return -1; } return 0; } /* CCITT Group 4 filter may reduce file size. */ static pdf_obj * create_pk_CharProc_stream (struct pk_header_ *pkh, double chrwid, unsigned char *pkt_ptr, uint32_t pkt_len) { pdf_obj *stream; /* charproc */ int32_t llx, lly, urx, ury; int len; llx = -pkh->bm_hoff; lly = pkh->bm_voff - pkh->bm_ht; urx = pkh->bm_wd - pkh->bm_hoff; ury = pkh->bm_voff; stream = pdf_new_stream(STREAM_COMPRESS); /* * The following line is a "metric" for the PDF reader: * * PDF Reference Reference, 4th ed., p.385. * * The wx (first operand of d1) must be consistent with the corresponding * width in the font's Widths array. The format string of sprint() must be * consistent with write_number() in pdfobj.c. */ len = pdf_sprint_number(work_buffer, chrwid); len += sprintf (work_buffer + len, " 0 %d %d %d %d d1\n", llx, lly, urx, ury); pdf_add_stream(stream, work_buffer, len); /* * Acrobat dislike transformation [0 0 0 0 dx dy]. * PDF Reference, 4th ed., p.147, says, * * Use of a noninvertible matrix when painting graphics objects can result in * unpredictable behavior. * * but it does not forbid use of such transformation. */ if (pkh->bm_wd != 0 && pkh->bm_ht != 0 && pkt_len > 0) { /* Scale and translate origin to lower left corner for raster data */ len = sprintf (work_buffer, "q\n%u 0 0 %u %d %d cm\n", pkh->bm_wd, pkh->bm_ht, llx, lly); pdf_add_stream(stream, work_buffer, len); len = sprintf (work_buffer, "BI\n/W %u\n/H %u\n/IM true\n/BPC 1\nID ", pkh->bm_wd, pkh->bm_ht); pdf_add_stream(stream, work_buffer, len); /* Add bitmap data */ if (pkh->dyn_f == 14) /* bitmap */ pk_decode_bitmap(stream, pkh->bm_wd, pkh->bm_ht, pkh->dyn_f, pkh->run_color, pkt_ptr, pkt_len); else pk_decode_packed(stream, pkh->bm_wd, pkh->bm_ht, pkh->dyn_f, pkh->run_color, pkt_ptr, pkt_len); len = sprintf (work_buffer, "\nEI\nQ"); pdf_add_stream(stream, work_buffer, len); } /* Otherwise we embed an empty stream :-( */ return stream; } #define PK_XXX1 240 #define PK_XXX2 241 #define PK_XXX3 242 #define PK_XXX4 243 #define PK_YYY 244 #define PK_POST 245 #define PK_NO_OP 246 #define PK_PRE 247 #define pk_char2name(b,c) sprintf((b), "x%02X", (unsigned char)(c)) int pdf_font_load_pkfont (pdf_font *font) { pdf_obj *fontdict; char *usedchars; char *ident; unsigned dpi; FILE *fp; double point_size, pix2charu; int opcode, code, firstchar, lastchar, prev; pdf_obj *charprocs, *procset, *encoding, *tmp_array; double widths[256]; pdf_rect bbox; char charavail[256]; #if ENABLE_GLYPHENC int encoding_id; char **enc_vec; #endif /* ENABLE_GLYPHENC */ int error = 0; if (!font->reference) return 0; ident = font->filename; point_size = font->point_size; usedchars = font->usedchars; #if ENABLE_GLYPHENC encoding_id = font->encoding_id; if (encoding_id < 0) enc_vec = NULL; else { enc_vec = pdf_encoding_get_encoding(encoding_id); } #endif /* ENABLE_GLYPHENC */ ASSERT(ident && usedchars && point_size > 0.0); dpi = truedpi(ident, point_size, base_dpi); fp = dpx_open_pk_font_at(ident, dpi); if (!fp) { ERROR("Could not find/open PK font file: %s (at %udpi)", ident, dpi); } memset(charavail, 0, 256); charprocs = pdf_new_dict(); /* Include bitmap as 72dpi image: * There seems to be problems in "scaled" bitmap glyph * rendering in several viewers. */ pix2charu = 72. * 1000. / ((double) base_dpi) / point_size; bbox.llx = bbox.lly = HUGE_VAL; bbox.urx = bbox.ury = -HUGE_VAL; while ((opcode = fgetc(fp)) >= 0 && opcode != PK_POST) { if (opcode < 240) { struct pk_header_ pkh; error = read_pk_char_header(&pkh, opcode, fp); if (error) ERROR("Error in reading PK character header."); else if (charavail[pkh.chrcode & 0xff]) WARN("More than two bitmap image for single glyph?: font=\"%s\" code=0x%02x", ident, pkh.chrcode); if (!usedchars[pkh.chrcode & 0xff]) skip_bytes(pkh.pkt_len, fp); else { char *charname; pdf_obj *charproc; unsigned char *pkt_ptr; size_t bytesread; double charwidth; /* Charwidth in PDF units */ charwidth = ROUND(1000.0 * pkh.wd / (((double) (1<<20))*pix2charu), 0.1); widths[pkh.chrcode & 0xff] = charwidth; /* Update font BBox info */ bbox.llx = MIN(bbox.llx, -pkh.bm_hoff); bbox.lly = MIN(bbox.lly, (double)pkh.bm_voff - (double)pkh.bm_ht); bbox.urx = MAX(bbox.urx, (double)pkh.bm_wd - (double)pkh.bm_hoff); bbox.ury = MAX(bbox.ury, pkh.bm_voff); pkt_ptr = NEW(pkh.pkt_len, unsigned char); if ((bytesread = fread(pkt_ptr, 1, pkh.pkt_len, fp))!= pkh.pkt_len) { ERROR("Only %ld bytes PK packet read. (expected %ld bytes)", bytesread, pkh.pkt_len); } charproc = create_pk_CharProc_stream(&pkh, charwidth, pkt_ptr, bytesread); RELEASE(pkt_ptr); if (!charproc) ERROR("Unpacking PK character data failed."); #if ENABLE_GLYPHENC if (encoding_id >= 0 && enc_vec) { charname = (char *) enc_vec[pkh.chrcode & 0xff]; if (!charname) { WARN("\".notdef\" glyph used in font (code=0x%02x): %s", pkh.chrcode, ident); charname = work_buffer; pk_char2name(charname, pkh.chrcode); } } else #endif /* ENABLE_GLYPHENC */ { charname = work_buffer; pk_char2name(charname, pkh.chrcode); } pdf_add_dict(charprocs, pdf_new_name(charname), pdf_ref_obj(charproc)); /* _FIXME_ */ pdf_release_obj(charproc); } charavail[pkh.chrcode & 0xff] = 1; } else { /* A command byte */ switch (opcode) { case PK_NO_OP: break; case PK_XXX1: case PK_XXX2: case PK_XXX3: case PK_XXX4: { int32_t len = get_unsigned_num(fp, opcode-PK_XXX1); if (len < 0) WARN("PK: Special with %d bytes???", len); else skip_bytes(len, fp); break; } case PK_YYY: skip_bytes(4, fp); break; case PK_PRE: do_preamble(fp); break; } } } MFCLOSE(fp); /* Check if we really got all glyphs needed. */ for (code = 0; code < 256; code++) { if (usedchars[code] && !charavail[code]) WARN("Missing glyph code=0x%02x in PK font \"%s\".", code, ident); } /* Now actually fill fontdict. */ fontdict = pdf_font_get_resource(font); pdf_add_dict(fontdict, pdf_new_name("CharProcs"), pdf_ref_obj(charprocs)); pdf_release_obj(charprocs); /* * Resources: * * PDF Reference 4th ed. describes it as "Optional but strongly recommended". * There are no reason to put it in our case, but we will put this. * We do not care about compatibility with Acrobat 2.x. (See implementation * note 47, Appendix H of PDF Ref., 4th ed.). */ procset = pdf_new_dict(); tmp_array = pdf_new_array(); pdf_add_array(tmp_array, pdf_new_name("PDF")); pdf_add_array(tmp_array, pdf_new_name("ImageB")); pdf_add_dict(procset, pdf_new_name("ProcSet"), tmp_array); pdf_add_dict(fontdict, pdf_new_name("Resources"), procset); /* Encoding */ tmp_array = pdf_new_array(); prev = -2; firstchar = 255; lastchar = 0; for (code = 0; code < 256; code++) { char *charname; if (usedchars[code]) { if (code < firstchar) firstchar = code; if (code > lastchar) lastchar = code; if (code != prev + 1) pdf_add_array(tmp_array, pdf_new_number(code)); #if ENABLE_GLYPHENC if (encoding_id >= 0 && enc_vec) { charname = (char *) enc_vec[(unsigned char) code]; if (!charname) { charname = work_buffer; pk_char2name(charname, code); } } else #endif /* ENABLE_GLYPHENC */ { charname = work_buffer; pk_char2name(charname, code); } pdf_add_array(tmp_array, pdf_new_name(charname)); prev = code; } } if (firstchar > lastchar) { ERROR("Unexpected error: firstchar > lastchar (%d %d)", firstchar, lastchar); pdf_release_obj(tmp_array); return -1; } #if ENABLE_GLYPHENC if (encoding_id < 0 || !enc_vec) { #else if (1) { #endif /* ENABLE_GLYPHENC */ encoding = pdf_new_dict(); pdf_add_dict(encoding, pdf_new_name("Type"), pdf_new_name("Encoding")); pdf_add_dict(encoding, pdf_new_name("Differences"), tmp_array); pdf_add_dict(fontdict, pdf_new_name("Encoding"), pdf_ref_obj(encoding)); pdf_release_obj(encoding); } else pdf_release_obj(tmp_array); /* FontBBox: Accurate value is important. */ tmp_array = pdf_new_array(); pdf_add_array(tmp_array, pdf_new_number(bbox.llx)); pdf_add_array(tmp_array, pdf_new_number(bbox.lly)); pdf_add_array(tmp_array, pdf_new_number(bbox.urx)); pdf_add_array(tmp_array, pdf_new_number(bbox.ury)); pdf_add_dict (fontdict , pdf_new_name("FontBBox"), tmp_array); /* Widths: * Indirect reference preffered. (See PDF Reference) */ tmp_array = pdf_new_array(); for (code = firstchar; code <= lastchar; code++) { if (usedchars[code]) pdf_add_array(tmp_array, pdf_new_number(widths[code])); else { pdf_add_array(tmp_array, pdf_new_number(0)); } } pdf_add_dict(fontdict, pdf_new_name("Widths"), pdf_ref_obj(tmp_array)); pdf_release_obj(tmp_array); /* FontMatrix */ tmp_array = pdf_new_array(); pdf_add_array(tmp_array, pdf_new_number(0.001 * pix2charu)); pdf_add_array(tmp_array, pdf_new_number(0.0)); pdf_add_array(tmp_array, pdf_new_number(0.0)); pdf_add_array(tmp_array, pdf_new_number(0.001 * pix2charu)); pdf_add_array(tmp_array, pdf_new_number(0.0)); pdf_add_array(tmp_array, pdf_new_number(0.0)); pdf_add_dict (fontdict , pdf_new_name("FontMatrix"), tmp_array); pdf_add_dict(fontdict, pdf_new_name("FirstChar"), pdf_new_number(firstchar)); pdf_add_dict(fontdict, pdf_new_name("LastChar"), pdf_new_number(lastchar)); return 0; }