/* Copyright (C) 1993, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zchar1.c,v 1.7.2.2.2.1 2003/01/17 00:49:05 giles Exp $ */ /* Type 1 character display operator */ #include "memory_.h" #include "ghost.h" #include "oper.h" #include "gsstruct.h" #include "gxfixed.h" #include "gxmatrix.h" #include "gxdevice.h" /* for gxfont.h */ #include "gxfont.h" #include "gxfont1.h" #include "gxtype1.h" #include "gzstate.h" /* for path for gs_type1_init */ /* (should only be gsstate.h) */ #include "gspaint.h" /* for gs_fill, gs_stroke */ #include "gspath.h" #include "gsrect.h" #include "estack.h" #include "ialloc.h" #include "ichar.h" #include "ichar1.h" #include "icharout.h" #include "idict.h" #include "ifont.h" #include "igstate.h" #include "iname.h" #include "store.h" /* ---------------- Utilities ---------------- */ /* Test whether a font is a CharString font. */ private bool font_uses_charstrings(const gs_font *pfont) { return (pfont->FontType == ft_encrypted || pfont->FontType == ft_encrypted2 || pfont->FontType == ft_disk_based); } /* Initialize a Type 1 interpreter. */ private int type1_exec_init(gs_type1_state *pcis, gs_text_enum_t *penum, gs_state *pgs, gs_font_type1 *pfont1) { /* * We have to disregard penum->pis and penum->path, and render to * the current gstate and path. This is a design bug that we will * have to address someday! */ return gs_type1_interp_init(pcis, (gs_imager_state *)pgs, pgs->path, &penum->log2_scale, (penum->text.operation & TEXT_DO_ANY_CHARPATH) != 0, pfont1->PaintType, pfont1); } /* ---------------- .type1execchar ---------------- */ /* * This is the workhorse for %Type1/2BuildChar, %Type1/2BuildGlyph, * CCRun, and CID fonts. Eventually this will appear in the C API; * even now, its normal control path doesn't use any continuations. */ /* * Define the state record for this operator, which must save the metrics * separately as well as the Type 1 interpreter state. */ typedef struct gs_type1exec_state_s { gs_type1_state cis; /* must be first */ i_ctx_t *i_ctx_p; /* so push/pop can access o-stack */ double sbw[4]; int /*metrics_present */ present; gs_rect char_bbox; bool use_FontBBox_as_Metrics2; /* * The following elements are only used locally to make the stack clean * for OtherSubrs: they don't need to be declared for the garbage * collector. */ ref save_args[6]; int num_args; } gs_type1exec_state; gs_private_st_suffix_add1(st_gs_type1exec_state, gs_type1exec_state, "gs_type1exec_state", gs_type1exec_state_enum_ptrs, gs_type1exec_state_reloc_ptrs, st_gs_type1_state, i_ctx_p); /* Forward references */ private int bbox_continue(P1(i_ctx_t *)); private int nobbox_continue(P1(i_ctx_t *)); private int type1_push_OtherSubr(P4(i_ctx_t *, const gs_type1exec_state *, int (*)(P1(i_ctx_t *)), const ref *)); private int type1_call_OtherSubr(P4(i_ctx_t *, const gs_type1exec_state *, int (*)(P1(i_ctx_t *)), const ref *)); private int type1_callout_dispatch(P3(i_ctx_t *, int (*)(P1(i_ctx_t *)), int)); private int type1_continue_dispatch(P5(i_ctx_t *, gs_type1exec_state *, const ref *, ref *, int)); private int op_type1_cleanup(P1(i_ctx_t *)); private void op_type1_free(P1(i_ctx_t *)); private void type1_cis_get_metrics(P2(const gs_type1_state * pcis, double psbw[4])); private int bbox_getsbw_continue(P1(i_ctx_t *)); private int type1exec_bbox(P3(i_ctx_t *, gs_type1exec_state *, gs_font *)); private int bbox_finish_fill(P1(i_ctx_t *)); private int bbox_finish_stroke(P1(i_ctx_t *)); private int bbox_fill(P1(i_ctx_t *)); private int bbox_stroke(P1(i_ctx_t *)); private int nobbox_finish(P2(i_ctx_t *, gs_type1exec_state *)); private int nobbox_draw(P2(i_ctx_t *, int (*)(P1(gs_state *)))); private int nobbox_fill(P1(i_ctx_t *)); private int nobbox_stroke(P1(i_ctx_t *)); /* .type1execchar - */ private int ztype1execchar(i_ctx_t *i_ctx_p) { return charstring_execchar(i_ctx_p, (1 << (int)ft_encrypted) | (1 << (int)ft_disk_based)); } int charstring_execchar(i_ctx_t *i_ctx_p, int font_type_mask) { os_ptr op = osp; gs_font *pfont; int code = font_param(op - 3, &pfont); gs_font_base *const pbfont = (gs_font_base *) pfont; gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont; const gs_type1_data *pdata; gs_text_enum_t *penum = op_show_find(i_ctx_p); gs_type1exec_state cxs; gs_type1_state *const pcis = &cxs.cis; if (code < 0) return code; if (penum == 0 || pfont->FontType >= sizeof(font_type_mask) * 8 || !(font_type_mask & (1 << (int)pfont->FontType))) return_error(e_undefined); pdata = &pfont1->data; /* * Any reasonable implementation would execute something like * 1 setmiterlimit 0 setlinejoin 0 setlinecap * here, but the Adobe implementations don't. * * If this is a stroked font, set the stroke width. */ if (pfont->PaintType) gs_setlinewidth(igs, pfont->StrokeWidth); check_estack(3); /* for continuations */ /* * Execute the definition of the character. */ if (r_is_proc(op)) return zchar_exec_char_proc(i_ctx_p); /* * The definition must be a Type 1 CharString. * Note that we do not require read access: this is deliberate. */ check_type(*op, t_string); if (r_size(op) <= max(pdata->lenIV, 0)) return_error(e_invalidfont); /* * In order to make character oversampling work, we must * set up the cache before calling .type1addpath. * To do this, we must get the bounding box from the FontBBox, * and the width from the CharString or the Metrics. * If the FontBBox isn't valid, we can't do any of this. */ if ((penum->FontBBox_as_Metrics2.x == 0 && penum->FontBBox_as_Metrics2.y == 0) || gs_rootfont(igs)->WMode == 0 ) { code = zchar_get_metrics(pbfont, op - 1, cxs.sbw); if (code < 0) return code; cxs.present = code; cxs.use_FontBBox_as_Metrics2 = false; } else { /* pass here if FontType==9,11 && WMode==1*/ cxs.sbw[0] = penum->FontBBox_as_Metrics2.x / 2; cxs.sbw[1] = penum->FontBBox_as_Metrics2.y; cxs.sbw[2] = 0; cxs.sbw[3] = -penum->FontBBox_as_Metrics2.x; /* Sic! */ cxs.use_FontBBox_as_Metrics2 = true; cxs.present = metricsSideBearingAndWidth; } /* Establish a current point. */ code = gs_moveto(igs, 0.0, 0.0); if (code < 0) return code; code = type1_exec_init(pcis, penum, igs, pfont1); if (code < 0) return code; gs_type1_set_callback_data(pcis, &cxs); if (pfont1->FontBBox.q.x > pfont1->FontBBox.p.x && pfont1->FontBBox.q.y > pfont1->FontBBox.p.y ) { /* The FontBBox appears to be valid. */ cxs.char_bbox = pfont1->FontBBox; return type1exec_bbox(i_ctx_p, &cxs, pfont); } else { /* * The FontBBox is not valid. In this case, * we create the path first, then do the setcachedevice. * If we are oversampling (in this case, only for anti- * aliasing, not just to improve quality), we have to * create the path twice, since we can't know the * oversampling factor until after setcachedevice. */ const ref *opstr = op; ref other_subr; if (cxs.present == metricsSideBearingAndWidth && !cxs.use_FontBBox_as_Metrics2) { gs_point sbpt; sbpt.x = cxs.sbw[0], sbpt.y = cxs.sbw[1]; gs_type1_set_lsb(pcis, &sbpt); } /* Continue interpreting. */ icont: code = type1_continue_dispatch(i_ctx_p, &cxs, opstr, &other_subr, 4); op = osp; /* OtherSubrs might change it */ switch (code) { case 0: /* all done */ return nobbox_finish(i_ctx_p, &cxs); default: /* code < 0, error */ return code; case type1_result_callothersubr: /* unknown OtherSubr */ return type1_call_OtherSubr(i_ctx_p, &cxs, nobbox_continue, &other_subr); case type1_result_sbw: /* [h]sbw, just continue */ if (cxs.present != metricsSideBearingAndWidth) { if (!cxs.use_FontBBox_as_Metrics2) type1_cis_get_metrics(pcis, cxs.sbw); else cxs.present = metricsSideBearingAndWidth; } opstr = 0; goto icont; } } } /* -------- bbox case -------- */ /* Do all the work for the case where we have a bounding box. */ private int type1exec_bbox(i_ctx_t *i_ctx_p, gs_type1exec_state * pcxs, gs_font * pfont) { os_ptr op = osp; gs_type1_state *const pcis = &pcxs->cis; gs_font_base *const pbfont = (gs_font_base *) pfont; /* * We appear to have a valid bounding box. If we don't have Metrics for * this character, start interpreting the CharString; do the * setcachedevice as soon as we know the (side bearing and) width. */ if (pcxs->present == metricsNone) { /* Get the width from the CharString, */ /* then set the cache device. */ ref cnref; ref other_subr; int code; /* Since an OtherSubr callout might change osp, */ /* save the character name now. */ ref_assign(&cnref, op - 1); code = type1_continue_dispatch(i_ctx_p, pcxs, op, &other_subr, 4); op = osp; /* OtherSubrs might change it */ switch (code) { default: /* code < 0 or done, error */ return ((code < 0 ? code : gs_note_error(e_invalidfont))); case type1_result_callothersubr: /* unknown OtherSubr */ return type1_call_OtherSubr(i_ctx_p, pcxs, bbox_getsbw_continue, &other_subr); case type1_result_sbw: /* [h]sbw, done */ break; } type1_cis_get_metrics(pcis, pcxs->sbw); return zchar_set_cache(i_ctx_p, pbfont, &cnref, NULL, pcxs->sbw + 2, &pcxs->char_bbox, bbox_finish_fill, bbox_finish_stroke, NULL); } else { /* We have the width and bounding box: */ /* set up the cache device now. */ double w[2]; w[0] = pcxs->sbw[2], w[1] = pcxs->sbw[3]; if (pcxs->use_FontBBox_as_Metrics2) { /* In this case, we have to calculate width for WMode=0. pcxs->sbw[2, 3] is width for WMode=1. Normally, the width for WMode=0 is not used in WMode=1 rendering. However, if CDevProc is defined, the width for WMode=0 is used. Do the same as the case pcxs->present == metricsNone */ double sbw[4]; ref cnref; ref other_subr; int code; /* Since an OtherSubr callout might change osp, */ /* save the character name now. */ ref_assign(&cnref, op - 1); code = type1_continue_dispatch(i_ctx_p, pcxs, op, &other_subr, 4); op = osp; /* OtherSubrs might change it */ switch (code) { default: /* code < 0 or done, error */ return ((code < 0 ? code : gs_note_error(e_invalidfont))); case type1_result_callothersubr: /* unknown OtherSubr */ return type1_call_OtherSubr(i_ctx_p, pcxs, bbox_getsbw_continue, &other_subr); case type1_result_sbw: /* [h]sbw, done */ break; } type1_cis_get_metrics(pcis, sbw); w[0] = sbw[2], w[1] = sbw[3]; /* Now actual width is available, I can calculate much better side bearing for WMode 1 from the width. */ pcxs->sbw[0] = w[0] / 2; } return zchar_set_cache(i_ctx_p, pbfont, op - 1, (pcxs->present == metricsSideBearingAndWidth && !pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL), w, &pcxs->char_bbox, bbox_finish_fill, bbox_finish_stroke, (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL)); } } /* Continue from an OtherSubr callout while getting metrics. */ private int bbox_getsbw_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; ref other_subr; gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state); gs_type1_state *const pcis = &pcxs->cis; int code; code = type1_continue_dispatch(i_ctx_p, pcxs, NULL, &other_subr, 4); op = osp; /* in case z1_push/pop_proc was called */ switch (code) { default: /* code < 0 or done, error */ op_type1_free(i_ctx_p); return ((code < 0 ? code : gs_note_error(e_invalidfont))); case type1_result_callothersubr: /* unknown OtherSubr */ return type1_push_OtherSubr(i_ctx_p, pcxs, bbox_getsbw_continue, &other_subr); case type1_result_sbw: { /* [h]sbw, done */ double sbw[4]; const gs_font_base *const pbfont = (const gs_font_base *)pcis->pfont; gs_rect bbox; /* Get the metrics before freeing the state. */ type1_cis_get_metrics(pcis, sbw); bbox = pcxs->char_bbox; op_type1_free(i_ctx_p); return zchar_set_cache(i_ctx_p, pbfont, op, sbw, sbw + 2, &bbox, bbox_finish_fill, bbox_finish_stroke, NULL); } } } /* %bbox_{fill|stroke} - */ /* %bbox_{fill|stroke} - */ private int bbox_finish(P2(i_ctx_t *, int (*)(P1(i_ctx_t *)))); private int bbox_finish_fill(i_ctx_t *i_ctx_p) { return bbox_finish(i_ctx_p, bbox_fill); } private int bbox_finish_stroke(i_ctx_t *i_ctx_p) { return bbox_finish(i_ctx_p, bbox_stroke); } private int bbox_finish(i_ctx_t *i_ctx_p, int (*cont) (P1(i_ctx_t *))) { os_ptr op = osp; gs_font *pfont; int code; gs_text_enum_t *penum = op_show_find(i_ctx_p); gs_type1exec_state cxs; /* stack allocate to avoid sandbars */ gs_type1_state *const pcis = &cxs.cis; double sbxy[2]; gs_point sbpt; gs_point *psbpt = 0; os_ptr opc = op; const ref *opstr; ref other_subr; if (!r_has_type(opc, t_string)) { check_op(3); code = num_params(op, 2, sbxy); if (code < 0) return code; sbpt.x = sbxy[0]; sbpt.y = sbxy[1]; psbpt = &sbpt; opc -= 2; check_type(*opc, t_string); } code = font_param(opc - 3, &pfont); if (code < 0) return code; if (penum == 0 || !font_uses_charstrings(pfont)) return_error(e_undefined); { gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont; int lenIV = pfont1->data.lenIV; if (lenIV > 0 && r_size(opc) <= lenIV) return_error(e_invalidfont); check_estack(5); /* in case we need to do a callout */ code = type1_exec_init(pcis, penum, igs, pfont1); if (code < 0) return code; if (psbpt) gs_type1_set_lsb(pcis, psbpt); } opstr = opc; icont: code = type1_continue_dispatch(i_ctx_p, &cxs, opstr, &other_subr, (psbpt ? 6 : 4)); op = osp; /* OtherSubrs might have altered it */ switch (code) { case 0: /* all done */ /* Call the continuation now. */ if (psbpt) pop(2); return (*cont)(i_ctx_p); case type1_result_callothersubr: /* unknown OtherSubr */ push_op_estack(cont); /* call later */ return type1_call_OtherSubr(i_ctx_p, &cxs, bbox_continue, &other_subr); case type1_result_sbw: /* [h]sbw, just continue */ opstr = 0; goto icont; default: /* code < 0, error */ return code; } } private int bbox_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; int npop = (r_has_type(op, t_string) ? 4 : 6); int code = type1_callout_dispatch(i_ctx_p, bbox_continue, npop); if (code == 0) { op = osp; /* OtherSubrs might have altered it */ npop -= 4; /* nobbox_fill/stroke handles the rest */ pop(npop); op -= npop; op_type1_free(i_ctx_p); } return code; } /* * Check the path against FontBBox before drawing. The original operands * of type1execchar are still on the o-stack. */ private int bbox_draw(i_ctx_t *i_ctx_p, int (*draw)(P1(gs_state *))) { os_ptr op = osp; gs_rect bbox; gs_font *pfont; gs_text_enum_t *penum; gs_font_base * pbfont; gs_font_type1 * pfont1; gs_type1exec_state cxs; int code; if (igs->in_cachedevice < 2) /* not caching */ return nobbox_draw(i_ctx_p, draw); if ((code = font_param(op - 3, &pfont)) < 0) return code; penum = op_show_find(i_ctx_p); if (penum == 0 || !font_uses_charstrings(pfont)) return_error(e_undefined); if ((code = gs_pathbbox(igs, &bbox)) < 0) { /* * If the matrix is singular, all user coordinates map onto a * straight line. Don't bother rendering the character at all. */ if (code == e_undefinedresult) { pop(4); gs_newpath(igs); return 0; } return code; } if (draw == gs_stroke) { /* Expand the bounding box by the line width. */ float width = gs_currentlinewidth(igs) * 1.41422; bbox.p.x -= width, bbox.p.y -= width; bbox.q.x += width, bbox.q.y += width; } pbfont = (gs_font_base *)pfont; if (rect_within(bbox, pbfont->FontBBox)) /* within bounds */ return nobbox_draw(i_ctx_p, draw); /* Enlarge the FontBBox to save work in the future. */ rect_merge(pbfont->FontBBox, bbox); /* Dismantle everything we've done, and start over. */ gs_text_retry(penum); pfont1 = (gs_font_type1 *) pfont; if ((penum->FontBBox_as_Metrics2.x == 0 && penum->FontBBox_as_Metrics2.y == 0) || gs_rootfont(igs)->WMode == 0 ) { code = zchar_get_metrics(pbfont, op - 1, cxs.sbw); if (code < 0) return code; cxs.present = code; cxs.use_FontBBox_as_Metrics2 = false; } else { cxs.sbw[0] = penum->FontBBox_as_Metrics2.x / 2; cxs.sbw[1] = penum->FontBBox_as_Metrics2.y; cxs.sbw[2] = 0; cxs.sbw[3] = -penum->FontBBox_as_Metrics2.x; /* Sic! */ cxs.use_FontBBox_as_Metrics2 = true; cxs.present = metricsSideBearingAndWidth; } code = type1_exec_init(&cxs.cis, penum, igs, pfont1); if (code < 0) return code; cxs.char_bbox = bbox; return type1exec_bbox(i_ctx_p, &cxs, pfont); } private int bbox_fill(i_ctx_t *i_ctx_p) { /* See nobbox_fill for why we use eofill here. */ return bbox_draw(i_ctx_p, gs_eofill); } private int bbox_stroke(i_ctx_t *i_ctx_p) { return bbox_draw(i_ctx_p, gs_stroke); } /* -------- Common code -------- */ /* Get the metrics (l.s.b. and width) from the Type 1 interpreter. */ private void type1_cis_get_metrics(const gs_type1_state * pcis, double psbw[4]) { psbw[0] = fixed2float(pcis->lsb.x); psbw[1] = fixed2float(pcis->lsb.y); psbw[2] = fixed2float(pcis->width.x); psbw[3] = fixed2float(pcis->width.y); } /* Handle the results of interpreting the CharString. */ /* pcref points to a t_string ref. */ private int type1_continue_dispatch(i_ctx_t *i_ctx_p, gs_type1exec_state *pcxs, const ref * pcref, ref *pos, int num_args) { int value; int code; gs_const_string charstring; gs_const_string *pchars; if (pcref == 0) { pchars = 0; } else { charstring.data = pcref->value.const_bytes; charstring.size = r_size(pcref); pchars = &charstring; } /* * Since OtherSubrs may push or pop values on the PostScript operand * stack, remove the arguments of .type1execchar before calling the * Type 1 interpreter, and put them back afterwards unless we're * about to execute an OtherSubr procedure. Also, we must set up * the callback data for pushing OtherSubrs arguments. */ pcxs->i_ctx_p = i_ctx_p; pcxs->num_args = num_args; memcpy(pcxs->save_args, osp - (num_args - 1), num_args * sizeof(ref)); osp -= num_args; gs_type1_set_callback_data(&pcxs->cis, pcxs); code = pcxs->cis.pfont->data.interpret(&pcxs->cis, pchars, &value); switch (code) { case type1_result_callothersubr: { /* * The Type 1 interpreter handles all known OtherSubrs, * so this must be an unknown one. */ const font_data *pfdata = pfont_data(gs_currentfont(igs)); code = array_get(&pfdata->u.type1.OtherSubrs, (long)value, pos); if (code >= 0) return type1_result_callothersubr; } } /* Put back the arguments removed above. */ memcpy(osp + 1, pcxs->save_args, num_args * sizeof(ref)); osp += num_args; return code; } /* * Push a continuation, the arguments removed for the OtherSubr, and * the OtherSubr procedure. */ private int type1_push_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state *pcxs, int (*cont)(P1(i_ctx_t *)), const ref *pos) { int i, n = pcxs->num_args; push_op_estack(cont); /* * Push the saved arguments (in reverse order, so they will get put * back on the operand stack in the correct order) on the e-stack. */ for (i = n; --i >= 0; ) { *++esp = pcxs->save_args[i]; r_clear_attrs(esp, a_executable); /* just in case */ } ++esp; *esp = *pos; return o_push_estack; } /* * Do a callout to an OtherSubr implemented in PostScript. * The caller must have done a check_estack(4 + num_args). */ private int type1_call_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state * pcxs, int (*cont) (P1(i_ctx_t *)), const ref * pos) { /* Move the Type 1 interpreter state to the heap. */ gs_type1exec_state *hpcxs = ialloc_struct(gs_type1exec_state, &st_gs_type1exec_state, "type1_call_OtherSubr"); if (hpcxs == 0) return_error(e_VMerror); *hpcxs = *pcxs; gs_type1_set_callback_data(&hpcxs->cis, hpcxs); push_mark_estack(es_show, op_type1_cleanup); ++esp; make_istruct(esp, 0, hpcxs); return type1_push_OtherSubr(i_ctx_p, pcxs, cont, pos); } /* Continue from an OtherSubr callout while building the path. */ private int type1_callout_dispatch(i_ctx_t *i_ctx_p, int (*cont)(P1(i_ctx_t *)), int num_args) { ref other_subr; gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state); int code; icont: code = type1_continue_dispatch(i_ctx_p, pcxs, NULL, &other_subr, num_args); switch (code) { case 0: /* callout done, cont is on e-stack */ return 0; default: /* code < 0 or done, error */ op_type1_free(i_ctx_p); return ((code < 0 ? code : gs_note_error(e_invalidfont))); case type1_result_callothersubr: /* unknown OtherSubr */ return type1_push_OtherSubr(i_ctx_p, pcxs, cont, &other_subr); case type1_result_sbw: /* [h]sbw, just continue */ goto icont; } } /* Clean up after a Type 1 callout. */ private int op_type1_cleanup(i_ctx_t *i_ctx_p) { ifree_object(r_ptr(esp + 2, void), "op_type1_cleanup"); return 0; } private void op_type1_free(i_ctx_t *i_ctx_p) { ifree_object(r_ptr(esp, void), "op_type1_free"); /* * In order to avoid popping from the e-stack and then pushing onto * it, which would violate an interpreter invariant, we simply * overwrite the two e-stack items being discarded (hpcxs and the * cleanup operator) with empty procedures. */ make_empty_const_array(esp - 1, a_readonly + a_executable); make_empty_const_array(esp, a_readonly + a_executable); } /* -------- no-bbox case -------- */ private int nobbox_continue(i_ctx_t *i_ctx_p) { int code = type1_callout_dispatch(i_ctx_p, nobbox_continue, 4); if (code) return code; { gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state); gs_type1exec_state cxs; cxs = *pcxs; gs_type1_set_callback_data(&cxs.cis, &cxs); op_type1_free(i_ctx_p); return nobbox_finish(i_ctx_p, &cxs); } } /* Finish the no-FontBBox case after constructing the path. */ /* If we are oversampling for anti-aliasing, we have to go around again. */ /* %nobbox_continue - */ private int nobbox_finish(i_ctx_t *i_ctx_p, gs_type1exec_state * pcxs) { os_ptr op = osp; int code; gs_text_enum_t *penum = op_show_find(i_ctx_p); gs_font *pfont; double w[2]; if ((code = gs_pathbbox(igs, &pcxs->char_bbox)) < 0 || (code = font_param(op - 3, &pfont)) < 0 ) return code; if (penum == 0 || !font_uses_charstrings(pfont)) return_error(e_undefined); { gs_font_base *const pbfont = (gs_font_base *) pfont; gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont; if (pcxs->present == metricsNone || pcxs->use_FontBBox_as_Metrics2) { gs_point endpt; if ((code = gs_currentpoint(igs, &endpt)) < 0) return code; /* We will not use sbw[3, 4]. If pcxs->use_FontBBox_as_Metrics2 is true, sbw[3, 4] are used as arguments(W1x, W1y) of setcachedevice2. We will use w[0, 1] as W0x and W0y of setcachedevice2. W0 and W1 is differrent parameters. Don't confuse. */ w[0] = endpt.x, w[1] = endpt.y; pcxs->present = metricsSideBearingAndWidth; if (pcxs->use_FontBBox_as_Metrics2) { /* Now actual width is available, I can calculate much better side bearing for WMode 1 from the width. */ pcxs->sbw[0] = w[0] / 2; } } /* * We only need to rebuild the path from scratch if we might * oversample for anti-aliasing. */ if ((*dev_proc(igs->device, get_alpha_bits))(igs->device, go_text) > 1 ) { gs_newpath(igs); gs_moveto(igs, 0.0, 0.0); code = type1_exec_init(&pcxs->cis, penum, igs, pfont1); if (code < 0) return code; return type1exec_bbox(i_ctx_p, pcxs, pfont); } return zchar_set_cache(i_ctx_p, pbfont, op, NULL, w, &pcxs->char_bbox, nobbox_fill, nobbox_stroke, (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL)); } } /* Finish by popping the operands and filling or stroking. */ private int nobbox_draw(i_ctx_t *i_ctx_p, int (*draw)(P1(gs_state *))) { int code = draw(igs); if (code >= 0) pop(4); return code; } private int nobbox_fill(i_ctx_t *i_ctx_p) { /* * Properly designed fonts, which have no self-intersecting outlines * and in which outer and inner outlines are drawn in opposite * directions, aren't affected by choice of filling rule; but some * badly designed fonts in the Genoa test suite seem to require * using the even-odd rule to match Adobe interpreters. */ return nobbox_draw(i_ctx_p, gs_eofill); } private int nobbox_stroke(i_ctx_t *i_ctx_p) { return nobbox_draw(i_ctx_p, gs_stroke); } /* ------ Initialization procedure ------ */ const op_def zchar1_op_defs[] = { {"4.type1execchar", ztype1execchar}, /* Internal operators */ {"4%bbox_getsbw_continue", bbox_getsbw_continue}, {"4%bbox_continue", bbox_continue}, {"4%bbox_finish_fill", bbox_finish_fill}, {"4%bbox_finish_stroke", bbox_finish_stroke}, {"4%nobbox_continue", nobbox_continue}, {"4%nobbox_fill", nobbox_fill}, {"4%nobbox_stroke", nobbox_stroke}, op_def_end(0) }; /* ------ Auxiliary procedures for type 1 fonts ------ */ private int z1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph, gs_const_string * pstr) { ref gref; glyph_ref(glyph, &gref); return zchar_charstring_data((gs_font *)pfont, &gref, pstr); } private int z1_subr_data(gs_font_type1 * pfont, int index, bool global, gs_const_string * pstr) { const font_data *pfdata = pfont_data(pfont); ref subr; int code; code = array_get((global ? &pfdata->u.type1.GlobalSubrs : &pfdata->u.type1.Subrs), index, &subr); if (code < 0) return code; check_type_only(subr, t_string); pstr->data = subr.value.const_bytes; pstr->size = r_size(&subr); return 0; } private int z1_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph, gs_const_string *pstr) { ref std_glyph; int code = array_get(&StandardEncoding, (long)ccode, &std_glyph); if (code < 0) return code; if (pglyph) { switch (r_type(&std_glyph)) { case t_name: *pglyph = name_index(&std_glyph); break; case t_integer: *pglyph = gs_min_cid_glyph + std_glyph.value.intval; if (*pglyph < gs_min_cid_glyph || *pglyph > gs_max_glyph) *pglyph = gs_no_glyph; break; default: return_error(e_typecheck); } } if (pstr) code = zchar_charstring_data((gs_font *)pfont, &std_glyph, pstr); return code; } private int z1_push(void *callback_data, const fixed * pf, int count) { gs_type1exec_state *pcxs = callback_data; i_ctx_t *i_ctx_p = pcxs->i_ctx_p; const fixed *p = pf + count - 1; int i; check_ostack(count); for (i = 0; i < count; i++, p--) { osp++; make_real(osp, fixed2float(*p)); } return 0; } private int z1_pop(void *callback_data, fixed * pf) { gs_type1exec_state *pcxs = callback_data; i_ctx_t *i_ctx_p = pcxs->i_ctx_p; double val; int code = real_param(osp, &val); if (code < 0) return code; *pf = float2fixed(val); osp--; return 0; } /* Define the Type 1 procedure vector. */ const gs_type1_data_procs_t z1_data_procs = { z1_glyph_data, z1_subr_data, z1_seac_data, z1_push, z1_pop }; /* ------ Font procedures for Type 1 fonts ------ */ /* * Get a Type 1 or Type 2 glyph outline. This is the glyph_outline * procedure for the font. */ int zchar1_glyph_outline(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, gx_path *ppath) { gs_font_type1 *const pfont1 = (gs_font_type1 *)font; ref gref; gs_const_string charstring; int code; glyph_ref(glyph, &gref); code = zchar_charstring_data(font, &gref, &charstring); if (code < 0) return code; return zcharstring_outline(pfont1, &gref, &charstring, pmat, ppath); } /* * Get a glyph outline given a CharString. The glyph_outline procedure * for CIDFontType 0 fonts uses this. */ int zcharstring_outline(gs_font_type1 *pfont1, const ref *pgref, const gs_const_string *pgstr, const gs_matrix *pmat, gx_path *ppath) { const gs_const_string *pchars = pgstr; int code; gs_type1exec_state cxs; gs_type1_state *const pcis = &cxs.cis; static const gs_log2_scale_point no_scale = {0, 0}; const gs_type1_data *pdata; const ref *pfdict; ref *pcdevproc; int value; gs_imager_state gis; double sbw[4]; gs_point mpt; pdata = &pfont1->data; if (pgstr->size <= max(pdata->lenIV, 0)) return_error(e_invalidfont); pfdict = &pfont_data(pfont1)->dict; if (dict_find_string(pfdict, "CDevProc", &pcdevproc) > 0) return_error(e_rangecheck); /* can't call CDevProc from here */ switch (pfont1->WMode) { default: code = zchar_get_metrics2((gs_font_base *)pfont1, pgref, sbw); if (code) break; /* falls through */ case 0: code = zchar_get_metrics((gs_font_base *)pfont1, pgref, sbw); } if (code < 0) return code; cxs.present = code; /* Initialize just enough of the imager state. */ if (pmat) gs_matrix_fixed_from_matrix(&gis.ctm, pmat); else { gs_matrix imat; gs_make_identity(&imat); gs_matrix_fixed_from_matrix(&gis.ctm, &imat); } gis.flatness = 0; code = gs_type1_interp_init(&cxs.cis, &gis, ppath, &no_scale, true, 0, pfont1); if (code < 0) return code; cxs.cis.charpath_flag = true; /* suppress hinting */ gs_type1_set_callback_data(pcis, &cxs); switch (cxs.present) { case metricsSideBearingAndWidth: mpt.x = sbw[0], mpt.y = sbw[1]; gs_type1_set_lsb(pcis, &mpt); /* falls through */ case metricsWidthOnly: mpt.x = sbw[2], mpt.y = sbw[3]; gs_type1_set_width(pcis, &mpt); case metricsNone: ; } /* Continue interpreting. */ icont: code = pfont1->data.interpret(pcis, pchars, &value); switch (code) { case 0: /* all done */ /* falls through */ default: /* code < 0, error */ return code; case type1_result_callothersubr: /* unknown OtherSubr */ return_error(e_rangecheck); /* can't handle it */ case type1_result_sbw: /* [h]sbw, just continue */ type1_cis_get_metrics(pcis, cxs.sbw); pchars = 0; goto icont; } } /* * Redefine glyph_info to take Metrics[2] and CDevProc into account. * (If CDevProc is present, return e_rangecheck, since we can't call the * interpreter from here.) */ int z1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, int members, gs_glyph_info_t *info) { ref gref; ref *pcdevproc; gs_font_type1 *const pfont = (gs_font_type1 *)font; gs_font_base *const pbfont = (gs_font_base *)font; int wmode = pfont->WMode; const ref *pfdict = &pfont_data(pbfont)->dict; double sbw[4]; int width_members = members & (GLYPH_INFO_WIDTH0 << wmode); int default_members = members - width_members; int done_members = 0; int code; if (!width_members) return gs_type1_glyph_info(font, glyph, pmat, members, info); if (dict_find_string(pfdict, "CDevProc", &pcdevproc) > 0) return_error(e_rangecheck); /* can't handle it */ glyph_ref(glyph, &gref); if (width_members == GLYPH_INFO_WIDTH1) { code = zchar_get_metrics2(pbfont, &gref, sbw); if (code > 0) { info->width[1].x = sbw[2]; info->width[1].y = sbw[3]; done_members = width_members; width_members = 0; } } if (width_members) { code = zchar_get_metrics(pbfont, &gref, sbw); if (code > 0) { info->width[wmode].x = sbw[2]; info->width[wmode].y = sbw[3]; done_members = width_members; width_members = 0; } } default_members |= width_members; if (default_members) { code = gs_type1_glyph_info(font, glyph, pmat, default_members, info); if (code < 0) return code; } else info->members = 0; info->members |= done_members; return 0; }