diff options
Diffstat (limited to 'gs/base/gdevpdte.c')
-rw-r--r-- | gs/base/gdevpdte.c | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/gs/base/gdevpdte.c b/gs/base/gdevpdte.c new file mode 100644 index 000000000..ceaffe136 --- /dev/null +++ b/gs/base/gdevpdte.c @@ -0,0 +1,1174 @@ +/* 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$ */ +/* Encoding-based (Type 1/2/42) text processing for pdfwrite. */ + +#include "math_.h" +#include "memory_.h" +#include "string_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsutil.h" +#include "gxfcmap.h" +#include "gxfcopy.h" +#include "gxfont.h" +#include "gxfont0.h" +#include "gxfont0c.h" +#include "gxpath.h" /* for getting current point */ +#include "gdevpsf.h" +#include "gdevpdfx.h" +#include "gdevpdfg.h" +#include "gdevpdfo.h" +#include "gdevpdtx.h" +#include "gdevpdtd.h" +#include "gdevpdtf.h" +#include "gdevpdts.h" +#include "gdevpdtt.h" + +static int pdf_char_widths(gx_device_pdf *const pdev, + pdf_font_resource_t *pdfont, int ch, + gs_font_base *font, + pdf_glyph_widths_t *pwidths /* may be NULL */); +static int pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr, + const gs_matrix *pfmat, + pdf_text_process_state_t *ppts, + const gs_glyph *gdata); + +/* + * Process a string with a simple gs_font. + */ +int +pdf_process_string_aux(pdf_text_enum_t *penum, gs_string *pstr, + const gs_glyph *gdata, const gs_matrix *pfmat, + pdf_text_process_state_t *ppts) +{ + gs_font_base *font = (gs_font_base *)penum->current_font; + + switch (font->FontType) { + case ft_TrueType: + case ft_encrypted: + case ft_encrypted2: + case ft_user_defined: + break; + default: + return_error(gs_error_rangecheck); + } + return pdf_process_string(penum, pstr, pfmat, ppts, gdata); +} + +/* + * Add char code pair to ToUnicode CMap, + * creating the CMap on neccessity. + */ +int +pdf_add_ToUnicode(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont, + gs_glyph glyph, gs_char ch, const gs_const_string *gnstr) +{ int code; + gs_char unicode; + + if (glyph == GS_NO_GLYPH) + return 0; + unicode = font->procs.decode_glyph((gs_font *)font, glyph); + if (unicode == GS_NO_CHAR && gnstr != NULL && gnstr->size == 7) { + if (!memcmp(gnstr->data, "uni", 3)) { + static const char *hexdigits = "0123456789ABCDEF"; + char *d0 = strchr(hexdigits, gnstr->data[3]); + char *d1 = strchr(hexdigits, gnstr->data[4]); + char *d2 = strchr(hexdigits, gnstr->data[5]); + char *d3 = strchr(hexdigits, gnstr->data[6]); + + if (d0 != NULL && d1 != NULL && d2 != NULL && d3 != NULL) + unicode = ((d0 - hexdigits) << 12) + ((d1 - hexdigits) << 8) + + ((d2 - hexdigits) << 4 ) + (d3 - hexdigits); + } + } + if (unicode != GS_NO_CHAR) { + if (pdfont->cmap_ToUnicode == NULL) { + uint num_codes = 256, key_size = 1; + + if (font->FontType == ft_CID_encrypted) { + gs_font_cid0 *pfcid = (gs_font_cid0 *)font; + + num_codes = pfcid->cidata.common.CIDCount; + key_size = 2; + } else if (font->FontType == ft_CID_TrueType) { +#if 0 + gs_font_cid2 *pfcid = (gs_font_cid2 *)font; + + num_codes = pfcid->cidata.common.CIDCount; +#else + /* Since PScript5.dll creates GlyphNames2Unicode with character codes + instead CIDs, and with the WinCharSetFFFF-H2 CMap + character codes appears from the range 0-0xFFFF (Bug 687954), + we must use the maximal character code value for the ToUnicode + code count. */ + num_codes = 65536; +#endif + key_size = 2; + } + code = gs_cmap_ToUnicode_alloc(pdev->pdf_memory, pdfont->rid, num_codes, key_size, + &pdfont->cmap_ToUnicode); + if (code < 0) + return code; + } + if (pdfont->cmap_ToUnicode != NULL) + gs_cmap_ToUnicode_add_pair(pdfont->cmap_ToUnicode, ch, unicode); + } + return 0; +} + +typedef struct { + gx_device_pdf *pdev; + pdf_resource_type_t rtype; +} pdf_resource_enum_data_t; + +static int +process_resources2(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v) +{ + pdf_resource_enum_data_t *data = (pdf_resource_enum_data_t *)client_data; + pdf_resource_t *pres = pdf_find_resource_by_resource_id(data->pdev, data->rtype, v->contents.object->id); + + if (pres == NULL) + return_error(gs_error_unregistered); /* Must not happen. */ + pres->where_used |= data->pdev->used_mask; + return 0; +} + +static int +process_resources1(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v) +{ + pdf_resource_enum_data_t *data = (pdf_resource_enum_data_t *)client_data; + static const char *rn[] = {PDF_RESOURCE_TYPE_NAMES}; + int i; + + for (i = 0; i < count_of(rn); i++) { + if (rn[i] != NULL && !bytes_compare((const byte *)rn[i], strlen(rn[i]), key_data, key_size)) + break; + } + if (i >= count_of(rn)) + return 0; + data->rtype = i; + return cos_dict_forall((cos_dict_t *)v->contents.object, data, process_resources2); +} + +/* + * Register charproc fonts with the page or substream. + */ +int +pdf_used_charproc_resources(gx_device_pdf *pdev, pdf_font_resource_t *pdfont) +{ + if (pdfont->where_used & pdev->used_mask) + return 0; + pdfont->where_used |= pdev->used_mask; + if (pdev->CompatibilityLevel >= 1.2) + return 0; + if (pdfont->FontType == ft_user_defined) { + pdf_resource_enum_data_t data; + + data.pdev = pdev; + return cos_dict_forall(pdfont->u.simple.s.type3.Resources, &data, process_resources1); + } + return 0; +} + +/* + * Given a text string and a simple gs_font, return a font resource suitable + * for the text string, possibly re-encoding the string. This + * may involve creating a font resource and/or adding glyphs and/or Encoding + * entries to it. + * + * Sets *ppdfont. + */ +static int +pdf_encode_string_element(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont, + gs_char ch, const gs_glyph *gdata) +{ + gs_font_base *cfont, *ccfont; + int code; + gs_glyph copied_glyph; + gs_const_string gnstr; + pdf_encoding_element_t *pet; + gs_glyph glyph; + + /* + * In contradiction with pre-7.20 versions of pdfwrite, + * we never re-encode texts due to possible encoding conflict while font merging. + */ + cfont = pdf_font_resource_font(pdfont, false); + ccfont = pdf_font_resource_font(pdfont, true); + pet = &pdfont->u.simple.Encoding[ch]; + glyph = (gdata == NULL ? font->procs.encode_char(font, ch, GLYPH_SPACE_NAME) + : *gdata); + if (glyph == GS_NO_GLYPH || glyph == pet->glyph) + return 0; + if (pet->glyph != GS_NO_GLYPH) { /* encoding conflict */ + return_error(gs_error_rangecheck); + /* Must not happen because pdf_obtain_font_resource + * checks for encoding compatibility. + */ + } + code = font->procs.glyph_name(font, glyph, &gnstr); + if (code < 0) + return code; /* can't get name of glyph */ + if (font->FontType != ft_user_defined) { + /* The standard 14 fonts don't have a FontDescriptor. */ + code = (pdfont->base_font != 0 ? + pdf_base_font_copy_glyph(pdfont->base_font, glyph, (gs_font_base *)font) : + pdf_font_used_glyph(pdfont->FontDescriptor, glyph, (gs_font_base *)font)); + if (code < 0 && code != gs_error_undefined) + return code; + if (code == gs_error_undefined) { + /* PS font has no such glyph. */ + if (bytes_compare(gnstr.data, gnstr.size, (const byte *)".notdef", 7)) { + pet->glyph = glyph; + pet->str = gnstr; + pet->is_difference = true; + } + } else if (pdfont->base_font == NULL && ccfont != NULL && + (gs_copy_glyph_options(font, glyph, (gs_font *)ccfont, COPY_GLYPH_NO_NEW) != 1 || + gs_copied_font_add_encoding((gs_font *)ccfont, ch, glyph) < 0)) { + /* + * The "complete" copy of the font appears incomplete + * due to incrementally added glyphs. Drop the "complete" + * copy now and continue with subset font only. + * + * Note that we need to add the glyph to the encoding of the + * "complete" font, because "PPI-ProPag 2.6.1.4 (archivePg)" + * creates multiple font copies with reduced encodings + * (we believe it is poorly designed), + * and we can merge the copies back to a single font (see Bug 686875). + * We also check whether the encoding is compatible. + * It must be compatible here due to the pdf_obtain_font_resource + * and ccfont logics, but we want to ensure for safety reason. + */ + ccfont = NULL; + pdf_font_descriptor_drop_complete_font(pdfont->FontDescriptor); + } + /* + * We arbitrarily allow the first encoded character in a given + * position to determine the encoding associated with the copied + * font. + */ + copied_glyph = cfont->procs.encode_char((gs_font *)cfont, ch, + GLYPH_SPACE_NAME); + if (glyph != copied_glyph && + gs_copied_font_add_encoding((gs_font *)cfont, ch, glyph) < 0 + ) + pet->is_difference = true; + pdfont->used[ch >> 3] |= 0x80 >> (ch & 7); + } + /* + * We always generate ToUnicode for simple fonts, because + * we can't detemine in advance, which glyphs the font actually uses. + * The decision about writing it out is deferred until pdf_write_font_resource. + */ + code = pdf_add_ToUnicode(pdev, font, pdfont, glyph, ch, &gnstr); + if (code < 0) + return code; + pet->glyph = glyph; + pet->str = gnstr; + return 0; +} + +/* + * Estimate text bbox. + */ +static int +process_text_estimate_bbox(pdf_text_enum_t *pte, gs_font_base *font, + const gs_const_string *pstr, + const gs_matrix *pfmat, + gs_rect *text_bbox, gs_point *pdpt) +{ + int i; + int space_char = + (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ? + pte->text.space.s_char : -1); + int WMode = font->WMode; + int code = 0; + gs_point total = {0, 0}; + gs_fixed_point origin; + gs_matrix m; + int xy_index = pte->xy_index; + + if (font->FontBBox.p.x == font->FontBBox.q.x || + font->FontBBox.p.y == font->FontBBox.q.y) + return_error(gs_error_undefined); + code = gx_path_current_point(pte->path, &origin); + if (code < 0) + return code; + m = ctm_only(pte->pis); + m.tx = fixed2float(origin.x); + m.ty = fixed2float(origin.y); + gs_matrix_multiply(pfmat, &m, &m); + for (i = 0; i < pstr->size; ++i) { + byte c = pstr->data[i]; + gs_rect bbox; + gs_point wanted, tpt, p0, p1, p2, p3; + gs_glyph glyph = font->procs.encode_char((gs_font *)font, c, + GLYPH_SPACE_NAME); + gs_glyph_info_t info; + int code = font->procs.glyph_info((gs_font *)font, glyph, NULL, + GLYPH_INFO_WIDTH0 << WMode, + &info); + + if (code < 0) + return code; + gs_point_transform(font->FontBBox.p.x, font->FontBBox.p.y, &m, &p0); + gs_point_transform(font->FontBBox.p.x, font->FontBBox.q.y, &m, &p1); + gs_point_transform(font->FontBBox.q.x, font->FontBBox.p.y, &m, &p2); + gs_point_transform(font->FontBBox.q.x, font->FontBBox.q.y, &m, &p3); + bbox.p.x = min(min(p0.x, p1.x), min(p1.x, p2.x)) + total.x; + bbox.p.y = min(min(p0.y, p1.y), min(p1.y, p2.y)) + total.y; + bbox.q.x = max(max(p0.x, p1.x), max(p1.x, p2.x)) + total.x; + bbox.q.y = max(max(p0.y, p1.y), max(p1.y, p2.y)) + total.y; + if (i == 0) + *text_bbox = bbox; + else + rect_merge(*text_bbox, bbox); + if (pte->text.operation & TEXT_REPLACE_WIDTHS) { + gs_text_replaced_width(&pte->text, xy_index++, &tpt); + gs_distance_transform(tpt.x, tpt.y, &ctm_only(pte->pis), &wanted); + } else { + gs_distance_transform(info.width[WMode].x, + info.width[WMode].y, + &m, &wanted); + if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) { + gs_distance_transform(pte->text.delta_all.x, + pte->text.delta_all.y, + &ctm_only(pte->pis), &tpt); + wanted.x += tpt.x; + wanted.y += tpt.y; + } + if (pstr->data[i] == space_char && pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) { + gs_distance_transform(pte->text.delta_space.x, + pte->text.delta_space.y, + &ctm_only(pte->pis), &tpt); + wanted.x += tpt.x; + wanted.y += tpt.y; + } + } + total.x += wanted.x; + total.y += wanted.y; + } + *pdpt = total; + return 0; +} + +void +adjust_first_last_char(pdf_font_resource_t *pdfont, byte *str, int size) +{ + int i; + + for (i = 0; i < size; ++i) { + int chr = str[i]; + + if (chr < pdfont->u.simple.FirstChar) + pdfont->u.simple.FirstChar = chr; + if (chr > pdfont->u.simple.LastChar) + pdfont->u.simple.LastChar = chr; + } +} + +int +pdf_shift_text_currentpoint(pdf_text_enum_t *penum, gs_point *wpt) +{ + gs_state *pgs; + extern_st(st_gs_state); + + if (gs_object_type(penum->dev->memory, penum->pis) != &st_gs_state) { + /* Probably never happens. Not sure though. */ + return_error(gs_error_unregistered); + } + pgs = (gs_state *)penum->pis; + return gs_moveto_aux(penum->pis, gx_current_path(pgs), + fixed2float(penum->origin.x) + wpt->x, + fixed2float(penum->origin.y) + wpt->y); +} + +/* + * Internal procedure to process a string in a non-composite font. + * Doesn't use or set pte->{data,size,index}; may use/set pte->xy_index; + * may set penum->returned.total_width. Sets ppts->values. + * + * Note that the caller is responsible for re-encoding the string, if + * necessary; for adding Encoding entries in pdfont; and for copying any + * necessary glyphs. penum->current_font provides the gs_font for getting + * glyph metrics, but this font's Encoding is not used. + */ +static int process_text_return_width(const pdf_text_enum_t *pte, + gs_font_base *font, + pdf_text_process_state_t *ppts, + const gs_const_string *pstr, const gs_glyph *gdata, + gs_point *pdpt, int *accepted); +static int +pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr, + const gs_matrix *pfmat, + pdf_text_process_state_t *ppts, const gs_glyph *gdata) +{ + gx_device_pdf *const pdev = (gx_device_pdf *)penum->dev; + gs_font_base *font = (gs_font_base *)penum->current_font; + pdf_font_resource_t *pdfont; + const gs_text_params_t *text = &penum->text; + int code = 0, mask; + gs_point width_pt; + gs_rect text_bbox; + int accepted; + + code = pdf_obtain_font_resource(penum, pstr, &pdfont); + if (code < 0) + return code; + if (pfmat == 0) + pfmat = &font->FontMatrix; + if (text->operation & TEXT_RETURN_WIDTH) { + code = gx_path_current_point(penum->path, &penum->origin); + if (code < 0) + return code; + } + if (text->size == 0) + return 0; + if (penum->pis->text_rendering_mode != 3 && !(text->operation & TEXT_DO_NONE)) { + /* + * Acrobat Reader can't handle text with huge coordinates, + * so skip the text if it is outside the clip bbox + * (Note : it ever fails with type 3 fonts). + */ + code = process_text_estimate_bbox(penum, font, (gs_const_string *)pstr, pfmat, + &text_bbox, &width_pt); + if (code == 0) { + gs_fixed_rect clip_bbox; + gs_rect rect; + + gx_cpath_outer_box(penum->pcpath, &clip_bbox); + rect.p.x = fixed2float(clip_bbox.p.x); + rect.p.y = fixed2float(clip_bbox.p.y); + rect.q.x = fixed2float(clip_bbox.q.x); + rect.q.y = fixed2float(clip_bbox.q.y); + rect_intersect(rect, text_bbox); + if (rect.p.x > rect.q.x || rect.p.y > rect.q.y) { + penum->index += pstr->size; + goto finish; + } + } + } else { + /* We have no penum->pcpath. */ + } + + /* + * Note that pdf_update_text_state sets all the members of ppts->values + * to their current values. + */ + code = pdf_update_text_state(ppts, penum, pdfont, pfmat); + if (code > 0) { + /* Try not to emulate ADD_TO_WIDTH if we don't have to. */ + if (code & TEXT_ADD_TO_SPACE_WIDTH) { + if (!memchr(pstr->data, penum->text.space.s_char, pstr->size)) + code &= ~TEXT_ADD_TO_SPACE_WIDTH; + } + } + if (code < 0) + return code; + mask = code; + + if (text->operation & TEXT_REPLACE_WIDTHS) + mask |= TEXT_REPLACE_WIDTHS; + + /* + * The only operations left to handle are TEXT_DO_DRAW and + * TEXT_RETURN_WIDTH. + */ + if (mask == 0) { + /* + * If any character has real_width != Width, we have to process + * the string character-by-character. process_text_return_width + * will tell us what we need to know. + */ + if (!(text->operation & (TEXT_DO_DRAW | TEXT_RETURN_WIDTH))) + return 0; + code = process_text_return_width(penum, font, ppts, + (gs_const_string *)pstr, gdata, + &width_pt, &accepted); + if (code < 0) + return code; + if (code == 0) { + /* No characters with redefined widths -- the fast case. */ + if (text->operation & TEXT_DO_DRAW || penum->pis->text_rendering_mode == 3) { + code = pdf_append_chars(pdev, pstr->data, accepted, + width_pt.x, width_pt.y, false); + if (code < 0) + return code; + adjust_first_last_char(pdfont, pstr->data, accepted); + penum->index += accepted; + } else if (text->operation & TEXT_DO_NONE) + penum->index += accepted; + } else { + /* Use the slow case. Set mask to any non-zero value. */ + mask = TEXT_RETURN_WIDTH; + } + } + if (mask) { + /* process_text_modify_width destroys text parameters, save them now. */ + int index0 = penum->index, xy_index = penum->xy_index; + gs_text_params_t text = penum->text; + int xy_index_step = (!(penum->text.operation & TEXT_REPLACE_WIDTHS) ? 0 : + penum->text.x_widths == penum->text.y_widths ? 2 : 1); + + if (penum->text.operation & TEXT_REPLACE_WIDTHS) { + if (penum->text.x_widths != NULL) + penum->text.x_widths += xy_index * xy_index_step; + if (penum->text.y_widths != NULL) + penum->text.y_widths += xy_index * xy_index_step; + } + penum->xy_index = 0; + code = process_text_modify_width(penum, (gs_font *)font, ppts, + (gs_const_string *)pstr, + &width_pt, gdata, false); + if (penum->text.operation & TEXT_REPLACE_WIDTHS) { + if (penum->text.x_widths != NULL) + penum->text.x_widths -= xy_index * xy_index_step; + if (penum->text.y_widths != NULL) + penum->text.y_widths -= xy_index * xy_index_step; + } + penum->xy_index += xy_index; + adjust_first_last_char(pdfont, pstr->data, penum->index); + penum->text = text; + penum->index += index0; + if (code < 0) + return code; + } + +finish: + /* Finally, return the total width if requested. */ + if (!(text->operation & TEXT_RETURN_WIDTH)) + return 0; + if (text->operation & TEXT_DO_NONE) { + /* stringwidth needs to transform to user space. */ + gs_point p; + + gs_distance_transform_inverse(width_pt.x, width_pt.y, &ctm_only(penum->pis), &p); + penum->returned.total_width.x += p.x; + penum->returned.total_width.y += p.y; + } else + penum->returned.total_width = width_pt; + return pdf_shift_text_currentpoint(penum, &width_pt); +} + +/* + * Get the widths (unmodified and possibly modified) of a given character + * in a simple font. May add the widths to the widths cache (pdfont->Widths + * and pdf_font_cache_elem::real_widths). Return 1 if the widths were not cached. + */ +static int +pdf_char_widths(gx_device_pdf *const pdev, + pdf_font_resource_t *pdfont, int ch, gs_font_base *font, + pdf_glyph_widths_t *pwidths /* may be NULL */) +{ + pdf_glyph_widths_t widths; + int code; + byte *glyph_usage; + double *real_widths; + int char_cache_size, width_cache_size; + pdf_font_resource_t *pdfont1; + + code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont1, + &glyph_usage, &real_widths, &char_cache_size, &width_cache_size); + if (code < 0) + return code; + if (pdfont1 != pdfont) + return_error(gs_error_unregistered); /* Must not happen. */ + if (ch < 0 || ch > 255) + return_error(gs_error_rangecheck); + if (ch >= width_cache_size) + return_error(gs_error_unregistered); /* Must not happen. */ + if (pwidths == 0) + pwidths = &widths; + if (font->FontType != ft_user_defined && real_widths[ch] == 0) { + /* Might be an unused char, or just not cached. */ + gs_glyph glyph = pdfont->u.simple.Encoding[ch].glyph; + + code = pdf_glyph_widths(pdfont, font->WMode, glyph, (gs_font *)font, pwidths, NULL); + if (code < 0) + return code; + if (font->WMode != 0 && code > 0 && !pwidths->replaced_v) { + /* + * The font has no Metrics2, so it must write + * horizontally due to PS spec. + * Therefore we need to fill the Widths array, + * which is required by PDF spec. + * Take it from WMode==0. + */ + code = pdf_glyph_widths(pdfont, 0, glyph, (gs_font *)font, pwidths, NULL); + } + if (pwidths->replaced_v) { + pdfont->u.simple.v[ch].x = pwidths->real_width.v.x - pwidths->Width.v.x; + pdfont->u.simple.v[ch].y = pwidths->real_width.v.y - pwidths->Width.v.y; + } else + pdfont->u.simple.v[ch].x = pdfont->u.simple.v[ch].y = 0; + if (code == 0) { + pdfont->Widths[ch] = pwidths->Width.w; + real_widths[ch] = pwidths->real_width.w; + } + } else { + if (font->FontType == ft_user_defined) { + if (!(pdfont->used[ch >> 3] & 0x80 >> (ch & 7))) + return gs_error_undefined; /* The charproc was not accumulated. */ + if (!pdev->charproc_just_accumulated && + !(pdfont->u.simple.s.type3.cached[ch >> 3] & 0x80 >> (ch & 7))) { + /* The charproc uses setcharwidth. + Need to accumulate again to check for a glyph variation. */ + return gs_error_undefined; + } + } + pwidths->Width.w = pdfont->Widths[ch]; + pwidths->Width.v = pdfont->u.simple.v[ch]; + if (font->FontType == ft_user_defined) { + pwidths->real_width.w = real_widths[ch * 2]; + pwidths->Width.xy.x = pwidths->Width.w; + pwidths->Width.xy.y = 0; + pwidths->real_width.xy.x = real_widths[ch * 2 + 0]; + pwidths->real_width.xy.y = real_widths[ch * 2 + 1]; + pwidths->replaced_v = 0; + } else if (font->WMode) { + pwidths->real_width.w = real_widths[ch]; + pwidths->Width.xy.x = 0; + pwidths->Width.xy.y = pwidths->Width.w; + pwidths->real_width.xy.x = 0; + pwidths->real_width.xy.y = pwidths->real_width.w; + } else { + pwidths->real_width.w = real_widths[ch]; + pwidths->Width.xy.x = pwidths->Width.w; + pwidths->Width.xy.y = 0; + pwidths->real_width.xy.x = pwidths->real_width.w; + pwidths->real_width.xy.y = 0; + } + code = 0; + } + return code; +} + +/* + * Convert glyph widths (.Width.xy and .real_widths.xy) from design to PDF text space + * Zero-out one of Width.xy.x/y per PDF Ref 5.3.3 "Text Space Details" + */ +static void +pdf_char_widths_to_uts(pdf_font_resource_t *pdfont /* may be NULL for non-Type3 */, + pdf_glyph_widths_t *pwidths) +{ + if (pdfont && pdfont->FontType == ft_user_defined) { + gs_matrix *pmat = &pdfont->u.simple.s.type3.FontMatrix; + + pwidths->Width.xy.x *= pmat->xx; /* formula simplified based on wy in glyph space == 0 */ + pwidths->Width.xy.y = 0.0; /* WMode == 0 for PDF Type 3 fonts */ + gs_distance_transform(pwidths->real_width.xy.x, pwidths->real_width.xy.y, pmat, &pwidths->real_width.xy); + } else { + /* + * For other font types: + * - PDF design->text space is a simple scaling by 0.001. + * - The Width.xy.x/y that should be zeroed-out per 5.3.3 "Text Space Details" is already 0. + */ + pwidths->Width.xy.x /= 1000.0; + pwidths->Width.xy.y /= 1000.0; + pwidths->real_width.xy.x /= 1000.0; + pwidths->real_width.xy.y /= 1000.0; + } +} + +/* + * Compute the total text width (in user space). Return 1 if any + * character had real_width != Width, otherwise 0. + */ +static int +process_text_return_width(const pdf_text_enum_t *pte, gs_font_base *font, + pdf_text_process_state_t *ppts, + const gs_const_string *pstr, const gs_glyph *gdata, + gs_point *pdpt, int *accepted) +{ + int i; + gs_point w; + gs_point dpt; + int num_spaces = 0; + int space_char = + (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ? + pte->text.space.s_char : -1); + int widths_differ = 0, code; + gx_device_pdf *pdev = (gx_device_pdf *)pte->dev; + pdf_font_resource_t *pdfont; + + code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont, NULL, NULL, NULL, NULL); + if (code < 0) + return code; + for (i = 0, w.x = w.y = 0; i < pstr->size; ++i) { + pdf_glyph_widths_t cw; /* in PDF text space */ + gs_char ch = pstr->data[i]; + + { const gs_glyph *gdata_i = (gdata != NULL ? gdata + i : 0); + + code = pdf_encode_string_element(pdev, (gs_font *)font, pdfont, ch, gdata_i); + if (code < 0) + return code; + } + if (font->FontType == ft_user_defined && + (i > 0 || !pdev->charproc_just_accumulated) && + !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7)))) + code = gs_error_undefined; + else + code = pdf_char_widths((gx_device_pdf *)pte->dev, + ppts->values.pdfont, ch, font, + &cw); + if (code < 0) { + if (i) + break; + *accepted = 0; + return code; + } + pdf_char_widths_to_uts(pdfont, &cw); + w.x += cw.real_width.xy.x; + w.y += cw.real_width.xy.y; + if (cw.real_width.xy.x != cw.Width.xy.x || + cw.real_width.xy.y != cw.Width.xy.y + ) + widths_differ = 1; + if (pstr->data[i] == space_char) + ++num_spaces; + } + *accepted = i; + gs_distance_transform(w.x * ppts->values.size, w.y * ppts->values.size, + &ppts->values.matrix, &dpt); + if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) { + int num_chars = *accepted; + gs_point tpt; + + gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y, + &ctm_only(pte->pis), &tpt); + dpt.x += tpt.x * num_chars; + dpt.y += tpt.y * num_chars; + } + if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) { + gs_point tpt; + + gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y, + &ctm_only(pte->pis), &tpt); + dpt.x += tpt.x * num_spaces; + dpt.y += tpt.y * num_spaces; + } + *pdpt = dpt; + + return widths_differ; +} + +#define RIGHT_SBW 1 /* Old code = 0, new code = 1. */ +#if !RIGHT_SBW +/* + * Retrieve glyph origing shift for WMode = 1 in design units. + */ +static void +pdf_glyph_origin(pdf_font_resource_t *pdfont, int ch, int WMode, gs_point *p) +{ + /* For CID fonts PDF viewers provide glyph origin shift automatically. + * Therefore we only need to do for non-CID fonts. + */ + switch (pdfont->FontType) { + case ft_encrypted: + case ft_encrypted2: + case ft_TrueType: + case ft_user_defined: + *p = pdfont->u.simple.v[ch]; + break; + default: + p->x = p->y = 0; + break; + } +} +#endif + +/* + * Emulate TEXT_ADD_TO_ALL_WIDTHS and/or TEXT_ADD_TO_SPACE_WIDTH, + * and implement TEXT_REPLACE_WIDTHS if requested. + * Uses and updates ppts->values.matrix; uses ppts->values.pdfont. + * + * Destroys the text parameters in *pte. + * The caller must restore them. + */ +int +process_text_modify_width(pdf_text_enum_t *pte, gs_font *font, + pdf_text_process_state_t *ppts, + const gs_const_string *pstr, + gs_point *pdpt, const gs_glyph *gdata, bool composite) +{ + gx_device_pdf *const pdev = (gx_device_pdf *)pte->dev; + int space_char = + (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ? + pte->text.space.s_char : -1); + gs_point start, total; + pdf_font_resource_t *pdfont3 = NULL; + + if (font->FontType == ft_user_defined) + pdf_attached_font_resource(pdev, font, &pdfont3, NULL, NULL, NULL, NULL); + pte->text.data.bytes = pstr->data; + pte->text.size = pstr->size; + pte->index = 0; + pte->text.operation &= ~TEXT_FROM_ANY; + pte->text.operation |= TEXT_FROM_STRING; + start.x = ppts->values.matrix.tx; + start.y = ppts->values.matrix.ty; + total.x = total.y = 0; /* user space */ + /* + * Note that character widths are in design space, but text.delta_* + * values and the width value returned in *pdpt are in user space, + * and the width values for pdf_append_chars are in device space. + */ + for (;;) { + pdf_glyph_widths_t cw; /* design space, then converted to PDF text space */ + gs_point did, wanted, tpt; /* user space */ + gs_point v = {0, 0}; /* design space */ + gs_char chr; + gs_glyph glyph; + int code, index = pte->index; + gs_text_enum_t pte1 = *(gs_text_enum_t *)pte; + int FontType; +#if RIGHT_SBW + bool use_cached_v = true; +#endif + byte composite_type3_text[1]; + + code = pte1.orig_font->procs.next_char_glyph(&pte1, &chr, &glyph); + if (code == 2) { /* end of string */ + gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true); + break; + } + if (code < 0) + return code; + if (composite) { /* from process_cmap_text */ + gs_font *subfont = pte1.fstack.items[pte1.fstack.depth].font; + + if (subfont->FontType == ft_user_defined) { + pdf_font_resource_t *pdfont; + + FontType = subfont->FontType; + code = pdf_attached_font_resource(pdev, subfont, + &pdfont, NULL, NULL, NULL, NULL); + if (code < 0) + return code; + chr = pdf_find_glyph(pdfont, glyph); + composite_type3_text[0] = (byte)chr; + code = pdf_char_widths((gx_device_pdf *)pte->dev, + ppts->values.pdfont, chr, (gs_font_base *)subfont, + &cw); + } else { + pdf_font_resource_t *pdsubf = ppts->values.pdfont->u.type0.DescendantFont; + + FontType = pdsubf->FontType; + code = pdf_glyph_widths(pdsubf, font->WMode, glyph, subfont, &cw, + pte->cdevproc_callout ? pte->cdevproc_result : NULL); + } + } else {/* must be a base font */ + const gs_glyph *gdata_i = (gdata != NULL ? gdata + pte->index : 0); + + /* gdata is NULL when composite == true, or the text isn't a single byte. */ + code = pdf_encode_string_element(pdev, font, ppts->values.pdfont, chr, gdata_i); + FontType = font->FontType; + if (code >= 0) { + if (chr == GS_NO_CHAR && glyph != GS_NO_GLYPH) { + /* glyphshow, we have no char code. Bug 686988.*/ + code = pdf_glyph_widths(ppts->values.pdfont, font->WMode, glyph, font, &cw, NULL); + use_cached_v = false; /* Since we have no chr and don't call pdf_char_widths. */ + } else { + code = pdf_char_widths((gx_device_pdf *)pte->dev, + ppts->values.pdfont, chr, (gs_font_base *)font, + &cw); + } + } + } + if (code < 0) { + if (index > 0) + break; + return code; + } + gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true); +#if RIGHT_SBW + if (composite || !use_cached_v) { + if (cw.replaced_v) { + v.x = cw.real_width.v.x - cw.Width.v.x; + v.y = cw.real_width.v.y - cw.Width.v.y; + } + } else + v = ppts->values.pdfont->u.simple.v[chr]; + if (font->WMode) { + /* With WMode 1 v-vector is (WMode 1 origin) - (WMode 0 origin). + The glyph shifts in the opposite direction. */ + v.x = - v.x; + v.y = - v.y; + } else { + /* With WMode 0 v-vector is (Metrics sb) - (native sb). + The glyph shifts in same direction. */ + } + /* pdf_glyph_origin is not longer used. */ +#else + if ((pte->text.operation & TEXT_FROM_SINGLE_GLYPH) || + (pte->text.operation & TEXT_FROM_GLYPHS)) { + v.x = v.y = 0; + } else if (composite) { + if (cw.replaced_v) { + v.x = cw.real_width.v.x - cw.Width.v.x; + v.y = cw.real_width.v.y - cw.Width.v.y; + } + } else + pdf_glyph_origin(ppts->values.pdfont, chr, font->WMode, &v); +#endif + if (v.x != 0 || v.y != 0) { + gs_point glyph_origin_shift; + double scale0; + + if (FontType == ft_TrueType || FontType == ft_CID_TrueType) + scale0 = (float)0.001; + else + scale0 = 1; +#if RIGHT_SBW + glyph_origin_shift.x = v.x * scale0; + glyph_origin_shift.y = v.y * scale0; +#else + glyph_origin_shift.x = - v.x * scale0; + glyph_origin_shift.y = - v.y * scale0; +#endif + if (composite) { + gs_font *subfont = pte->fstack.items[pte->fstack.depth].font; + + gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y, + &subfont->FontMatrix, &glyph_origin_shift); + } + gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y, + &font->FontMatrix, &glyph_origin_shift); + gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y, + &ctm_only(pte->pis), &glyph_origin_shift); + if (glyph_origin_shift.x != 0 || glyph_origin_shift.y != 0) { + ppts->values.matrix.tx = start.x + total.x + glyph_origin_shift.x; + ppts->values.matrix.ty = start.y + total.y + glyph_origin_shift.y; + code = pdf_set_text_state_values(pdev, &ppts->values); + if (code < 0) + break; + } + } + pdf_char_widths_to_uts(pdfont3, &cw); /* convert design->text space */ + if (pte->text.operation & TEXT_DO_DRAW) { + gs_distance_transform(cw.Width.xy.x * ppts->values.size, + cw.Width.xy.y * ppts->values.size, + &ppts->values.matrix, &did); + gs_distance_transform((font->WMode ? 0 : ppts->values.character_spacing), + (font->WMode ? ppts->values.character_spacing : 0), + &ppts->values.matrix, &tpt); + did.x += tpt.x; + did.y += tpt.y; + if (chr == space_char) { + gs_distance_transform((font->WMode ? 0 : ppts->values.word_spacing), + (font->WMode ? ppts->values.word_spacing : 0), + &ppts->values.matrix, &tpt); + did.x += tpt.x; + did.y += tpt.y; + } + if (composite && FontType == ft_user_defined) + code = pdf_append_chars(pdev, composite_type3_text, 1, did.x, did.y, composite); + else + code = pdf_append_chars(pdev, pstr->data + index, pte->index - index, did.x, did.y, composite); + if (code < 0) + break; + } else + did.x = did.y = 0; + if (pte->text.operation & TEXT_REPLACE_WIDTHS) { + gs_point dpt; + + code = gs_text_replaced_width(&pte->text, pte->xy_index++, &dpt); + if (code < 0) + return_error(gs_error_unregistered); + gs_distance_transform(dpt.x, dpt.y, &ctm_only(pte->pis), &wanted); + } else { + gs_distance_transform(cw.real_width.xy.x * ppts->values.size, + cw.real_width.xy.y * ppts->values.size, + &ppts->values.matrix, &wanted); + if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) { + gs_distance_transform(pte->text.delta_all.x, + pte->text.delta_all.y, + &ctm_only(pte->pis), &tpt); + wanted.x += tpt.x; + wanted.y += tpt.y; + } + if (chr == space_char && pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) { + gs_distance_transform(pte->text.delta_space.x, + pte->text.delta_space.y, + &ctm_only(pte->pis), &tpt); + wanted.x += tpt.x; + wanted.y += tpt.y; + } + } + total.x += wanted.x; + total.y += wanted.y; + if (wanted.x != did.x || wanted.y != did.y) { + ppts->values.matrix.tx = start.x + total.x; + ppts->values.matrix.ty = start.y + total.y; + code = pdf_set_text_state_values(pdev, &ppts->values); + if (code < 0) + break; + } + pdev->charproc_just_accumulated = false; + } + *pdpt = total; + return 0; +} + +/* + * Get character code from a glyph code. + * An usage of this function is very undesirable, + * because a glyph may be unlisted in Encoding. + */ +int +pdf_encode_glyph(gs_font_base *bfont, gs_glyph glyph0, + byte *buf, int buf_size, int *char_code_length) +{ + gs_char c; + + *char_code_length = 1; + if (*char_code_length > buf_size) + return_error(gs_error_rangecheck); /* Must not happen. */ + for (c = 0; c < 255; c++) { + gs_glyph glyph1 = bfont->procs.encode_char((gs_font *)bfont, c, + GLYPH_SPACE_NAME); + if (glyph1 == glyph0) { + buf[0] = (byte)c; + return 0; + } + } + return_error(gs_error_rangecheck); /* Can't encode. */ +} + +/* ---------------- Type 1 or TrueType font ---------------- */ + +/* + * Process a text string in a simple font. + */ +int +process_plain_text(gs_text_enum_t *pte, void *vbuf, uint bsize) +{ + byte *const buf = vbuf; + uint count; + uint operation = pte->text.operation; + pdf_text_enum_t *penum = (pdf_text_enum_t *)pte; + int code; + gs_string str; + pdf_text_process_state_t text_state; + const gs_glyph *gdata = NULL; + + if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) { + count = pte->text.size - pte->index; + if (bsize < count) + return_error(gs_error_unregistered); /* Must not happen. */ + memcpy(buf, (const byte *)pte->text.data.bytes + pte->index, count); + } else if (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)) { + /* Check that all chars fit in a single byte. */ + const gs_char *cdata; + int i; + + if (operation & TEXT_FROM_CHARS) { + cdata = pte->text.data.chars; + count = (pte->text.size - pte->index); + } else { + cdata = &pte->text.data.d_char; + count = 1; + } + if (bsize < count * sizeof(gs_char)) + return_error(gs_error_unregistered); /* Must not happen. */ + for (i = 0; i < count; ++i) { + gs_char chr = cdata[pte->index + i]; + + if (chr & ~0xff) + return_error(gs_error_rangecheck); + buf[i] = (byte)chr; + } + } else if (operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH)) { + /* + * Since PDF has no analogue of 'glyphshow', + * we try to encode glyphs with the current + * font's encoding. If the current font has no encoding, + * or the encoding doesn't contain necessary glyphs, + * the text will be represented with a Type 3 font with + * bitmaps or outlines. + * + * When we fail with encoding (136-01.ps is an example), + * we could locate a PDF font resource or create a new one + * with same outlines and an appropriate encoding. + * Also we could change .notdef entries in the + * copied font (assuming that document designer didn't use + * .notdef for a meanful printing). + * fixme: Not implemented yet. + */ + gs_font *font = pte->current_font; + uint size; + int i; + + if (operation & TEXT_FROM_GLYPHS) { + gdata = pte->text.data.glyphs; + size = pte->text.size - pte->index; + } else { + gdata = &pte->text.data.d_glyph; + size = 1; + } + if (!pdf_is_simple_font(font)) + return_error(gs_error_unregistered); /* Must not happen. */ + count = 0; + for (i = 0; i < size; ++i) { + gs_glyph glyph = gdata[pte->index + i]; + int char_code_length; + + code = pdf_encode_glyph((gs_font_base *)font, glyph, + buf + count, size - count, &char_code_length); + if (code < 0) + break; + count += char_code_length; + if (operation & TEXT_INTERVENE) + break; /* Just do one character. */ + } + if (i < size) { + pdf_font_resource_t *pdfont; + + str.data = buf; + str.size = size; + code = pdf_obtain_font_resource_unencoded(penum, &str, &pdfont, gdata); + if (code < 0) { + /* + * pdf_text_process will fall back + * to default implementation. + */ + return code; + } + count = size; + } + /* So far we will use TEXT_FROM_STRING instead + TEXT_FROM_*_GLYPH*. Since we used a single + byte encoding, the character index appears invariant + during this substitution. + */ + } else + return_error(gs_error_rangecheck); + str.data = buf; + if (count > 1 && (operation & TEXT_INTERVENE)) { + /* Just do one character. */ + str.size = 1; + code = pdf_process_string_aux(penum, &str, gdata, NULL, &text_state); + if (code >= 0) { + pte->returned.current_char = buf[0]; + code = TEXT_PROCESS_INTERVENE; + } + } else { + str.size = count; + code = pdf_process_string_aux(penum, &str, gdata, NULL, &text_state); + } + return code; +} |