/* 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 #include #include #include "system.h" #include "mem.h" #include "error.h" #include "dpxconf.h" #include "mfileio.h" #include "numbers.h" #include "pdfdoc.h" #include "pdfobj.h" #include "pdffont.h" #include "fontmap.h" #include "cmap.h" #include "pdfximage.h" #include "pdfdraw.h" #include "pdfcolor.h" #include "pdflimits.h" #include "pdfdev.h" struct dev_param { /* Text composition (direction) mode is ignored (always same * as font's writing mode) if autorotate is unset (value zero). */ int autorotate; /* * Ignore color migrated to here. This is device's capacity. * colormode 0 for ignore colors */ int colormode; }; /* * Text handling routines. */ /* Motion state: * GRAPHICS_MODE Initial state (not within BT/ET block nor in string) * TEXT_MODE Text section is started via BT operator but not * in string. * STRING_MODE In string. A string or array of strings are currently * in process. May started '[', '<' or '('. */ #define GRAPHICS_MODE 1 #define TEXT_MODE 2 #define STRING_MODE 3 /* * In PDF, vertical text positioning is always applied when current font * is vertical font. While ASCII pTeX manages current writing direction * and font's WMode separately. * * 000/101 WMODE_HH/VV h(v) font, h(v) direction. * 001 WMODE_HV -90 deg. rotated * 100 WMODE_VH +90 deg. rotated * 011 WMODE_HD +90 deg. rotated * 111 WMODE_VD 180 deg. rotated * In MetaPost PostScript file processing (mp_mode = 1), only HH/VV mode * is applied. */ #define TEXT_WMODE_HH 0 #define TEXT_WMODE_HV 1 #define TEXT_WMODE_VH 4 #define TEXT_WMODE_VV 5 #define TEXT_WMODE_HD 3 #define TEXT_WMODE_VD 7 #define ANGLE_CHANGES(m1,m2) ((abs((m1)-(m2)) % 5) == 0 ? 0 : 1) #define ROTATE_TEXT(m) ((m) != TEXT_WMODE_HH && (m) != TEXT_WMODE_VV) struct text_state { /* Current font. * This is index within dev_fonts. */ int font_id; /* Dvipdfmx does compression of text by doing text positioning * in relative motion and uses string array [(foo) -250 (bar)] * with kerning (negative kern is used for white space as suited * for TeX). This is offset within current string. */ spt_t offset; /* This is reference point of strings. * It may include error correction induced by rounding. */ spt_t ref_x; spt_t ref_y; /* Using text raise and leading is highly recommended for * text extraction to work properly. But not implemented yet. * We can't do consice output for \TeX without this. */ spt_t raise; /* unused */ spt_t leading; /* unused */ /* This is not always text matrix but rather font matrix. * We do not use horizontal scaling in PDF text state parameter * since they always apply scaling in fixed direction regardless * of writing mode. */ struct { double slant; double extend; int rotate; /* TEXT_WMODE_XX */ } matrix; /* Fake bold parameter: * If bold_param is positive, use text rendering mode * fill-then-stroke with stroking line width specified * by bold_param. */ double bold_param; /* Text composition (direction) mode. */ int dir_mode; /* internal */ /* Flag indicating text matrix to be forcibly reset. * Enabled if synthetic font features (slant, extend, etc) * are used for current font or when text rotation mode * changes. */ int force_reset; /* This information is duplicated from dev[font_id].format. * Set to 1 if font is composite (Type0) font. */ int is_mb; }; #define PDF_FONTTYPE_SIMPLE 1 #define PDF_FONTTYPE_BITMAP 2 #define PDF_FONTTYPE_COMPOSITE 3 struct dev_font { /* Needs to be big enough to hold name "Fxxx" * where xxx is number of largest font */ char short_name[16]; /* Resource name */ int used_on_this_page; char *tex_name; /* String identifier of this font */ spt_t sptsize; /* Point size */ /* Returned values from font/encoding layer: * * The font_id and enc_id is font and encoding (CMap) identifier * used in pdf_font or encoding/cmap layer. * The PDF object "resource" is an indirect reference object * pointing font resource of this font. The used_chars is somewhat * misleading, this is actually used_glyphs in CIDFont for Type0 * and is 65536/8 bytes binary data with each bits representing * whether the glyph is in-use or not. It is 256 char array for * simple font. */ int font_id; int enc_id; pdf_obj *resource; char *used_chars; /* Font format: * simple, composite or bitmap. */ int format; /* Writing mode: * Non-zero for vertical. Duplicated from CMap. */ int wmode; /* Syntetic Font: * * We use text matrix for creating extended or slanted font, * but not with font's FontMatrix since TrueType and Type0 * font don't support them. */ double extend; double slant; double bold; /* Boldness prameter */ }; /* * Unit conversion, formatting and others. */ #define TEX_ONE_HUNDRED_BP 6578176 struct dev_unit { double dvi2pts; /* multiplier for DVI unit to bpt conversion */ int min_bp_val; /* Shortest resolvable distance in the output PDF. */ int precision; /* Number of decimal digits (in fractional part) kept. */ }; #define FORMAT_BUF_SIZE 4096 struct pdf_dev { int motion_state; struct dev_param param; struct dev_unit unit; struct text_state text_state; struct dev_font *fonts; int num_dev_fonts; int max_dev_fonts; char format_buffer[FORMAT_BUF_SIZE+1]; }; /* * For a moment declare as static variable here * 2020/07/23 Most of the static variables put into single struct pdev */ static pdf_dev pdev; static pdf_dev * current_device (void) { return &pdev; } static void dev_out (pdf_dev *p, const char *str, size_t len) { pdf_doc_add_page_content(str, len); } static double pdf_dev_unit_dviunit (pdf_dev *p) { ASSERT(p); return (1.0 / p->unit.dvi2pts); } #define DEV_PRECISION_MAX 8 static uint32_t ten_pow[10] = { 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u }; static double ten_pow_inv[10] = { 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001 }; #define bpt2spt(p, b) ( (spt_t) round( (b) / (p)->unit.dvi2pts ) ) #define spt2bpt(p, s) ( (s) * (p)->unit.dvi2pts ) #define dround_at(v,p) (ROUND( (v), ten_pow_inv[(p)] )) static int p_itoa (int value, char *buf) { int sign, ndigits; char *p = buf; if (value < 0) { *p++ = '-'; value = -value; sign = 1; } else { sign = 0; } ndigits = 0; /* Generate at least one digit in reverse order */ do { p[ndigits++] = (value % 10) + '0'; value /= 10; } while (value != 0); /* Reverse the digits */ { int i; for (i = 0; i < ndigits / 2 ; i++) { char tmp = p[i]; p[i] = p[ndigits-i-1]; p[ndigits-i-1] = tmp; } } p[ndigits] = '\0'; return (sign ? ndigits + 1 : ndigits); } /* NOTE: Acrobat 5 and prior uses 16.16 fixed point representation for * real numbers. */ static int p_dtoa (double value, int prec, char *buf) { const int32_t p[10] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; double i, f; int32_t g; char *c = buf; int n; if (value < 0) { value = -value; *c++ = '-'; n = 1; } else { n = 0; } f = modf(value, &i); g = (int32_t) (f * p[prec] + 0.5); if (g == p[prec]) { g = 0; i += 1; } if (i) { int m = sprintf(c, "%.0f", i); c += m; n += m; } else if (g == 0) { *(c = buf) = '0'; n = 1; } if (g) { int j = prec; *c++ = '.'; while (j--) { c[j] = (g % 10) + '0'; g /= 10; } c += prec - 1; n += 1 + prec; while (*c == '0') { c--; n--; } } *(++c) = 0; return n; } static int dev_sprint_bp (pdf_dev *p, char *buf, spt_t value, spt_t *error) { double value_in_bp; double error_in_bp; int prec; ASSERT(p); prec = p->unit.precision; value_in_bp = spt2bpt(p, value); if (error) { error_in_bp = value_in_bp - dround_at(value_in_bp, prec); *error = bpt2spt(p, error_in_bp); } return p_dtoa(value_in_bp, prec, buf); } /* They are affected by precision (set at device initialization). */ static size_t pdf_dev_sprint_matrix (pdf_dev *p, char *buf, const pdf_tmatrix *M) { size_t len; int prec0, prec2; ASSERT(p); prec2 = MIN(p->unit.precision + 2, DEV_PRECISION_MAX); prec0 = MAX(p->unit.precision, 2); len = p_dtoa(M->a, prec2, buf); buf[len++] = ' '; len += p_dtoa(M->b, prec2, buf+len); buf[len++] = ' '; len += p_dtoa(M->c, prec2, buf+len); buf[len++] = ' '; len += p_dtoa(M->d, prec2, buf+len); buf[len++] = ' '; len += p_dtoa(M->e, prec0, buf+len); buf[len++] = ' '; len += p_dtoa(M->f, prec0, buf+len); buf[len] = '\0'; /* xxx_sprint_xxx NULL terminates strings. */ return len; } static size_t pdf_dev_sprint_rect (pdf_dev *p, char *buf, const pdf_rect *rect) { size_t len; ASSERT(p); len = p_dtoa(rect->llx, p->unit.precision, buf); buf[len++] = ' '; len += p_dtoa(rect->lly, p->unit.precision, buf+len); buf[len++] = ' '; len += p_dtoa(rect->urx, p->unit.precision, buf+len); buf[len++] = ' '; len += p_dtoa(rect->ury, p->unit.precision, buf+len); buf[len] = '\0'; /* xxx_sprint_xxx NULL terminates strings. */ return len; } static size_t pdf_dev_sprint_coord (pdf_dev *p, char *buf, const pdf_coord *c) { size_t len; ASSERT(p); len = p_dtoa(c->x, p->unit.precision, buf); buf[len++] = ' '; len += p_dtoa(c->y, p->unit.precision, buf+len); buf[len] = '\0'; /* xxx_sprint_xxx NULL terminates strings. */ return len; } static size_t pdf_dev_sprint_length (pdf_dev *p, char *buf, double value) { size_t len; ASSERT(p); len = p_dtoa(value, p->unit.precision, buf); buf[len] = '\0'; /* xxx_sprint_xxx NULL terminates strings. */ return len; } static size_t pdf_dev_sprint_number (pdf_dev *p, char *buf, double value) { size_t len; ASSERT(p); len = p_dtoa(value, DEV_PRECISION_MAX, buf); buf[len] = '\0'; /* xxx_sprint_xxx NULL terminates strings. */ return len; } /* Text handling */ #define CURRENTFONT(p) (((p)->text_state.font_id < 0) ? \ NULL : &((p)->fonts[(p)->text_state.font_id])) #define GET_FONT(p, n) (&((p)->fonts[(n)])) static void dev_set_text_matrix (pdf_dev *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate) { pdf_tmatrix tm; int len = 0; ASSERT(p); /* slant is negated for vertical font so that right-side * is always lower. */ switch (rotate) { case TEXT_WMODE_VH: /* Vertical font */ tm.a = slant ; tm.b = 1.0; tm.c = -extend; tm.d = 0.0 ; break; case TEXT_WMODE_HV: /* Horizontal font */ tm.a = 0.0; tm.b = -extend; tm.c = 1.0; tm.d = -slant ; break; case TEXT_WMODE_HH: /* Horizontal font */ tm.a = extend; tm.b = 0.0; tm.c = slant ; tm.d = 1.0; break; case TEXT_WMODE_VV: /* Vertical font */ tm.a = 1.0; tm.b = -slant; tm.c = 0.0; tm.d = extend; break; case TEXT_WMODE_HD: /* Horizontal font */ tm.a = 0.0; tm.b = extend; tm.c = -1.0; tm.d = slant ; break; case TEXT_WMODE_VD: /* Vertical font */ tm.a = -1.0; tm.b = slant; tm.c = 0.0; tm.d = -extend; break; } tm.e = xpos * p->unit.dvi2pts; tm.f = ypos * p->unit.dvi2pts; p->format_buffer[len++] = ' '; len += pdf_dev_sprint_matrix(p, p->format_buffer+len, &tm); p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'T'; p->format_buffer[len++] = 'm'; dev_out(p, p->format_buffer, len); /* op: Tm */ p->text_state.ref_x = xpos; p->text_state.ref_y = ypos; p->text_state.matrix.slant = slant; p->text_state.matrix.extend = extend; p->text_state.matrix.rotate = rotate; } /* * reset_text_state() outputs a BT and does any necessary coordinate * transformations to get ready to ship out text. */ static void reset_text_state (pdf_dev *p) { ASSERT(p); /* * We need to reset the line matrix to handle slanted fonts. */ dev_out(p, " BT", 3); /* op: BT */ /* * text_state.matrix is identity at top of page. * This sometimes write unnecessary "Tm"s when transition from * GRAPHICS_MODE to TEXT_MODE occurs. */ if (p->text_state.force_reset || p->text_state.matrix.slant != 0.0 || p->text_state.matrix.extend != 1.0 || ROTATE_TEXT(p->text_state.matrix.rotate)) { dev_set_text_matrix(p, 0, 0, p->text_state.matrix.slant, p->text_state.matrix.extend, p->text_state.matrix.rotate); } p->text_state.ref_x = 0; p->text_state.ref_y = 0; p->text_state.offset = 0; p->text_state.force_reset = 0; } static void pdf_dev_text_mode (pdf_dev *p) { ASSERT(p); switch (p->motion_state) { case TEXT_MODE: break; case STRING_MODE: dev_out(p, p->text_state.is_mb ? ">]TJ" : ")]TJ", 4); /* op: TJ */ break; case GRAPHICS_MODE: reset_text_state(p); break; } p->motion_state = TEXT_MODE; p->text_state.offset = 0; } static void pdf_dev_graphics_mode (pdf_dev *p) { switch (p->motion_state) { case GRAPHICS_MODE: break; case STRING_MODE: dev_out(p, p->text_state.is_mb ? ">]TJ" : ")]TJ", 4); /* op: TJ */ /* continue */ case TEXT_MODE: if (p->text_state.bold_param != 0.0) { /* fake-bold "2 Tr" is still active */ dev_out(p, " 0 Tr", 5); /* op: Tr */ p->text_state.bold_param = 0.0; } dev_out(p, " ET", 3); /* op: ET */ p->text_state.force_reset = 0; p->text_state.font_id = -1; break; } p->motion_state = GRAPHICS_MODE; } static void start_string (pdf_dev *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate) { spt_t delx, dely, error_delx, error_dely; spt_t desired_delx, desired_dely; int len = 0; ASSERT(p); delx = xpos - p->text_state.ref_x; dely = ypos - p->text_state.ref_y; /* * Precompensating for line transformation matrix. * * Line transformation matrix L for horizontal font in horizontal * mode and it's inverse I is * * | e 0| | 1/e 0| * L_hh = | | , I_hh = | | * | s 1| |-s/e 1| * * For vertical font in vertical mode, * * | 1 -s| | 1 s/e| * L_vv = | | , I_vv = | | * | 0 e| | 0 1/e| * * For vertical font in horizontal mode, * * | s 1| | 0 1| * L_vh = | | = L_vv x | | * |-e 0| |-1 0| * * | 0 -1| * I_vh = | | x I_vv * | 1 0| * * For horizontal font in vertical mode, * * | 0 -e| | 0 -1| * L_hv = | | = L_hh x | | * | 1 -s| | 1 0| * * | 0 1| * I_hv = | | x I_hh * |-1 0| * */ switch (rotate) { case TEXT_WMODE_VH: /* Vertical font in horizontal mode: rot = +90 * | 0 -1/e| * d_user = d x I_vh = d x | | * | 1 s/e| */ desired_delx = dely; desired_dely = (spt_t) (-(delx - dely*slant)/extend); /* error_del is in device space * * | 0 1| * e = e_user x | | = (-e_user_y, e_user_x) * |-1 0| * * We must care about rotation here but not extend/slant... * The extend and slant actually is font matrix. */ p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_dely); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_delx); error_delx = -error_delx; break; case TEXT_WMODE_HV: /* Horizontal font in vertical mode: rot = -90 * * |-s/e 1| * d_user = d x I_hv = d x | | * |-1/e 0| */ desired_delx = (spt_t)(-(dely + delx*slant)/extend); desired_dely = delx; /* * e = (e_user_y, -e_user_x) */ p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_dely); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_delx); error_dely = -error_dely; break; case TEXT_WMODE_HH: /* Horizontal font in horizontal mode: * | 1/e 0| * d_user = d x I_hh = d x | | * |-s/e 1| */ desired_delx = (spt_t)((delx - dely*slant)/extend); desired_dely = dely; p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_delx); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_dely); break; case TEXT_WMODE_VV: /* Vertical font in vertical mode: * | 1 s/e| * d_user = d x I_vv = d x | | * | 0 1/e| */ desired_delx = delx; desired_dely = (spt_t)((dely + delx*slant)/extend); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_delx); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_dely); break; case TEXT_WMODE_HD: /* Horizontal font in down-to-up mode: rot = +90 * * | s/e -1| * d_user = d x -I_hv = d x | | * | 1/e 0| */ desired_delx = -(spt_t)(-(dely + delx*slant)/extend); desired_dely = -delx; p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_dely); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_delx); error_delx = -error_delx; error_dely = -error_dely; break; case TEXT_WMODE_VD: /* Vertical font in down-to-up mode: rot = 180 * |-1 -s/e| * d_user = d x -I_vv = d x | | * | 0 -1/e| */ desired_delx = -delx; desired_dely = -(spt_t)((dely + delx*slant)/extend); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_delx, &error_delx); p->format_buffer[len++] = ' '; len += dev_sprint_bp(p, p->format_buffer+len, desired_dely, &error_dely); error_delx = -error_delx; error_dely = -error_dely; break; } dev_out(p, p->format_buffer, len); /* op: */ /* * dvipdfm wrongly using "TD" in place of "Td". * The TD operator set leading, but we are not using T* etc. */ dev_out(p, p->text_state.is_mb ? " Td[<" : " Td[(", 5); /* op: Td */ /* Error correction */ p->text_state.ref_x = xpos - error_delx; p->text_state.ref_y = ypos - error_dely; p->text_state.offset = 0; } static void pdf_dev_string_mode (pdf_dev *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate) { ASSERT(p); switch (p->motion_state) { case STRING_MODE: break; case GRAPHICS_MODE: reset_text_state(p); /* continue */ case TEXT_MODE: if (p->text_state.force_reset) { dev_set_text_matrix(p, xpos, ypos, slant, extend, rotate); dev_out(p, p->text_state.is_mb ? "[<" : "[(", 2); /* op: */ p->text_state.force_reset = 0; } else { start_string(p, xpos, ypos, slant, extend, rotate); } break; } p->motion_state = STRING_MODE; } /* * The purpose of the following routine is to force a Tf only * when it's actually necessary. This became a problem when the * VF code was added. The VF spec says to instantiate the * first font contained in the VF file before drawing a virtual * character. However, that font may not be used for * many characters (e.g. small caps fonts). For this reason, * dev_select_font() should not force a "physical" font selection. * This routine prevents a PDF Tf font selection until there's * really a character in that font. */ static int pdf_dev_set_font (pdf_dev *p, int font_id) { struct dev_font *font; int text_rotate; double font_scale; int len; int vert_dir, vert_font; ASSERT(p); /* text_mode() must come before text_state.is_mb is changed. */ pdf_dev_text_mode(p); font = GET_FONT(p, font_id); ASSERT(font); /* Caller should check font_id. */ p->text_state.is_mb = (font->format == PDF_FONTTYPE_COMPOSITE) ? 1 : 0; vert_font = font->wmode ? 1 : 0; if (p->param.autorotate) { vert_dir = p->text_state.dir_mode; } else { vert_dir = vert_font; } text_rotate = (vert_font << 2)|vert_dir; if (font->slant != p->text_state.matrix.slant || font->extend != p->text_state.matrix.extend || ANGLE_CHANGES(text_rotate, p->text_state.matrix.rotate)) { p->text_state.force_reset = 1; } p->text_state.matrix.slant = font->slant; p->text_state.matrix.extend = font->extend; p->text_state.matrix.rotate = text_rotate; if (!font->resource) { font->resource = pdf_get_font_reference(font->font_id); font->used_chars = pdf_get_font_usedchars(font->font_id); } if (!font->used_on_this_page) { pdf_doc_add_page_resource("Font", font->short_name, pdf_link_obj(font->resource)); font->used_on_this_page = 1; } font_scale = (double) font->sptsize * p->unit.dvi2pts; len = sprintf(p->format_buffer, " /%s", font->short_name); /* space not necessary. */ p->format_buffer[len++] = ' '; len += p_dtoa(font_scale, MIN(p->unit.precision+1, DEV_PRECISION_MAX), p->format_buffer+len); p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'T'; p->format_buffer[len++] = 'f'; dev_out(p, p->format_buffer, len); /* op: Tf */ if (font->bold > 0.0 || font->bold != p->text_state.bold_param) { if (font->bold <= 0.0) len = sprintf(p->format_buffer, " 0 Tr"); else len = sprintf(p->format_buffer, " 2 Tr %.6f w", font->bold); /* _FIXME_ */ dev_out(p, p->format_buffer, len); /* op: Tr w */ } p->text_state.bold_param = font->bold; p->text_state.font_id = font_id; return 0; } /* These tmp buffers can't be removed since the pointer to this can be * used as the return value of handle_multibyte_string(): str_ptr can point these. */ static unsigned char sbuf0[FORMAT_BUF_SIZE]; static int handle_multibyte_string (struct dev_font *font, const unsigned char **str_ptr, int *str_len) { const unsigned char *p; int length; p = *str_ptr; length = *str_len; /* * Font is double-byte font. Output is assumed to be 16-bit fixed length * encoding. * TODO: A character decomposed to multiple characters. */ if (font->enc_id >= 0) { const unsigned char *inbuf; unsigned char *outbuf; int inbytesleft, outbytesleft; CMap *cmap; cmap = CMap_cache_get(font->enc_id); inbuf = p; outbuf = sbuf0; inbytesleft = length; outbytesleft = FORMAT_BUF_SIZE; CMap_decode(cmap, &inbuf, &inbytesleft, &outbuf, &outbytesleft); if (inbytesleft != 0) { WARN("CMap conversion failed. (%d bytes remains)", inbytesleft); return -1; } length = FORMAT_BUF_SIZE - outbytesleft; p = sbuf0; } *str_ptr = p; *str_len = length; return 0; } void pdf_dev_set_string (spt_t xpos, spt_t ypos, const void *instr_ptr, int instr_len, spt_t width, int font_id) { pdf_dev *p = current_device(); struct dev_font *font; const unsigned char *str_ptr; /* Pointer to the reencoded string. */ int length, i, len = 0; spt_t kern, delh, delv; spt_t text_xorigin; spt_t text_yorigin; if (font_id < 0 || font_id >= p->num_dev_fonts) { ERROR("Invalid font: %d (%d)", font_id, p->num_dev_fonts); return; } if (font_id != p->text_state.font_id) { pdf_dev_set_font(p, font_id); } font = CURRENTFONT(p); if (!font) { ERROR("Currentfont not set."); return; } text_xorigin = p->text_state.ref_x; text_yorigin = p->text_state.ref_y; str_ptr = instr_ptr; length = instr_len; if (font->format == PDF_FONTTYPE_COMPOSITE) { if (handle_multibyte_string(font, &str_ptr, &length) < 0) { ERROR("Error in converting input string..."); return; } if (font->used_chars != NULL) { for (i = 0; i < length; i += 2) { unsigned short cid = (str_ptr[i] << 8) | str_ptr[i + 1]; add_to_used_chars2(font->used_chars, cid); } } } else { if (font->used_chars != NULL) { for (i = 0; i < length; i++) font->used_chars[str_ptr[i]] = 1; } } /* * Kern is in units of character units, i.e., 1000 = 1 em. * * Positive kern means kerning (reduce excess white space). * * The following formula is of the form a*x/b where a, x, and b are signed long * integers. Since in integer arithmetic (a*x) could overflow and a*(x/b) would * not be accurate, we use floating point arithmetic rather than trying to do * this all with integer arithmetic. * * 1000.0 / (font->extend * font->sptsize) is caluculated each times... * Is accuracy really a matter? Character widths are always rounded to integer * (in 1000 units per em) but dvipdfmx does not take into account of this... */ if (p->text_state.dir_mode==0) { /* Left-to-right */ delh = text_xorigin + p->text_state.offset - xpos; delv = ypos - text_yorigin; } else if (p->text_state.dir_mode==1) { /* Top-to-bottom */ delh = ypos - text_yorigin + p->text_state.offset; delv = xpos - text_xorigin; } else { /* Bottom-to-top */ delh = ypos + text_yorigin + p->text_state.offset; delv = xpos + text_xorigin; } /* White-space more than 3em is not considered as a part of single text. * So we will break string mode in that case. * Dvipdfmx spend most of time processing strings with kern = 0 (but far * more times in font handling). * You may want to use pre-calculated value for WORD_SPACE_MAX. * More text compression may be possible by replacing kern with space char * when -kern is equal to space char width. */ #define WORD_SPACE_MAX(f) (spt_t) (3.0 * (f)->extend * (f)->sptsize) if (p->text_state.force_reset || labs(delv) > p->unit.min_bp_val || labs(delh) > WORD_SPACE_MAX(font)) { pdf_dev_text_mode(p); kern = 0; } else { kern = (spt_t) (1000.0 / font->extend * delh / font->sptsize); } /* Inaccucary introduced by rounding of character width appears within * single text block. There are point_size/1000 rounding error per character. * If you really care about accuracy, you should compensate this here too. */ if (p->motion_state != STRING_MODE) pdf_dev_string_mode(p, xpos, ypos, font->slant, font->extend, p->text_state.matrix.rotate); else if (kern != 0) { /* * Same issues as earlier. Use floating point for simplicity. * This routine needs to be fast, so we don't call sprintf() or strcpy(). */ p->text_state.offset -= (spt_t) (kern * font->extend * (font->sptsize / 1000.0)); p->format_buffer[len++] = p->text_state.is_mb ? '>' : ')'; if (font->wmode) len += p_itoa(-kern, p->format_buffer + len); else { len += p_itoa( kern, p->format_buffer + len); } p->format_buffer[len++] = p->text_state.is_mb ? '<' : '('; dev_out(p, p->format_buffer, len); /* op: */ len = 0; } if (p->text_state.is_mb) { if (FORMAT_BUF_SIZE - len < 2 * length) ERROR("Buffer overflow..."); for (i = 0; i < length; i++) { int first, second; first = (str_ptr[i] >> 4) & 0x0f; second = str_ptr[i] & 0x0f; p->format_buffer[len++] = ((first >= 10) ? first + 'W' : first + '0'); p->format_buffer[len++] = ((second >= 10) ? second + 'W' : second + '0'); } } else { len += pdfobj_escape_str(p->format_buffer + len, FORMAT_BUF_SIZE - len, str_ptr, length); } /* I think if you really care about speed, you should avoid memcopy here. */ dev_out(p, p->format_buffer, len); /* op: */ p->text_state.offset += width; } void pdf_init_device (double dvi2pts, int precision, int black_and_white) { pdf_dev *p = current_device(); p->motion_state = GRAPHICS_MODE; p->unit.dvi2pts = 0.0; p->unit.min_bp_val = 658; p->unit.precision = 2; p->param.autorotate = 1; p->param.colormode = 1; p->text_state.font_id = -1; p->text_state.offset = 0; p->text_state.matrix.slant = 0; p->text_state.matrix.extend = 0; p->text_state.matrix.rotate = 0; p->text_state.bold_param = 0; p->text_state.dir_mode = 0; p->text_state.force_reset = 0; p->text_state.is_mb = 0; if (precision < 0 || precision > DEV_PRECISION_MAX) WARN("Number of decimal digits out of range [0-%d].", DEV_PRECISION_MAX); if (precision < 0) { p->unit.precision = 0; } else if (precision > DEV_PRECISION_MAX) { p->unit.precision = DEV_PRECISION_MAX; } else { p->unit.precision = precision; } p->unit.dvi2pts = dvi2pts; p->unit.min_bp_val = (int) ROUND(1.0/(ten_pow[p->unit.precision]*dvi2pts), 1); if (p->unit.min_bp_val < 0) p->unit.min_bp_val = -p->unit.min_bp_val; p->param.colormode = (black_and_white ? 0 : 1); pdf_dev_graphics_mode(p); pdf_color_clear_stack(); pdf_dev_init_gstates(); p->num_dev_fonts = p->max_dev_fonts = 0; p->fonts = NULL; return; } void pdf_close_device (void) { pdf_dev *p = current_device(); if (p->fonts) { int i; for (i = 0; i < p->num_dev_fonts; i++) { if (p->fonts[i].tex_name) RELEASE(p->fonts[i].tex_name); if (p->fonts[i].resource) pdf_release_obj(p->fonts[i].resource); p->fonts[i].tex_name = NULL; p->fonts[i].resource = NULL; } RELEASE(p->fonts); p->fonts = NULL; } pdf_dev_clear_gstates(); } /* * BOP, EOP, and FONT section. * BOP and EOP manipulate some of the same data structures * as the font stuff. */ void pdf_dev_reset_fonts (int newpage) { pdf_dev *p = current_device(); int i; for (i = 0; i < p->num_dev_fonts; i++) { p->fonts[i].used_on_this_page = 0; } p->text_state.font_id = -1; p->text_state.matrix.slant = 0.0; p->text_state.matrix.extend = 1.0; p->text_state.matrix.rotate = TEXT_WMODE_HH; if (newpage) p->text_state.bold_param = 0.0; p->text_state.is_mb = 0; } void pdf_dev_reset_color (int force) { pdf_color *sc, *fc; pdf_color_get_current(&sc, &fc); pdf_dev_set_color(sc, 0, force); pdf_dev_set_color(fc, 0x20, force); } void pdf_dev_bop (const pdf_tmatrix *M) { pdf_dev *p = current_device(); pdf_dev_graphics_mode(p); p->text_state.force_reset = 0; pdf_dev_gsave(); pdf_dev_concat(M); pdf_dev_reset_fonts(1); pdf_dev_reset_color(0); pdf_dev_reset_xgstate(0); } void pdf_dev_eop (void) { pdf_dev *p = current_device(); int depth; pdf_dev_graphics_mode(p); depth = pdf_dev_current_depth(); if (depth != 1) { WARN("Unbalenced q/Q nesting...: %d", depth); pdf_dev_grestore_to(0); } else { pdf_dev_grestore(); } } static void print_fontmap (const char *font_name, fontmap_rec *mrec) { if (!mrec) return; MESG("\n"); MESG("fontmap: %s -> %s", font_name, mrec->font_name); if (mrec->enc_name) MESG("(%s)", mrec->enc_name); if (mrec->opt.extend != 1.0) MESG("[extend:%g]", mrec->opt.extend); if (mrec->opt.slant != 0.0) MESG("[slant:%g]", mrec->opt.slant); if (mrec->opt.bold != 0.0) MESG("[bold:%g]", mrec->opt.bold); if (mrec->opt.flags & FONTMAP_OPT_NOEMBED) MESG("[noemb]"); if (mrec->opt.mapc >= 0) MESG("[map:<%02x>]", mrec->opt.mapc); if (mrec->opt.charcoll) MESG("[csi:%s]", mrec->opt.charcoll); if (mrec->opt.index) MESG("[index:%d]", mrec->opt.index); switch (mrec->opt.style) { case FONTMAP_STYLE_BOLD: MESG("[style:bold]"); break; case FONTMAP_STYLE_ITALIC: MESG("[style:italic]"); break; case FONTMAP_STYLE_BOLDITALIC: MESG("[style:bolditalic]"); break; } MESG("\n"); } /* _FIXME_ * Font is identified with font_name and point_size as in DVI here. * However, except for PDF_FONTTYPE_BITMAP, we can share the * short_name, resource and used_chars between multiple instances * of the same font at different sizes. */ int pdf_dev_locate_font (const char *font_name, spt_t ptsize) { char *pp; pdf_dev *p = current_device(); int i; fontmap_rec *mrec; struct dev_font *font; if (!font_name) return -1; if (ptsize == 0) { ERROR("pdf_dev_locate_font() called with the zero ptsize."); return -1; } for (i = 0; i < p->num_dev_fonts; i++) { if (!strcmp(font_name, p->fonts[i].tex_name) && ptsize == p->fonts[i].sptsize) { return i; /* found a dev_font that matches the request */ } } /* * Make sure we have room for a new one, even though we may not * actually create one. */ if (p->num_dev_fonts >= p->max_dev_fonts) { p->max_dev_fonts += 16; p->fonts = RENEW(p->fonts, p->max_dev_fonts, struct dev_font); } font = &p->fonts[p->num_dev_fonts]; /* New font */ mrec = pdf_lookup_fontmap_record(font_name); /* The extension ".pfb" is not needed for type1 fonts. And the extension ".pfb" prohibits to call mktexpk with right arguments when pdftex.map is used and when type1 is not found. Thus we discard the extension ".pfb". */ if (mrec && mrec->font_name) { pp = strrchr(mrec->font_name, '.'); if (pp && strcasecmp(pp, ".pfb") == 0) *pp = '\0'; } if (dpx_conf.verbose_level > 1) print_fontmap(font_name, mrec); font->font_id = pdf_font_findresource(font_name, ptsize * p->unit.dvi2pts); if (font->font_id < 0) { font->font_id = pdf_font_load_font(font_name, ptsize * p->unit.dvi2pts, mrec); if (font->font_id < 0) return -1; } pdf_font_resource_name(font->font_id, font->short_name); font->used_on_this_page = 0; font->tex_name = NEW(strlen(font_name) + 1, char); strcpy(font->tex_name, font_name); font->sptsize = ptsize; switch (pdf_get_font_subtype(font->font_id)) { case PDF_FONT_FONTTYPE_TYPE3: font->format = PDF_FONTTYPE_BITMAP; break; case PDF_FONT_FONTTYPE_TYPE0: font->format = PDF_FONTTYPE_COMPOSITE; break; default: font->format = PDF_FONTTYPE_SIMPLE; break; } font->wmode = pdf_get_font_wmode (font->font_id); font->enc_id = pdf_get_font_encoding(font->font_id); font->resource = NULL; /* Don't ref obj until font is actually used. */ font->used_chars = NULL; font->extend = 1.0; font->slant = 0.0; font->bold = 0.0; if (mrec) { font->extend = mrec->opt.extend; font->slant = mrec->opt.slant; font->bold = mrec->opt.bold; } return p->num_dev_fonts++; } /* This does not remember current stroking width. */ static int dev_sprint_line (pdf_dev *p, char *buf, spt_t width, spt_t p0_x, spt_t p0_y, spt_t p1_x, spt_t p1_y) { int len = 0; double w; w = width * p->unit.dvi2pts; len += p_dtoa(w, MIN(p->unit.precision+1, DEV_PRECISION_MAX), buf+len); buf[len++] = ' '; buf[len++] = 'w'; buf[len++] = ' '; len += dev_sprint_bp(p, buf+len, p0_x, NULL); buf[len++] = ' '; len += dev_sprint_bp(p, buf+len, p0_y, NULL); buf[len++] = ' '; buf[len++] = 'm'; buf[len++] = ' '; len += dev_sprint_bp(p, buf+len, p1_x, NULL); buf[len++] = ' '; len += dev_sprint_bp(p, buf+len, p1_y, NULL); buf[len++] = ' '; buf[len++] = 'l'; buf[len++] = ' '; buf[len++] = 'S'; return len; } /* Not optimized. */ #define PDF_LINE_THICKNESS_MAX 5.0 void pdf_dev_set_rule (spt_t xpos, spt_t ypos, spt_t width, spt_t height) { pdf_dev *p = current_device(); int len = 0; double width_in_bp; pdf_dev_graphics_mode(p); p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'q'; p->format_buffer[len++] = ' '; /* Don't use too thick line. */ width_in_bp = ((width < height) ? width : height) * p->unit.dvi2pts; if (width_in_bp < 0.0 || /* Shouldn't happen */ width_in_bp > PDF_LINE_THICKNESS_MAX) { pdf_rect rect; rect.llx = p->unit.dvi2pts * xpos; rect.lly = p->unit.dvi2pts * ypos; rect.urx = p->unit.dvi2pts * width; rect.ury = p->unit.dvi2pts * height; len += pdf_dev_sprint_rect(p, p->format_buffer+len, &rect); p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'r'; p->format_buffer[len++] = 'e'; p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'f'; } else { if (width > height) { /* NOTE: * A line width of 0 denotes the thinnest line that can be rendered at * device resolution. See, PDF Reference Manual 4th ed., sec. 4.3.2, * "Details of Graphics State Parameters", p. 185. */ if (height < p->unit.min_bp_val) { WARN("Too thin line: height=%ld (%g bp)", height, width_in_bp); WARN("Please consider using \"-d\" option."); } len += dev_sprint_line(p, p->format_buffer+len, height, xpos, ypos + height/2, xpos + width, ypos + height/2); } else { if (width < p->unit.min_bp_val) { WARN("Too thin line: width=%ld (%g bp)", width, width_in_bp); WARN("Please consider using \"-d\" option."); } len += dev_sprint_line(p, p->format_buffer+len, width, xpos + width/2, ypos, xpos + width/2, ypos + height); } } p->format_buffer[len++] = ' '; p->format_buffer[len++] = 'Q'; dev_out(p, p->format_buffer, len); /* op: q re f Q */ } /* Rectangle in device space coordinate. */ void pdf_dev_set_rect (pdf_rect *rect, spt_t x_user, spt_t y_user, spt_t width, spt_t height, spt_t depth) { pdf_dev *p = current_device(); double dev_x, dev_y; pdf_coord p0, p1, p2, p3; double min_x, min_y, max_x, max_y; dev_x = x_user * p->unit.dvi2pts; dev_y = y_user * p->unit.dvi2pts; if (p->text_state.dir_mode) { p0.x = dev_x - p->unit.dvi2pts * depth; p0.y = dev_y - p->unit.dvi2pts * width; p1.x = dev_x + p->unit.dvi2pts * height; p1.y = p0.y; p2.x = p1.x; p2.y = dev_y; p3.x = p0.x; p3.y = p2.y; } else { p0.x = dev_x; p0.y = dev_y - p->unit.dvi2pts * depth; p1.x = dev_x + p->unit.dvi2pts * width; p1.y = p0.y; p2.x = p1.x; p2.y = dev_y + p->unit.dvi2pts * height; p3.x = p0.x; p3.y = p2.y; } pdf_dev_transform(&p0, NULL); /* currentmatrix */ pdf_dev_transform(&p1, NULL); pdf_dev_transform(&p2, NULL); pdf_dev_transform(&p3, NULL); min_x = MIN(p0.x , p1.x); min_x = MIN(min_x, p2.x); min_x = MIN(min_x, p3.x); max_x = MAX(p0.x , p1.x); max_x = MAX(max_x, p2.x); max_x = MAX(max_x, p3.x); min_y = MIN(p0.y , p1.y); min_y = MIN(min_y, p2.y); min_y = MIN(min_y, p3.y); max_y = MAX(p0.y , p1.y); max_y = MAX(max_y, p2.y); max_y = MAX(max_y, p3.y); rect->llx = min_x; rect->lly = min_y; rect->urx = max_x; rect->ury = max_y; return; } int pdf_dev_get_dirmode (void) { pdf_dev *p = current_device(); return p->text_state.dir_mode; } void pdf_dev_set_dirmode (int text_dir) { pdf_dev *p = current_device(); struct dev_font *font; int text_rotate; int vert_dir, vert_font; font = CURRENTFONT(p); vert_font = (font && font->wmode) ? 1 : 0; if (p->param.autorotate) { vert_dir = text_dir; } else { vert_dir = vert_font; } text_rotate = (vert_font << 2)|vert_dir; if (font && ANGLE_CHANGES(text_rotate, p->text_state.matrix.rotate)) { p->text_state.force_reset = 1; } p->text_state.matrix.rotate = text_rotate; p->text_state.dir_mode = text_dir; } static void dev_set_param_autorotate (pdf_dev *p, int auto_rotate) { struct dev_font *font; int text_rotate, vert_font, vert_dir; ASSERT(p); font = CURRENTFONT(p); vert_font = (font && font->wmode) ? 1 : 0; if (auto_rotate) { vert_dir = p->text_state.dir_mode; } else { vert_dir = vert_font; } text_rotate = (vert_font << 2)|vert_dir; if (ANGLE_CHANGES(text_rotate, p->text_state.matrix.rotate)) { p->text_state.force_reset = 1; } p->text_state.matrix.rotate = text_rotate; p->param.autorotate = auto_rotate; } int pdf_dev_get_param (int param_type) { pdf_dev *p = current_device(); int value = 0; switch (param_type) { case PDF_DEV_PARAM_AUTOROTATE: value = p->param.autorotate; break; case PDF_DEV_PARAM_COLORMODE: value = p->param.colormode; break; default: ERROR("Unknown device parameter: %d", param_type); } return value; } void pdf_dev_set_param (int param_type, int value) { pdf_dev *p = current_device(); switch (param_type) { case PDF_DEV_PARAM_AUTOROTATE: dev_set_param_autorotate(p, value); break; case PDF_DEV_PARAM_COLORMODE: p->param.colormode = value; /* 0 for B&W */ break; default: ERROR("Unknown device parameter: %d", param_type); } return; } /* rect returned */ int pdf_dev_put_image (int id, transform_info *ti, double ref_x, double ref_y, pdf_rect *rect) { pdf_dev *p = current_device(); char *res_name; pdf_tmatrix M, M1; pdf_rect r; int len = 0; pdf_copymatrix(&M, &(ti->matrix)); M.e += ref_x; M.f += ref_y; /* Just rotate by -90, but not tested yet. Any problem if M has scaling? */ if (p->param.autorotate && p->text_state.dir_mode) { double tmp; tmp = -M.a; M.a = M.b; M.b = tmp; tmp = -M.c; M.c = M.d; M.d = tmp; } pdf_dev_graphics_mode(p); pdf_dev_gsave(); pdf_ximage_scale_image(id, &M1, &r, ti); pdf_concatmatrix(&M, &M1); pdf_dev_concat(&M); /* Clip */ if (ti->flags & INFO_DO_CLIP) { pdf_dev_rectclip(r.llx, r.lly, r.urx - r.llx, r.ury - r.lly); } res_name = pdf_ximage_get_resname(id); { char *buf; buf = NEW(strlen(res_name)+6, char); len = sprintf(buf, " /%s Do", res_name); dev_out(p, buf, len); /* op: Do */ RELEASE(buf); } if (rect) { pdf_rect r1; /* Sorry for ugly code. */ pdf_dev_set_rect(&r1, bpt2spt(p, r.llx), bpt2spt(p, r.lly), bpt2spt(p, r.urx - r.llx), bpt2spt(p, r.ury - r.lly), 0); rect->llx = r1.llx; rect->lly = r1.lly; rect->urx = r1.urx; rect->ury = r1.ury; } pdf_dev_grestore(); pdf_doc_add_page_resource("XObject", res_name, pdf_ximage_get_reference(id)); return 0; } void transform_info_clear (transform_info *info) { /* Physical dimensions */ info->width = 0.0; info->height = 0.0; info->depth = 0.0; info->bbox.llx = 0.0; info->bbox.lly = 0.0; info->bbox.urx = 0.0; info->bbox.ury = 0.0; /* Transformation matrix */ pdf_setmatrix(&(info->matrix), 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); info->flags = 0; } void pdf_dev_begin_actualtext (uint16_t *unicodes, int count) { pdf_dev *p = current_device(); int len, i, pdf_doc_enc = 1; /* check whether we can use PDFDocEncoding for this string (we punt on the 0x80..0xA0 range that does not directly correspond to unicode) */ for (i = 0; i < count; i++) { if (unicodes[i] > 0xff || (unicodes[i] > 0x7f && unicodes[i] < 0xa1)) { pdf_doc_enc = 0; break; } } pdf_dev_graphics_mode(p); dev_out(p, "\n/Span << /ActualText (", 23); if (!pdf_doc_enc) dev_out(p, "\xFE\xFF", 2); while (count-- > 0) { unsigned char s[2] = { *unicodes >> 8, *unicodes & 0xff }; char buf[32]; i = pdf_doc_enc; /* if using PDFDocEncoding, we only care about the low 8 bits, so start with the second byte of our pair */ len = 0; for (; i < 2; i++) { unsigned char c = s[i]; if (c == '(' || c == ')' || c == '\\') { len += sprintf(buf + len, "\\%c", c); } else if (c < ' ') { len += sprintf(buf + len, "\\%03o", c); } else { len += sprintf(buf + len, "%c", c); } } dev_out(p, buf, len); ++unicodes; } dev_out(p, ") >> BDC", 8); } void pdf_dev_end_actualtext (void) { pdf_dev *p = current_device(); pdf_dev_graphics_mode(p); dev_out(p, " EMC", 4); } /* Compatibility functions.... For other files still using old interface */ void graphics_mode (void) { pdf_dev *p = current_device(); pdf_dev_graphics_mode(p); } double dev_unit_dviunit (void) { pdf_dev *p = current_device(); return pdf_dev_unit_dviunit(p); } int pdf_sprint_matrix (char *buf, const pdf_tmatrix *M) { pdf_dev *p = current_device(); return (int) pdf_dev_sprint_matrix(p, buf, M); } int pdf_sprint_rect (char *buf, const pdf_rect *rect) { pdf_dev *p = current_device(); return (int) pdf_dev_sprint_rect(p, buf, rect); } int pdf_sprint_coord (char *buf, const pdf_coord *c) { pdf_dev *p = current_device(); return (int) pdf_dev_sprint_coord(p, buf, c); } int pdf_sprint_length (char *buf, double value) { pdf_dev *p = current_device(); return (int) pdf_dev_sprint_length(p, buf, value); } int pdf_sprint_number (char *buf, double value) { pdf_dev *p = current_device(); return (int) pdf_dev_sprint_number(p, buf, value); } /* Access text state parameters. */ int pdf_dev_get_font_wmode (int font_id) { pdf_dev *p = current_device(); struct dev_font *font; font = GET_FONT(p, font_id); if (font) { return font->wmode; } return 0; } int pdf_dev_font_minbytes (int font_id) { pdf_dev *p = current_device(); struct dev_font *font; int minbytes = 1; font = GET_FONT(p, font_id); if (font && font->format == PDF_FONTTYPE_COMPOSITE) { CMap *cmap; cmap = CMap_cache_get(font->enc_id); minbytes = CMap_get_profile(cmap, CMAP_PROF_TYPE_INBYTES_MIN); } return minbytes; }