static bool charstring_is_notdef_proc(const gs_memory_t *mem, const ref *pcstr) { if (r_is_array(pcstr) && r_size(pcstr) == 4) { ref elts[4]; long i; for (i = 0; i < 4; ++i) array_get(mem, pcstr, i, &elts[i]); if (r_has_type(&elts[0], t_name) && r_has_type(&elts[1], t_integer) && elts[1].value.intval == 0 && r_has_type(&elts[2], t_integer) && elts[2].value.intval == 0 && r_has_type(&elts[3], t_name) ) { ref nref; name_enter_string(mem, "pop", &nref); if (name_eq(&elts[0], &nref)) { name_enter_string(mem, "setcharwidth", &nref); if (name_eq(&elts[3], &nref)) return true; } } } return false; }
/* Call out to a PostScript procedure. */ static int push_callout(i_ctx_t *i_ctx_p, const char *callout_name) { int code; check_estack(1); code = name_enter_string(imemory, callout_name, esp + 1); if (code < 0) return code; ++esp; r_set_attrs(esp, a_executable); return o_push_estack; }
/* - .typenames <name1|null> ... <nameN|null> */ static int ztypenames(i_ctx_t *i_ctx_p) { os_ptr op = osp; static const char *const tnames[] = { REF_TYPE_NAME_STRINGS }; int i; check_ostack(t_next_index); for (i = 0; i < t_next_index; i++) { ref *const rtnp = op + 1 + i; if (i >= countof(tnames) || tnames[i] == 0) make_null(rtnp); else { int code = name_enter_string(imemory, tnames[i], rtnp); if (code < 0) return code; r_set_attrs(rtnp, a_executable); } } osp += t_next_index; return 0; }
/* initialize the dictionaries that hold operator definitions. */ int obj_init(i_ctx_t **pi_ctx_p, gs_dual_memory_t *idmem) { int level = gs_op_language_level(); ref system_dict; i_ctx_t *i_ctx_p; int code; /* * Create systemdict. The context machinery requires that * we do this before initializing the interpreter. */ code = dict_alloc(idmem->space_global, (level >= 3 ? SYSTEMDICT_LL3_SIZE : level >= 2 ? SYSTEMDICT_LEVEL2_SIZE : SYSTEMDICT_SIZE), &system_dict); if (code < 0) return code; /* Initialize the interpreter. */ code = gs_interp_init(pi_ctx_p, &system_dict, idmem); if (code < 0) return code; i_ctx_p = *pi_ctx_p; { #define icount countof(initial_dictionaries) ref idicts[icount]; int i; const op_def *const *tptr; min_dstack_size = MIN_DSTACK_SIZE; refset_null(idicts, icount); /* Put systemdict on the dictionary stack. */ if (level >= 2) { dsp += 2; /* * For the moment, let globaldict be an alias for systemdict. */ dsp[-1] = system_dict; min_dstack_size++; } else { ++dsp; } *dsp = system_dict; /* Create dictionaries which are to be homes for operators. */ for (tptr = op_defs_all; *tptr != 0; tptr++) { const op_def *def; for (def = *tptr; def->oname != 0; def++) if (op_def_is_begin_dict(def)) { if (make_initial_dict(i_ctx_p, def->oname, idicts) == 0) return_error(e_VMerror); } } /* Set up the initial dstack. */ for (i = 0; i < countof(initial_dstack); i++) { const char *dname = initial_dstack[i]; ++dsp; if (!strcmp(dname, "userdict")) dstack_userdict_index = dsp - dsbot; ref_assign(dsp, make_initial_dict(i_ctx_p, dname, idicts)); } /* Enter names of referenced initial dictionaries into systemdict. */ initial_enter_name("systemdict", systemdict); for (i = 0; i < icount; i++) { ref *idict = &idicts[i]; if (!r_has_type(idict, t_null)) { /* * Note that we enter the dictionary in systemdict * even if it is in local VM. There is a special * provision in the garbage collector for this: * see ivmspace.h for more information. * In order to do this, we must temporarily * identify systemdict as local, so that the * store check in dict_put won't fail. */ uint save_space = r_space(systemdict); r_set_space(systemdict, avm_local); code = initial_enter_name(initial_dictionaries[i].name, idict); r_set_space(systemdict, save_space); if (code < 0) return code; } } #undef icount } gs_interp_reset(i_ctx_p); { ref vnull, vtrue, vfalse; make_null(&vnull); make_true(&vtrue); make_false(&vfalse); if ((code = initial_enter_name("null", &vnull)) < 0 || (code = initial_enter_name("true", &vtrue)) < 0 || (code = initial_enter_name("false", &vfalse)) < 0 ) return code; } /* Create the error name table */ { int n = countof(gs_error_names) - 1; int i; ref era; code = ialloc_ref_array(&era, a_readonly, n, "ErrorNames"); if (code < 0) return code; for (i = 0; i < n; i++) if ((code = name_enter_string(imemory, (const char *)gs_error_names[i], era.value.refs + i)) < 0) return code; return initial_enter_name("ErrorNames", &era); } }
/* * Note that op_show_continue_dispatch sets osp = op explicitly iff the * dispatch succeeds. This is so that the show operators don't pop anything * from the o-stack if they don't succeed. Note also that if it returns an * error, it has freed the enumerator. */ int op_show_continue_dispatch(i_ctx_t *i_ctx_p, int npop, int code) { os_ptr op = osp - npop; gs_text_enum_t *penum = senum; switch (code) { case 0: { /* all done */ os_ptr save_osp = osp; osp = op; code = (*real_opproc(&seproc)) (i_ctx_p); op_show_free(i_ctx_p, code); if (code < 0) { osp = save_osp; return code; } return o_pop_estack; } case TEXT_PROCESS_INTERVENE: { ref *pslot = &sslot; /* only used for kshow */ push(2); make_int(op - 1, gs_text_current_char(penum)); /* previous char */ make_int(op, gs_text_next_char(penum)); push_op_estack(op_show_continue); /* continue after kerning */ *++esp = *pslot; /* kerning procedure */ return o_push_estack; } case TEXT_PROCESS_RENDER: { gs_font *pfont = gs_currentfont(igs); font_data *pfdata = pfont_data(pfont); gs_char chr = gs_text_current_char(penum); gs_glyph glyph = gs_text_current_glyph(penum); push(2); op[-1] = pfdata->dict; /* push the font */ /* * For Type 1 and Type 4 fonts, prefer BuildChar to BuildGlyph * if there is no glyph, or if there is both a character and a * glyph and the glyph is the one that corresponds to the * character in the Encoding, so that PostScript procedures * appearing in the CharStrings dictionary will receive the * character code rather than the character name; for Type 3 * fonts, prefer BuildGlyph to BuildChar. For other font types * (such as CID fonts), only BuildGlyph will be present. */ if (pfont->FontType == ft_user_defined) { /* Type 3 font, prefer BuildGlyph. */ if (level2_enabled && !r_has_type(&pfdata->BuildGlyph, t_null) && glyph != gs_no_glyph ) { glyph_ref(imemory, glyph, op); esp[2] = pfdata->BuildGlyph; } else if (r_has_type(&pfdata->BuildChar, t_null)) goto err; else if (chr == gs_no_char) { /* glyphshow, reverse map the character */ /* through the Encoding */ ref gref; const ref *pencoding = &pfdata->Encoding; glyph_ref(imemory, glyph, &gref); if (!map_glyph_to_char(imemory, &gref, pencoding, (ref *) op) ) { /* Not found, try .notdef */ name_enter_string(imemory, ".notdef", &gref); if (!map_glyph_to_char(imemory, &gref, pencoding, (ref *) op) ) goto err; } esp[2] = pfdata->BuildChar; } else { make_int(op, chr & 0xff); esp[2] = pfdata->BuildChar; } } else { /* * For a Type 1 or Type 4 font, prefer BuildChar or * BuildGlyph as described above: we know that both * BuildChar and BuildGlyph are present. For other font * types, only BuildGlyph is available. */ ref eref, gref; if (chr != gs_no_char && !r_has_type(&pfdata->BuildChar, t_null) && (glyph == gs_no_glyph || (!r_has_type(&pfdata->Encoding, t_null) && array_get(imemory, &pfdata->Encoding, (long)(chr & 0xff), &eref) >= 0 && (glyph_ref(imemory, glyph, &gref), obj_eq(imemory, &gref, &eref)))) ) { make_int(op, chr & 0xff); esp[2] = pfdata->BuildChar; } else { /* We might not have a glyph: substitute 0. **HACK** */ if (glyph == gs_no_glyph) make_int(op, 0); else glyph_ref(imemory, glyph, op); esp[2] = pfdata->BuildGlyph; } } /* Save the stack depths in case we bail out. */ sodepth.value.intval = ref_stack_count(&o_stack) - 2; sddepth.value.intval = ref_stack_count(&d_stack); push_op_estack(op_show_continue); ++esp; /* skip BuildChar or BuildGlyph proc */ return o_push_estack; } case TEXT_PROCESS_CDEVPROC: { gs_font *pfont = penum->current_font; ref cnref; op_proc_t cont = op_show_continue, exec_cont = 0; gs_glyph glyph = penum->returned.current_glyph; int code; pop(npop); op = osp; glyph_ref(imemory, glyph, &cnref); if (pfont->FontType == ft_CID_TrueType) { gs_font_type42 *pfont42 = (gs_font_type42 *)pfont; uint glyph_index = pfont42->data.get_glyph_index(pfont42, glyph); code = zchar42_set_cache(i_ctx_p, (gs_font_base *)pfont42, &cnref, glyph_index, cont, &exec_cont); } else if (pfont->FontType == ft_CID_encrypted) code = z1_set_cache(i_ctx_p, (gs_font_base *)pfont, &cnref, glyph, cont, &exec_cont); else return_error(e_unregistered); /* Unimplemented. */ if (exec_cont != 0) return_error(e_unregistered); /* Must not happen. */ return code; } default: /* error */ err: if (code >= 0) code = gs_note_error(e_invalidfont); return op_show_free(i_ctx_p, code); } }
/* * Handle a scan_Comment or scan_DSC_Comment return from gs_scan_token * (scan_code) by calling out to %Process[DSC]Comment. The continuation * procedure expects the scanner state on the o-stack. */ int ztoken_handle_comment(i_ctx_t *i_ctx_p, scanner_state *sstate, const ref *ptoken, int scan_code, bool save, bool push_file, op_proc_t cont) { const char *proc_name; scanner_state *pstate; os_ptr op; ref *ppcproc; int code; switch (scan_code) { case scan_Comment: proc_name = "%ProcessComment"; break; case scan_DSC_Comment: proc_name = "%ProcessDSCComment"; break; default: return_error(e_Fatal); /* can't happen */ } /* * We can't use check_ostack here, because it returns on overflow. */ /*check_ostack(2);*/ if (ostop - osp < 2) { code = ref_stack_extend(&o_stack, 2); if (code < 0) return code; } check_estack(3); code = name_enter_string(imemory, proc_name, esp + 3); if (code < 0) return code; if (save) { pstate = (scanner_state *)ialloc_struct(scanner_state_dynamic, &st_scanner_state_dynamic, "ztoken_handle_comment"); if (pstate == 0) return_error(e_VMerror); ((scanner_state_dynamic *)pstate)->mem = imemory; *pstate = *sstate; } else pstate = sstate; /* Save the token now -- it might be on the e-stack. */ if (!pstate->s_pstack) osp[2] = *ptoken; /* * Push the continuation, scanner state, file, and callout procedure * on the e-stack. */ make_op_estack(esp + 1, cont); make_istruct(esp + 2, 0, pstate); ppcproc = dict_find_name(esp + 3); if (ppcproc == 0) { /* * This can only happen during initialization. * Pop the comment string from the o-stack if needed (see below). */ if (pstate->s_pstack) --osp; esp += 2; /* do run the continuation */ } else { /* * Push the file and comment string on the o-stack. * If we were inside { }, the comment string is already on the stack. */ if (pstate->s_pstack) { op = ++osp; *op = op[-1]; } else { op = osp += 2; /* *op = *ptoken; */ /* saved above */ } op[-1] = pstate->s_file; esp[3] = *ppcproc; esp += 3; } return o_push_estack; }
int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath, bool with_ucache) { int size = (with_ucache ? 6 : 5); gs_path_enum penum; gs_rect bbox; int op; ref *next; int code; /* Compute the bounding box. */ if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) { /* * Note: Adobe throws 'nocurrentpoint' error, but the PLRM does * not list this as a possible error from 'upath', so if we are * not in CPSI compatibility mode, we set a reasonable default * bbox instead. */ if (code != e_nocurrentpoint || gs_currentcpsimode(imemory)) return code; bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0; } code = path_length_for_upath(ppath); if (code < 0) return code; size += code; if (size >= 65536) return_error(e_limitcheck); code = ialloc_ref_array(rupath, a_all | a_executable, size, "make_upath"); if (code < 0) return code; /* Construct the path. */ next = rupath->value.refs; if (with_ucache) { if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0) return code; r_set_attrs(next, a_executable | l_new); ++next; } make_real_new(next, bbox.p.x); make_real_new(next + 1, bbox.p.y); make_real_new(next + 2, bbox.q.x); make_real_new(next + 3, bbox.q.y); next += 4; if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0) return code; r_set_attrs(next, a_executable | l_new); ++next; { gs_point pts[3]; /* Patch the path in the gstate to set up the enumerator. */ gx_path *save_path = pgs->path; pgs->path = ppath; gs_path_enum_copy_init(&penum, pgs, false); pgs->path = save_path; while ((op = gs_path_enum_next(&penum, pts)) != 0) { const char *opstr; switch (op) { case gs_pe_moveto: opstr = "moveto"; goto ml; case gs_pe_lineto: opstr = "lineto"; ml:make_real_new(next, pts[0].x); make_real_new(next + 1, pts[0].y); next += 2; break; case gs_pe_curveto: opstr = "curveto"; make_real_new(next, pts[0].x); make_real_new(next + 1, pts[0].y); make_real_new(next + 2, pts[1].x); make_real_new(next + 3, pts[1].y); make_real_new(next + 4, pts[2].x); make_real_new(next + 5, pts[2].y); next += 6; break; case gs_pe_closepath: opstr = "closepath"; break; default: return_error(e_unregistered); } if ((code = name_enter_string(pgs->memory, opstr, next)) < 0) return code; r_set_attrs(next, a_executable); ++next; } } return 0; }
/* <dict> <string> .parse_dsc_comments <dict> <dsc code> */ static int zparse_dsc_comments(i_ctx_t *i_ctx_p) { #define MAX_DSC_MSG_SIZE (DSC_LINE_LENGTH + 4) /* Allow for %% and CR/LF */ os_ptr const opString = osp; os_ptr const opDict = opString - 1; uint ssize; int comment_code, code; char dsc_buffer[MAX_DSC_MSG_SIZE + 2]; const cmdlist_t *pCmdList = DSCcmdlist; const char * const *pBadList = BadCmdlist; ref * pvalue; CDSC * dsc_data = NULL; dict_param_list list; /* * Verify operand types and length of DSC comment string. If a comment * is too long then we simply truncate it. Russell's parser gets to * handle any errors that may result. (Crude handling but the comment * is bad, so ...). */ check_type(*opString, t_string); check_dict_write(*opDict); ssize = r_size(opString); if (ssize > MAX_DSC_MSG_SIZE) /* need room for EOL + \0 */ ssize = MAX_DSC_MSG_SIZE; /* * Pick up the comment string to be parsed. */ memcpy(dsc_buffer, opString->value.bytes, ssize); dsc_buffer[ssize] = 0x0d; /* Russell wants a 'line end' */ dsc_buffer[ssize + 1] = 0; /* Terminate string */ /* * Skip data block comments (see comments in front of BadCmdList). */ while (*pBadList && strncmp(*pBadList, dsc_buffer, strlen(*pBadList))) pBadList++; if (*pBadList) { /* If found in list, then skip comment */ comment_code = 0; /* Force NOP */ } else { /* * Parse comments - use Russell Lang's DSC parser. We need to get * data area for Russell Lang's parser. Note: We have saved the * location of the data area for the parser in our DSC dict. */ code = dict_find_string(opDict, dsc_dict_name, &pvalue); dsc_data = r_ptr(pvalue, dsc_data_t)->dsc_data_ptr; if (code < 0) return code; comment_code = dsc_scan_data(dsc_data, dsc_buffer, ssize + 1); if_debug1('%', "[%%].parse_dsc_comments: code = %d\n", comment_code); /* * We ignore any errors from Russell's parser. The only value that * it will return for an error is -1 so there is very little information. * We also do not want bad DSC comments to abort processing of an * otherwise valid PS file. */ if (comment_code < 0) comment_code = 0; } /* * Transfer data from DSC structure to postscript variables. * Look up proper handler in the local cmd decode list. */ while (pCmdList->code && pCmdList->code != comment_code ) pCmdList++; if (pCmdList->dsc_proc) { code = dict_param_list_write(&list, opDict, NULL, iimemory); if (code < 0) return code; code = (pCmdList->dsc_proc)((gs_param_list *)&list, dsc_data); iparam_list_release(&list); if (code < 0) return code; } /* Put DSC comment name onto operand stack (replace string). */ return name_enter_string(imemory, pCmdList->comment_name, opString); }