diff options
Diffstat (limited to 'gs/psi/zchar1.c')
-rw-r--r-- | gs/psi/zchar1.c | 1304 |
1 files changed, 1304 insertions, 0 deletions
diff --git a/gs/psi/zchar1.c b/gs/psi/zchar1.c new file mode 100644 index 000000000..858cfc7f3 --- /dev/null +++ b/gs/psi/zchar1.c @@ -0,0 +1,1304 @@ +/* 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$ */ +/* 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 "gxfcid.h" +#include "gxchar.h" +#include "gzstate.h" /* for path for gs_type1_init */ + /* (should only be gsstate.h) */ +#include "gscencs.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 "iutil.h" +#include "store.h" + +/* + * 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. + * + * Properly designed fonts will render correctly with: eofill + * (required for Adobe CPSI compliant behavior + */ +/* + * On April 4, 2002, we received bug report #539359 + * which we interpret as some Genoa test are now obsolete, + * so we need to drop the bad font tolerance feature + * explained above. This temporary patch changes + * the even-odd rule back to non-zero rule. + * This patch to be kept until we accumulate + * enough information from regression testing and + * from user responses. + */ + +/* ********************************************************************* + * Make this dynamic via a global (somewhat better than a COMPILE option + ***********************************************************************/ +#define GS_CHAR_FILL gs_fill + +/* ---------------- Utilities ---------------- */ + +/* Test whether a font is a CharString font. */ +static 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. */ +static 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! + */ + + int alpha_bits = 1; + gs_log2_scale_point log2_subpixels; + + if (color_is_pure(pgs->dev_color)) /* Keep consistency with alpha_buffer_bits() */ + alpha_bits = (*dev_proc(pgs->device, get_alpha_bits)) (pgs->device, go_text); + if (alpha_bits <= 1) { + /* We render to cache device or the target device has no alpha bits. */ + log2_subpixels = penum->log2_scale; + } else { + /* We'll render to target device through alpha buffer. */ + /* Keep consistency with alpha_buffer_init() */ + log2_subpixels.x = log2_subpixels.y = ilog2(alpha_bits); + } + return gs_type1_interp_init(pcis, (gs_imager_state *)pgs, pgs->path, + &penum->log2_scale, &log2_subpixels, + (penum->text.operation & TEXT_DO_ANY_CHARPATH) != 0 || + penum->device_disabled_grid_fitting, + 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; + bool AlignToPixels; +} 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 */ +static int bbox_continue(i_ctx_t *); +static int nobbox_continue(i_ctx_t *); +static int type1_push_OtherSubr(i_ctx_t *, const gs_type1exec_state *, + int (*)(i_ctx_t *), const ref *); +static int type1_call_OtherSubr(i_ctx_t *, const gs_type1exec_state *, + int (*)(i_ctx_t *), const ref *); +static int type1_callout_dispatch(i_ctx_t *, int (*)(i_ctx_t *), int); +static int type1_continue_dispatch(i_ctx_t *, gs_type1exec_state *, + const ref *, ref *, int); +static int op_type1_cleanup(i_ctx_t *); +static void op_type1_free(i_ctx_t *); +static int bbox_getsbw_continue(i_ctx_t *); +static int type1exec_bbox(i_ctx_t *, gs_text_enum_t *, gs_type1exec_state *, gs_font *, op_proc_t *exec_cont); +static int bbox_finish_fill(i_ctx_t *); +static int bbox_finish_stroke(i_ctx_t *); +static int bbox_fill(i_ctx_t *); +static int bbox_stroke(i_ctx_t *); +static int nobbox_finish(i_ctx_t *, gs_type1exec_state *); +static int nobbox_draw(i_ctx_t *, int (*)(gs_state *)); +static int nobbox_fill(i_ctx_t *); +static int nobbox_stroke(i_ctx_t *); + +/* <font> <code|name> <name> <charstring> .type1execchar - */ +static int +ztype1execchar(i_ctx_t *i_ctx_p) +{ + return charstring_execchar(i_ctx_p, (1 << (int)ft_encrypted) | + (1 << (int)ft_disk_based)); +} +static int +charstring_execchar_aux(i_ctx_t *i_ctx_p, gs_text_enum_t *penum, gs_font *pfont) +{ + os_ptr op = osp; + gs_font_base *const pbfont = (gs_font_base *) pfont; + gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont; + const gs_type1_data *pdata; + gs_type1exec_state cxs; + gs_type1_state *const pcis = &cxs.cis; + gs_rect FontBBox = pfont1->FontBBox; + int code; + + if (penum->current_font->FontType == ft_CID_encrypted) { + if (FontBBox.q.x <= FontBBox.p.x && FontBBox.q.y <= FontBBox.p.y) { + gs_font_cid0 *pfcid0 = (gs_font_cid0 *)penum->current_font; + + FontBBox = pfcid0->FontBBox; + } + } + + 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 = metricsNone; + } + /* 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 (FontBBox.q.x > FontBBox.p.x && + FontBBox.q.y > FontBBox.p.y + ) { + /* The FontBBox appears to be valid. */ + op_proc_t exec_cont = 0; + + cxs.char_bbox = pfont1->FontBBox; + code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; + } else { + /* The FontBBox is not valid */ + const ref *opstr = op; + ref other_subr; + const gs_matrix * pctm = &ctm_only(igs); + + /* First, check for singular CTM */ + if (pctm->xx * pctm->yy == pctm->xy * pctm->yx) { + /* The code below won't be able to find the FontBBox but we + * don't need it anyway. Set an empty box and consider it valid. + */ + op_proc_t exec_cont = 0; + + cxs.char_bbox.p.x = 0; + cxs.char_bbox.p.y = 0; + cxs.char_bbox.q.x = 0; + cxs.char_bbox.q.y = 0; + code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; + } + /* Now 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. + */ + switch (cxs.present) { + case metricsSideBearingAndWidth: { + gs_point pt; + + pt.x = cxs.sbw[0], pt.y = cxs.sbw[1]; + gs_type1_set_lsb(pcis, &pt); + } + /* fall through */ + case metricsWidthOnly: { + gs_point pt; + + pt.x = cxs.sbw[2], pt.y = cxs.sbw[3]; + gs_type1_set_width(pcis, &pt); + } + } + + /* 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 */ + switch (cxs.present) { + case metricsNone: + cxs.sbw[0] = fixed2float(pcis->lsb.x); + cxs.sbw[1] = fixed2float(pcis->lsb.y); + /* fall through */ + case metricsWidthOnly: + cxs.sbw[2] = fixed2float(pcis->width.x); + cxs.sbw[3] = fixed2float(pcis->width.y); + } + opstr = 0; + goto icont; + } + } +} + +int +charstring_execchar(i_ctx_t *i_ctx_p, int font_type_mask) +{ + gs_text_enum_t *penum = op_show_find(i_ctx_p); + gs_font *pfont; + os_ptr op = osp; + int code = font_param(op - 3, &pfont); + + 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); + code = charstring_execchar_aux(i_ctx_p, penum, pfont); + if (code < 0 && igs->in_cachedevice == CACHE_DEVICE_CACHING) { + /* Perform the cache cleanup, when the cached character data + has been allocated (gx_alloc_char_bits) but + the character has not been added to the cache (gx_add_cached_char) + due to a falure in the character renderer. + */ + gs_show_enum *const penum_s = (gs_show_enum *)penum; + + if (penum_s->cc != NULL) { + gx_free_cached_char(pfont->dir, penum_s->cc); + penum_s->cc = NULL; + } + } + return code; +} + +/* -------- bbox case -------- */ + +/* Do all the work for the case where we have a bounding box. */ +/* Returns exec_cont - a function, which must be called by caller after this function. */ +static int +type1exec_bbox(i_ctx_t *i_ctx_p, gs_text_enum_t *penum, gs_type1exec_state * pcxs, + gs_font * pfont, op_proc_t *exec_cont) +{ + os_ptr op = osp; + gs_type1_state *const pcis = &pcxs->cis; + gs_font_base *const pbfont = (gs_font_base *) pfont; + op_proc_t cont = (pbfont->PaintType == 0 && penum->orig_font->PaintType == 0 + ? bbox_finish_fill : bbox_finish_stroke); + ref *pcdevproc; + + + /* + * 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 && !pcxs->use_FontBBox_as_Metrics2) || + (penum->orig_font->WMode && zchar_get_CDevProc(pbfont, &pcdevproc))) { + /* Get the width from the CharString, + * then set the cache device. */ + /* We pass here when WMode==1 and the font has CDevProc, + * because we do need sbw as CDevProc's argument. + * A more natural way would be not setting pcxs->use_FontBBox_as_Metrics2 + * when the font has CDevProc, except for missing sbw in the glyph. + * We prefer to pass here because we've got examples + * of Tyoe 1 fonts with empty glyphs, i.e. with no sbw, + * so we don't want to assume that they'll never appear in a CID font. + * In that case penum->FontBBox_as_Metrics2 will go here to zchar_set_cache. */ + 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, + cont, exec_cont, NULL); + } else { + /* We have the width and bounding box: */ + /* set up the cache device now. */ + return zchar_set_cache(i_ctx_p, pbfont, op - 1, + (pcxs->present == metricsSideBearingAndWidth + && !pcxs->use_FontBBox_as_Metrics2 ? + pcxs->sbw : NULL), + pcxs->sbw + 2, + &pcxs->char_bbox, + cont, exec_cont, + (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL)); + } +} + +/* Continue from an OtherSubr callout while getting metrics. */ +static 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; + op_proc_t cont = (pbfont->PaintType == 0 ? bbox_finish_fill : bbox_finish_stroke), exec_cont = 0; + + /* Get the metrics before freeing the state. */ + type1_cis_get_metrics(pcis, sbw); + bbox = pcxs->char_bbox; + op_type1_free(i_ctx_p); + code = zchar_set_cache(i_ctx_p, pbfont, op - 1, sbw, sbw + 2, &bbox, + cont, &exec_cont, NULL); + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; + } + } +} + +/* <font> <code|name> <name> <charstring> <sbx> <sby> %bbox_{fill|stroke} - */ +/* <font> <code|name> <name> <charstring> %bbox_{fill|stroke} - */ +static int bbox_finish(i_ctx_t *i_ctx_p, op_proc_t cont, op_proc_t *exec_cont); +static int +bbox_finish_fill(i_ctx_t *i_ctx_p) +{ + op_proc_t exec_cont = 0; + int code; + + code = bbox_finish(i_ctx_p, bbox_fill, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = exec_cont(i_ctx_p); + return code; +} +static int +bbox_finish_stroke(i_ctx_t *i_ctx_p) +{ + op_proc_t exec_cont = 0; + int code; + + code = bbox_finish(i_ctx_p, bbox_stroke, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = exec_cont(i_ctx_p); + return code; +} + +static int +bbox_finish(i_ctx_t *i_ctx_p, op_proc_t cont, op_proc_t *exec_cont) +{ /* Returns exec_cont - a function, which must be called by caller after this function. */ + 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); + *exec_cont = cont; + return 0; + 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; + } +} + +static 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. + * Returns exec_cont - a function, which must be called by caller after this function. + */ +static int +bbox_draw(i_ctx_t *i_ctx_p, int (*draw)(gs_state *), op_proc_t *exec_cont) +{ + 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 = pfont1->FontBBox; + code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, exec_cont); + return code; +} +static int +bbox_fill(i_ctx_t *i_ctx_p) +{ + op_proc_t exec_cont = 0; + int code; + + /* See above re GS_CHAR_FILL. */ + code = bbox_draw(i_ctx_p, GS_CHAR_FILL, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; +} +static int +bbox_stroke(i_ctx_t *i_ctx_p) +{ + op_proc_t exec_cont = 0; + int code; + + code = bbox_draw(i_ctx_p, gs_stroke, &exec_cont); + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; +} + +/* -------- Common code -------- */ + +/* Handle the results of interpreting the CharString. */ +/* pcref points to a t_string ref. */ +static 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_glyph_data_t cs_data; + gs_glyph_data_t *pcsd; + + cs_data.memory = imemory; + if (pcref == 0) { + pcsd = 0; + } else { + gs_glyph_data_from_string(&cs_data, pcref->value.const_bytes, + r_size(pcref), NULL); + pcsd = &cs_data; + } + /* + * 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, pcsd, &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(imemory, &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. + */ +static int +type1_push_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state *pcxs, + int (*cont)(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). + */ +static int +type1_call_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state * pcxs, + int (*cont) (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. */ +static int +type1_callout_dispatch(i_ctx_t *i_ctx_p, int (*cont)(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. */ +static int +op_type1_cleanup(i_ctx_t *i_ctx_p) +{ + ifree_object(r_ptr(esp + 2, void), "op_type1_cleanup"); + return 0; +} +static 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 -------- */ + +static 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. */ +/* <font> <code|name> <name> <charstring> %nobbox_continue - */ +static 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; + + 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; + op_proc_t cont, exec_cont = 0; + + if (pcxs->present == metricsNone) { + gs_point endpt; + + if ((code = gs_currentpoint(igs, &endpt)) < 0) + return code; + pcxs->sbw[2] = endpt.x, pcxs->sbw[3] = endpt.y; + pcxs->present = metricsSideBearingAndWidth; + } + /* + * 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; + code = type1exec_bbox(i_ctx_p, penum, pcxs, pfont, &exec_cont); + } else { + cont = (pbfont->PaintType == 0 && penum->orig_font->PaintType == 0 + ? nobbox_fill : nobbox_stroke); + exec_cont = 0; + code = zchar_set_cache(i_ctx_p, pbfont, op - 1, NULL, + pcxs->sbw + 2, + &pcxs->char_bbox, + cont, &exec_cont, + (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL)); + } + if (code >= 0 && exec_cont != 0) + code = (*exec_cont)(i_ctx_p); + return code; + } +} +/* Finish by popping the operands and filling or stroking. */ +static int +nobbox_draw(i_ctx_t *i_ctx_p, int (*draw)(gs_state *)) +{ + int code = draw(igs); + + if (code >= 0) + pop(4); + return code; +} +static int +nobbox_fill(i_ctx_t *i_ctx_p) +{ + /* See above re GS_CHAR_FILL. */ + return nobbox_draw(i_ctx_p, GS_CHAR_FILL); +} +static int +nobbox_stroke(i_ctx_t *i_ctx_p) +{ + /* As a compatibility to Adobe, use the exact "StrokeWidth". + Reset fill_adjust for that. */ + int code; + gs_fixed_point fa = i_ctx_p->pgs->fill_adjust; + + i_ctx_p->pgs->fill_adjust.x = i_ctx_p->pgs->fill_adjust.y = 0; + code = nobbox_draw(i_ctx_p, gs_stroke); + i_ctx_p->pgs->fill_adjust = fa; + return code; +} + +/* <font> <array> .setweightvector - */ +static int +zsetweightvector(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + gs_font *pfont; + int code = font_param(op - 1, &pfont); + gs_font_type1 *pfont1; + int size; + + if (code < 0) { + /* The font was not defined yet. Just ignore. See lib/gs_type1.ps . */ + pop(2); + return 0; + } + if (pfont->FontType != ft_encrypted && pfont->FontType != ft_encrypted2) + return_error(e_invalidfont); + pfont1 = (gs_font_type1 *)pfont; + size = r_size(op); + if (size != pfont1->data.WeightVector.count) + return_error(e_invalidfont); + code = process_float_array(imemory, op, size, pfont1->data.WeightVector.values); + if (code < 0) + return code; + pop(2); + return 0; +} + +/* ------ 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}, + {"4.setweightvector", zsetweightvector}, + op_def_end(0) +}; + +/* ------ Auxiliary procedures for type 1 fonts ------ */ + +static int +z1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph, gs_glyph_data_t *pgd) +{ + ref gref; + + glyph_ref(pfont->memory, glyph, &gref); + return zchar_charstring_data((gs_font *)pfont, &gref, pgd); +} + +static int +z1_subr_data(gs_font_type1 * pfont, int index, bool global, + gs_glyph_data_t *pgd) +{ + const font_data *pfdata = pfont_data(pfont); + ref subr; + int code; + + code = array_get(pfont->memory, (global ? &pfdata->u.type1.GlobalSubrs : + &pfdata->u.type1.Subrs), + index, &subr); + if (code < 0) + return code; + check_type_only(subr, t_string); + gs_glyph_data_from_string(pgd, subr.value.const_bytes, r_size(&subr), + NULL); + return 0; +} + +static int +z1_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph, + gs_const_string *gstr, gs_glyph_data_t *pgd) +{ + gs_glyph glyph = gs_c_known_encode((gs_char)ccode, + ENCODING_INDEX_STANDARD); + int code; + ref rglyph; + + if (glyph == GS_NO_GLYPH) + return_error(e_rangecheck); + if ((code = gs_c_glyph_name(glyph, gstr)) < 0 || + (code = name_ref(pfont->memory, gstr->data, gstr->size, &rglyph, 0)) < 0 + ) + return code; + if (pglyph) + *pglyph = name_index(pfont->memory, &rglyph); + if (pgd) + code = zchar_charstring_data((gs_font *)pfont, &rglyph, pgd); + return code; +} + +static 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; +} + +static 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, int WMode, gs_glyph glyph, const gs_matrix *pmat, + gx_path *ppath, double sbw[4]) +{ + gs_font_type1 *const pfont1 = (gs_font_type1 *)font; + ref gref; + gs_glyph_data_t gdata; + int code; + + glyph_ref(font->memory, glyph, &gref); + gdata.memory = font->memory; + code = zchar_charstring_data(font, &gref, &gdata); + if (code < 0) + return code; + return zcharstring_outline(pfont1, WMode, &gref, &gdata, pmat, ppath, sbw); +} +/* + * Get a glyph outline given a CharString. The glyph_outline procedure + * for CIDFontType 0 fonts uses this. + */ +int +zcharstring_outline(gs_font_type1 *pfont1, int WMode, const ref *pgref, + const gs_glyph_data_t *pgd_orig, + const gs_matrix *pmat, gx_path *ppath, double sbw[4]) +{ + const gs_glyph_data_t *pgd = pgd_orig; + int code; + gs_type1exec_state cxs; + gs_type1_state *const pcis = &cxs.cis; + const gs_type1_data *pdata; + int value; + gs_imager_state gis; + double wv[4]; + gs_point mpt; + + pdata = &pfont1->data; + if (pgd->bits.size <= max(pdata->lenIV, 0)) + return_error(e_invalidfont); +#if 0 /* Ignore CDevProc for now. */ + if (zchar_get_CDevProc((const gs_font_base *)pfont1, &pcdevproc)) + return_error(e_rangecheck); /* can't call CDevProc from here */ +#endif + switch (WMode) { + default: + code = zchar_get_metrics2((gs_font_base *)pfont1, pgref, wv); + if (code) { + sbw[0] = wv[2]; + sbw[1] = wv[3]; + sbw[2] = wv[0]; + sbw[3] = wv[1]; + 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, NULL, NULL, true, 0, + pfont1); + if (code < 0) + return code; + cxs.cis.no_grid_fitting = true; + 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, pgd, &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); + type1_cis_get_metrics(pcis, sbw); + pgd = 0; + goto icont; + } +} + +/* + * Redefine glyph_info to take Metrics[2] and CDevProc into account (unless + * GLYPH_INFO_OUTLINE_WIDTHS is set). If CDevProc is present, return + * e_rangecheck, since we can't call the interpreter from here. + */ +int +z1_glyph_info_generic(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, + int members, gs_glyph_info_t *info, font_proc_glyph_info((*proc)), int wmode) +{ + ref gref; + ref *pcdevproc; + gs_font_base *const pbfont = (gs_font_base *)font; + int width_members = members & (GLYPH_INFO_WIDTH0 << wmode); + int outline_widths = members & GLYPH_INFO_OUTLINE_WIDTHS; + bool modified_widths = false; + int default_members = members & ~(width_members + outline_widths + + GLYPH_INFO_VVECTOR0 + GLYPH_INFO_VVECTOR1 + + GLYPH_INFO_CDEVPROC); + int done_members = 0; + int code; + + if (!width_members) + return (*proc)(font, glyph, pmat, members, info); + if (!outline_widths && zchar_get_CDevProc(pbfont, &pcdevproc)) { + done_members |= GLYPH_INFO_CDEVPROC; + if (members & GLYPH_INFO_CDEVPROC) { + info->members = done_members; + return_error(e_rangecheck); + } else { + /* Ignore CDevProc. Used to compure MissingWidth.*/ + } + } + glyph_ref(pbfont->memory, glyph, &gref); + if (width_members == GLYPH_INFO_WIDTH1) { + double wv[4]; + code = zchar_get_metrics2(pbfont, &gref, wv); + if (code > 0) { + modified_widths = true; + info->width[1].x = wv[0]; + info->width[1].y = wv[1]; + info->v.x = wv[2]; + info->v.y = wv[3]; + done_members = width_members | GLYPH_INFO_VVECTOR1; + width_members = 0; + } + } + if (width_members) { + double sbw[4]; + code = zchar_get_metrics(pbfont, &gref, sbw); + if (code > 0) { + modified_widths = true; + info->width[wmode].x = sbw[2]; + info->width[wmode].y = sbw[3]; + if (code == metricsSideBearingAndWidth) { + info->v.x = sbw[0]; + info->v.y = sbw[1]; + width_members |= GLYPH_INFO_VVECTOR0; + } else { + info->v.x = 0; + info->v.y = 0; + } + done_members = width_members; + width_members = 0; + } + } + + if (outline_widths) { + if (modified_widths || zchar_get_CDevProc(pbfont, &pcdevproc)) { + /* Discard the modified widths, but indicate they exist. */ + width_members |= done_members; + done_members = outline_widths; + } + } + default_members |= width_members; + if (default_members) { + code = (*proc)(font, glyph, pmat, default_members, info); + + if (code < 0) + return code; + } else + info->members = 0; + info->members |= done_members; + return 0; +} + +int +z1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, + int members, gs_glyph_info_t *info) +{ + int wmode = font->WMode; + + return z1_glyph_info_generic(font, glyph, pmat, members, info, + &gs_type1_glyph_info, wmode); +} + +/* Get a Type 1 or Type 9 character metrics and set the cache device. */ +int +z1_set_cache(i_ctx_t *i_ctx_p, gs_font_base *pbfont, ref *cnref, + gs_glyph glyph, op_proc_t cont, op_proc_t *exec_cont) +{ /* This function is similar to zchar42_set_cache. */ + double sbw[4]; + gs_glyph_info_t info; + int wmode = gs_rootfont(igs)->WMode; + int code; + gs_matrix id_matrix = { identity_matrix_body }; + + code = gs_default_glyph_info((gs_font *)pbfont, glyph, &id_matrix, + ((GLYPH_INFO_WIDTH0 | GLYPH_INFO_VVECTOR0) << wmode) | GLYPH_INFO_BBOX, + &info); + if (code < 0) + return code; + sbw[0] = info.v.x; + sbw[1] = info.v.y; + sbw[2] = info.width[wmode].x; + sbw[3] = info.width[wmode].y; + return zchar_set_cache(i_ctx_p, pbfont, cnref, NULL, + sbw + 2, &info.bbox, + cont, exec_cont, + wmode ? sbw : NULL); +} |