xref: /plan9/sys/src/cmd/gs/src/gdevpdte.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1*593dc095SDavid du Colombier /* Copyright (C) 2002-2004 artofcode LLC. All rights reserved.
2*593dc095SDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
5*593dc095SDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
9*593dc095SDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15*593dc095SDavid du Colombier */
16*593dc095SDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gdevpdte.c,v 1.70 2005/03/03 13:15:55 igor Exp $ */
18*593dc095SDavid du Colombier /* Encoding-based (Type 1/2/42) text processing for pdfwrite. */
19*593dc095SDavid du Colombier 
20*593dc095SDavid du Colombier #include "math_.h"
21*593dc095SDavid du Colombier #include "memory_.h"
22*593dc095SDavid du Colombier #include "gx.h"
23*593dc095SDavid du Colombier #include "gserrors.h"
24*593dc095SDavid du Colombier #include "gsutil.h"
25*593dc095SDavid du Colombier #include "gxfcmap.h"
26*593dc095SDavid du Colombier #include "gxfcopy.h"
27*593dc095SDavid du Colombier #include "gxfont.h"
28*593dc095SDavid du Colombier #include "gxfont0.h"
29*593dc095SDavid du Colombier #include "gxfont0c.h"
30*593dc095SDavid du Colombier #include "gxpath.h"		/* for getting current point */
31*593dc095SDavid du Colombier #include "gdevpsf.h"
32*593dc095SDavid du Colombier #include "gdevpdfx.h"
33*593dc095SDavid du Colombier #include "gdevpdfg.h"
34*593dc095SDavid du Colombier #include "gdevpdtx.h"
35*593dc095SDavid du Colombier #include "gdevpdtd.h"
36*593dc095SDavid du Colombier #include "gdevpdtf.h"
37*593dc095SDavid du Colombier #include "gdevpdts.h"
38*593dc095SDavid du Colombier #include "gdevpdtt.h"
39*593dc095SDavid du Colombier 
40*593dc095SDavid du Colombier private int pdf_char_widths(gx_device_pdf *const pdev,
41*593dc095SDavid du Colombier 			    pdf_font_resource_t *pdfont, int ch,
42*593dc095SDavid du Colombier 			    gs_font_base *font,
43*593dc095SDavid du Colombier 			    pdf_glyph_widths_t *pwidths /* may be NULL */);
44*593dc095SDavid du Colombier private int pdf_encode_string(gx_device_pdf *pdev, pdf_text_enum_t *penum,
45*593dc095SDavid du Colombier 		  const gs_string *pstr, const gs_glyph *gdata,
46*593dc095SDavid du Colombier 		  pdf_font_resource_t **ppdfont);
47*593dc095SDavid du Colombier private int pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr,
48*593dc095SDavid du Colombier 			       pdf_font_resource_t *pdfont,
49*593dc095SDavid du Colombier 			       const gs_matrix *pfmat,
50*593dc095SDavid du Colombier 			       pdf_text_process_state_t *ppts);
51*593dc095SDavid du Colombier 
52*593dc095SDavid du Colombier /*
53*593dc095SDavid du Colombier  * Encode and process a string with a simple gs_font.
54*593dc095SDavid du Colombier  */
55*593dc095SDavid du Colombier int
pdf_encode_process_string(pdf_text_enum_t * penum,gs_string * pstr,const gs_glyph * gdata,const gs_matrix * pfmat,pdf_text_process_state_t * ppts)56*593dc095SDavid du Colombier pdf_encode_process_string(pdf_text_enum_t *penum, gs_string *pstr,
57*593dc095SDavid du Colombier 			  const gs_glyph *gdata, const gs_matrix *pfmat,
58*593dc095SDavid du Colombier 			  pdf_text_process_state_t *ppts)
59*593dc095SDavid du Colombier {
60*593dc095SDavid du Colombier     gx_device_pdf *const pdev = (gx_device_pdf *)penum->dev;
61*593dc095SDavid du Colombier     pdf_font_resource_t *pdfont;
62*593dc095SDavid du Colombier     gs_font_base *font;
63*593dc095SDavid du Colombier     int code = 0;
64*593dc095SDavid du Colombier 
65*593dc095SDavid du Colombier     switch (penum->current_font->FontType) {
66*593dc095SDavid du Colombier     case ft_TrueType:
67*593dc095SDavid du Colombier     case ft_encrypted:
68*593dc095SDavid du Colombier     case ft_encrypted2:
69*593dc095SDavid du Colombier     case ft_user_defined:
70*593dc095SDavid du Colombier 	break;
71*593dc095SDavid du Colombier     default:
72*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
73*593dc095SDavid du Colombier     }
74*593dc095SDavid du Colombier     font = (gs_font_base *)penum->current_font;
75*593dc095SDavid du Colombier 
76*593dc095SDavid du Colombier     code = pdf_encode_string(pdev, penum, pstr, gdata, &pdfont); /* Must not change penum. */
77*593dc095SDavid du Colombier     if (code < 0)
78*593dc095SDavid du Colombier 	return code;
79*593dc095SDavid du Colombier     return pdf_process_string(penum, pstr, pdfont, pfmat, ppts);
80*593dc095SDavid du Colombier }
81*593dc095SDavid du Colombier 
82*593dc095SDavid du Colombier /*
83*593dc095SDavid du Colombier  * Add char code pair to ToUnicode CMap,
84*593dc095SDavid du Colombier  * creating the CMap on neccessity.
85*593dc095SDavid du Colombier  */
86*593dc095SDavid du Colombier int
pdf_add_ToUnicode(gx_device_pdf * pdev,gs_font * font,pdf_font_resource_t * pdfont,gs_glyph glyph,gs_char ch)87*593dc095SDavid du Colombier pdf_add_ToUnicode(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont,
88*593dc095SDavid du Colombier 		  gs_glyph glyph, gs_char ch)
89*593dc095SDavid du Colombier {   int code;
90*593dc095SDavid du Colombier     gs_char unicode;
91*593dc095SDavid du Colombier 
92*593dc095SDavid du Colombier     if (glyph == GS_NO_GLYPH)
93*593dc095SDavid du Colombier 	return 0;
94*593dc095SDavid du Colombier     unicode = font->procs.decode_glyph((gs_font *)font, glyph);
95*593dc095SDavid du Colombier     if (unicode != GS_NO_CHAR) {
96*593dc095SDavid du Colombier 	if (pdfont->cmap_ToUnicode == NULL) {
97*593dc095SDavid du Colombier 	    uint num_codes = 256, key_size = 1;
98*593dc095SDavid du Colombier 
99*593dc095SDavid du Colombier 	    if (font->FontType == ft_CID_encrypted) {
100*593dc095SDavid du Colombier 		gs_font_cid0 *pfcid = (gs_font_cid0 *)font;
101*593dc095SDavid du Colombier 
102*593dc095SDavid du Colombier 		num_codes = pfcid->cidata.common.CIDCount;
103*593dc095SDavid du Colombier 		key_size = 2;
104*593dc095SDavid du Colombier 	    } else if (font->FontType == ft_CID_TrueType) {
105*593dc095SDavid du Colombier #if 0
106*593dc095SDavid du Colombier 		gs_font_cid2 *pfcid = (gs_font_cid2 *)font;
107*593dc095SDavid du Colombier 
108*593dc095SDavid du Colombier 		num_codes = pfcid->cidata.common.CIDCount;
109*593dc095SDavid du Colombier #else
110*593dc095SDavid du Colombier 		/* Since PScript5.dll creates GlyphNames2Unicode with character codes
111*593dc095SDavid du Colombier 		   instead CIDs, and with the WinCharSetFFFF-H2 CMap
112*593dc095SDavid du Colombier 		   character codes appears from the range 0-0xFFFF (Bug 687954),
113*593dc095SDavid du Colombier 		   we must use the maximal character code value for the ToUnicode
114*593dc095SDavid du Colombier 		   code count. */
115*593dc095SDavid du Colombier 		num_codes = 65536;
116*593dc095SDavid du Colombier #endif
117*593dc095SDavid du Colombier 		key_size = 2;
118*593dc095SDavid du Colombier 	    }
119*593dc095SDavid du Colombier 	    code = gs_cmap_ToUnicode_alloc(pdev->pdf_memory, pdfont->rid, num_codes, key_size,
120*593dc095SDavid du Colombier 					    &pdfont->cmap_ToUnicode);
121*593dc095SDavid du Colombier 	    if (code < 0)
122*593dc095SDavid du Colombier 		return code;
123*593dc095SDavid du Colombier 	}
124*593dc095SDavid du Colombier 	if (pdfont->cmap_ToUnicode != NULL)
125*593dc095SDavid du Colombier 	    gs_cmap_ToUnicode_add_pair(pdfont->cmap_ToUnicode, ch, unicode);
126*593dc095SDavid du Colombier     }
127*593dc095SDavid du Colombier     return 0;
128*593dc095SDavid du Colombier }
129*593dc095SDavid du Colombier 
130*593dc095SDavid du Colombier /*
131*593dc095SDavid du Colombier  * If the current substream is a charproc, register a font used in it.
132*593dc095SDavid du Colombier  */
133*593dc095SDavid du Colombier int
pdf_register_charproc_resource(gx_device_pdf * pdev,gs_id id,pdf_resource_type_t type)134*593dc095SDavid du Colombier pdf_register_charproc_resource(gx_device_pdf *pdev, gs_id id, pdf_resource_type_t type)
135*593dc095SDavid du Colombier {
136*593dc095SDavid du Colombier     if (pdev->font3 != 0) {
137*593dc095SDavid du Colombier 	pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pdev->font3;
138*593dc095SDavid du Colombier 	pdf_resource_ref_t *used_resources = pdfont->u.simple.s.type3.used_resources;
139*593dc095SDavid du Colombier 	int i, used_resources_count = pdfont->u.simple.s.type3.used_resources_count;
140*593dc095SDavid du Colombier 	int used_resources_max = pdfont->u.simple.s.type3.used_resources_max;
141*593dc095SDavid du Colombier 
142*593dc095SDavid du Colombier 	for (i = 0; i < used_resources_count; i++)
143*593dc095SDavid du Colombier 	    if (used_resources[i].id == id && used_resources[i].type == type)
144*593dc095SDavid du Colombier 		return 0;
145*593dc095SDavid du Colombier 	if (used_resources_count >= used_resources_max) {
146*593dc095SDavid du Colombier 	    used_resources_max += 10;
147*593dc095SDavid du Colombier 	    used_resources = (pdf_resource_ref_t *)gs_alloc_bytes(pdev->pdf_memory,
148*593dc095SDavid du Colombier 			sizeof(pdf_resource_ref_t) * used_resources_max,
149*593dc095SDavid du Colombier 			"pdf_register_charproc_resource");
150*593dc095SDavid du Colombier 	    if (!used_resources)
151*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
152*593dc095SDavid du Colombier 	    if (used_resources_count) {
153*593dc095SDavid du Colombier 		memcpy(used_resources, pdfont->u.simple.s.type3.used_resources,
154*593dc095SDavid du Colombier 		    sizeof(pdf_resource_ref_t) * used_resources_count);
155*593dc095SDavid du Colombier 		gs_free_object(pdev->pdf_memory, pdfont->u.simple.s.type3.used_resources,
156*593dc095SDavid du Colombier 			"pdf_register_charproc_resource");
157*593dc095SDavid du Colombier 	    }
158*593dc095SDavid du Colombier 	    pdfont->u.simple.s.type3.used_resources = used_resources;
159*593dc095SDavid du Colombier 	    pdfont->u.simple.s.type3.used_resources_max = used_resources_max;
160*593dc095SDavid du Colombier 	}
161*593dc095SDavid du Colombier 	used_resources[used_resources_count].id = id;
162*593dc095SDavid du Colombier 	used_resources[used_resources_count].type = type;
163*593dc095SDavid du Colombier 	pdfont->u.simple.s.type3.used_resources_count = used_resources_count + 1;
164*593dc095SDavid du Colombier     }
165*593dc095SDavid du Colombier     return 0;
166*593dc095SDavid du Colombier }
167*593dc095SDavid du Colombier 
168*593dc095SDavid du Colombier /*
169*593dc095SDavid du Colombier  * Register charproc fonts with the page or substream.
170*593dc095SDavid du Colombier  */
171*593dc095SDavid du Colombier int
pdf_used_charproc_resources(gx_device_pdf * pdev,pdf_font_resource_t * pdfont)172*593dc095SDavid du Colombier pdf_used_charproc_resources(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
173*593dc095SDavid du Colombier {
174*593dc095SDavid du Colombier     if (pdfont->where_used & pdev->used_mask)
175*593dc095SDavid du Colombier 	return 0;
176*593dc095SDavid du Colombier     pdfont->where_used |= pdev->used_mask;
177*593dc095SDavid du Colombier     if (pdfont->FontType == ft_user_defined) {
178*593dc095SDavid du Colombier 	pdf_resource_ref_t *used_resources = pdfont->u.simple.s.type3.used_resources;
179*593dc095SDavid du Colombier 	int i, used_resources_count = pdfont->u.simple.s.type3.used_resources_count;
180*593dc095SDavid du Colombier 
181*593dc095SDavid du Colombier 	for (i = 0; i < used_resources_count; i++) {
182*593dc095SDavid du Colombier 	    pdf_resource_t *pres =
183*593dc095SDavid du Colombier 		    pdf_find_resource_by_resource_id(pdev,
184*593dc095SDavid du Colombier 			    used_resources[i].type, used_resources[i].id);
185*593dc095SDavid du Colombier 	    if (pres == NULL)
186*593dc095SDavid du Colombier 		return_error(gs_error_unregistered); /* Must not happen. */
187*593dc095SDavid du Colombier 	    pres->where_used |= pdev->used_mask;
188*593dc095SDavid du Colombier 	}
189*593dc095SDavid du Colombier     }
190*593dc095SDavid du Colombier     return 0;
191*593dc095SDavid du Colombier }
192*593dc095SDavid du Colombier 
193*593dc095SDavid du Colombier /*
194*593dc095SDavid du Colombier  * Given a text string and a simple gs_font, return a font resource suitable
195*593dc095SDavid du Colombier  * for the text string, possibly re-encoding the string.  This
196*593dc095SDavid du Colombier  * may involve creating a font resource and/or adding glyphs and/or Encoding
197*593dc095SDavid du Colombier  * entries to it.
198*593dc095SDavid du Colombier  *
199*593dc095SDavid du Colombier  * Sets *ppdfont.
200*593dc095SDavid du Colombier  */
201*593dc095SDavid du Colombier private int
pdf_encode_string(gx_device_pdf * pdev,pdf_text_enum_t * penum,const gs_string * pstr,const gs_glyph * gdata,pdf_font_resource_t ** ppdfont)202*593dc095SDavid du Colombier pdf_encode_string(gx_device_pdf *pdev, pdf_text_enum_t *penum,
203*593dc095SDavid du Colombier 		  const gs_string *pstr, const gs_glyph *gdata,
204*593dc095SDavid du Colombier 		  pdf_font_resource_t **ppdfont)
205*593dc095SDavid du Colombier {
206*593dc095SDavid du Colombier     gs_font *font = (gs_font *)penum->current_font;
207*593dc095SDavid du Colombier     pdf_font_resource_t *pdfont = 0;
208*593dc095SDavid du Colombier     gs_font_base *cfont, *ccfont;
209*593dc095SDavid du Colombier     int code, i;
210*593dc095SDavid du Colombier 
211*593dc095SDavid du Colombier     /*
212*593dc095SDavid du Colombier      * In contradiction with pre-7.20 versions of pdfwrite,
213*593dc095SDavid du Colombier      * we never re-encode texts due to possible encoding conflict while font merging.
214*593dc095SDavid du Colombier      */
215*593dc095SDavid du Colombier     code = pdf_obtain_font_resource(penum, pstr, &pdfont);
216*593dc095SDavid du Colombier     if (code < 0)
217*593dc095SDavid du Colombier 	return code;
218*593dc095SDavid du Colombier     code = pdf_add_resource(pdev, pdev->substream_Resources, "/Font", (pdf_resource_t *)pdfont);
219*593dc095SDavid du Colombier     if (code < 0)
220*593dc095SDavid du Colombier 	return code;
221*593dc095SDavid du Colombier     code = pdf_register_charproc_resource(pdev, pdf_resource_id((pdf_resource_t *)pdfont), resourceFont);
222*593dc095SDavid du Colombier     if (code < 0)
223*593dc095SDavid du Colombier 	return code;
224*593dc095SDavid du Colombier     cfont = pdf_font_resource_font(pdfont, false);
225*593dc095SDavid du Colombier     ccfont = pdf_font_resource_font(pdfont, true);
226*593dc095SDavid du Colombier     for (i = 0; i < pstr->size; ++i) {
227*593dc095SDavid du Colombier 	int ch = pstr->data[i];
228*593dc095SDavid du Colombier 	pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
229*593dc095SDavid du Colombier 	gs_glyph glyph = (gdata == NULL
230*593dc095SDavid du Colombier 			    ? font->procs.encode_char(font, ch, GLYPH_SPACE_NAME)
231*593dc095SDavid du Colombier 			    : gdata[i]);
232*593dc095SDavid du Colombier 
233*593dc095SDavid du Colombier 	gs_glyph copied_glyph;
234*593dc095SDavid du Colombier 	gs_const_string gnstr;
235*593dc095SDavid du Colombier 
236*593dc095SDavid du Colombier 	if (glyph == GS_NO_GLYPH || glyph == pet->glyph)
237*593dc095SDavid du Colombier 	    continue;
238*593dc095SDavid du Colombier 	if (pet->glyph != GS_NO_GLYPH) { /* encoding conflict */
239*593dc095SDavid du Colombier 	    return_error(gs_error_rangecheck);
240*593dc095SDavid du Colombier 	    /* Must not happen because pdf_obtain_font_resource
241*593dc095SDavid du Colombier 	     * checks for encoding compatibility.
242*593dc095SDavid du Colombier 	     */
243*593dc095SDavid du Colombier 	}
244*593dc095SDavid du Colombier 	code = font->procs.glyph_name(font, glyph, &gnstr);
245*593dc095SDavid du Colombier 	if (code < 0)
246*593dc095SDavid du Colombier 	    return code;	/* can't get name of glyph */
247*593dc095SDavid du Colombier 	if (font->FontType != ft_user_defined) {
248*593dc095SDavid du Colombier 	    /* The standard 14 fonts don't have a FontDescriptor. */
249*593dc095SDavid du Colombier 	    code = (pdfont->base_font != 0 ?
250*593dc095SDavid du Colombier 		    pdf_base_font_copy_glyph(pdfont->base_font, glyph, (gs_font_base *)font) :
251*593dc095SDavid du Colombier 		    pdf_font_used_glyph(pdfont->FontDescriptor, glyph, (gs_font_base *)font));
252*593dc095SDavid du Colombier 	    if (code < 0 && code != gs_error_undefined)
253*593dc095SDavid du Colombier 		return code;
254*593dc095SDavid du Colombier 	    if (code == gs_error_undefined) {
255*593dc095SDavid du Colombier 		/* PS font has no such glyph. */
256*593dc095SDavid du Colombier 		if (bytes_compare(gnstr.data, gnstr.size, (const byte *)".notdef", 7)) {
257*593dc095SDavid du Colombier 		    pet->glyph = glyph;
258*593dc095SDavid du Colombier 		    pet->str = gnstr;
259*593dc095SDavid du Colombier 		    pet->is_difference = true;
260*593dc095SDavid du Colombier 		}
261*593dc095SDavid du Colombier 	    } else if (pdfont->base_font == NULL && ccfont != NULL &&
262*593dc095SDavid du Colombier 		    (gs_copy_glyph_options(font, glyph, (gs_font *)ccfont, COPY_GLYPH_NO_NEW) != 1 ||
263*593dc095SDavid du Colombier 		     gs_copied_font_add_encoding((gs_font *)ccfont, ch, glyph) < 0)) {
264*593dc095SDavid du Colombier 		/*
265*593dc095SDavid du Colombier 		 * The "complete" copy of the font appears incomplete
266*593dc095SDavid du Colombier 		 * due to incrementally added glyphs. Drop the "complete"
267*593dc095SDavid du Colombier 		 * copy now and continue with subset font only.
268*593dc095SDavid du Colombier 		 *
269*593dc095SDavid du Colombier 		 * Note that we need to add the glyph to the encoding of the
270*593dc095SDavid du Colombier 		 * "complete" font, because "PPI-ProPag 2.6.1.4 (archivePg)"
271*593dc095SDavid du Colombier 		 * creates multiple font copies with reduced encodings
272*593dc095SDavid du Colombier 		 * (we believe it is poorly designed),
273*593dc095SDavid du Colombier 		 * and we can merge the copies back to a single font (see Bug 686875).
274*593dc095SDavid du Colombier 		 * We also check whether the encoding is compatible.
275*593dc095SDavid du Colombier 		 * It must be compatible here due to the pdf_obtain_font_resource
276*593dc095SDavid du Colombier 		 * and ccfont logics, but we want to ensure for safety reason.
277*593dc095SDavid du Colombier 		 */
278*593dc095SDavid du Colombier 		ccfont = NULL;
279*593dc095SDavid du Colombier 		pdf_font_descriptor_drop_complete_font(pdfont->FontDescriptor);
280*593dc095SDavid du Colombier 	    }
281*593dc095SDavid du Colombier 	    /*
282*593dc095SDavid du Colombier 	     * We arbitrarily allow the first encoded character in a given
283*593dc095SDavid du Colombier 	     * position to determine the encoding associated with the copied
284*593dc095SDavid du Colombier 	     * font.
285*593dc095SDavid du Colombier 	     */
286*593dc095SDavid du Colombier 	    copied_glyph = cfont->procs.encode_char((gs_font *)cfont, ch,
287*593dc095SDavid du Colombier 						    GLYPH_SPACE_NAME);
288*593dc095SDavid du Colombier 	    if (glyph != copied_glyph &&
289*593dc095SDavid du Colombier 		gs_copied_font_add_encoding((gs_font *)cfont, ch, glyph) < 0
290*593dc095SDavid du Colombier 		)
291*593dc095SDavid du Colombier 		pet->is_difference = true;
292*593dc095SDavid du Colombier 	    pdfont->used[ch >> 3] |= 0x80 >> (ch & 7);
293*593dc095SDavid du Colombier 	}
294*593dc095SDavid du Colombier 	/*
295*593dc095SDavid du Colombier 	 * We always generate ToUnicode for simple fonts, because
296*593dc095SDavid du Colombier 	 * we can't detemine in advance, which glyphs the font actually uses.
297*593dc095SDavid du Colombier 	 * The decision about writing it out is deferred until pdf_write_font_resource.
298*593dc095SDavid du Colombier 	 */
299*593dc095SDavid du Colombier 	code = pdf_add_ToUnicode(pdev, font, pdfont, glyph, ch);
300*593dc095SDavid du Colombier 	if (code < 0)
301*593dc095SDavid du Colombier 	    return code;
302*593dc095SDavid du Colombier 	pet->glyph = glyph;
303*593dc095SDavid du Colombier 	pet->str = gnstr;
304*593dc095SDavid du Colombier     }
305*593dc095SDavid du Colombier     *ppdfont = pdfont;
306*593dc095SDavid du Colombier     return 0;
307*593dc095SDavid du Colombier }
308*593dc095SDavid du Colombier 
309*593dc095SDavid du Colombier /*
310*593dc095SDavid du Colombier  * Estimate text bbox.
311*593dc095SDavid du Colombier  */
312*593dc095SDavid du Colombier private 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)313*593dc095SDavid du Colombier process_text_estimate_bbox(pdf_text_enum_t *pte, gs_font_base *font,
314*593dc095SDavid du Colombier 			  const gs_const_string *pstr,
315*593dc095SDavid du Colombier 			  const gs_matrix *pfmat,
316*593dc095SDavid du Colombier 			  gs_rect *text_bbox, gs_point *pdpt)
317*593dc095SDavid du Colombier {
318*593dc095SDavid du Colombier     int i;
319*593dc095SDavid du Colombier     int space_char =
320*593dc095SDavid du Colombier 	(pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
321*593dc095SDavid du Colombier 	 pte->text.space.s_char : -1);
322*593dc095SDavid du Colombier     int WMode = font->WMode;
323*593dc095SDavid du Colombier     int code = 0;
324*593dc095SDavid du Colombier     gs_point total = {0, 0};
325*593dc095SDavid du Colombier     gs_fixed_point origin;
326*593dc095SDavid du Colombier     gs_matrix m;
327*593dc095SDavid du Colombier     int xy_index = pte->xy_index;
328*593dc095SDavid du Colombier 
329*593dc095SDavid du Colombier     if (font->FontBBox.p.x == font->FontBBox.q.x ||
330*593dc095SDavid du Colombier 	font->FontBBox.p.y == font->FontBBox.q.y)
331*593dc095SDavid du Colombier 	return_error(gs_error_undefined);
332*593dc095SDavid du Colombier     code = gx_path_current_point(pte->path, &origin);
333*593dc095SDavid du Colombier     if (code < 0)
334*593dc095SDavid du Colombier 	return code;
335*593dc095SDavid du Colombier     m = ctm_only(pte->pis);
336*593dc095SDavid du Colombier     m.tx = fixed2float(origin.x);
337*593dc095SDavid du Colombier     m.ty = fixed2float(origin.y);
338*593dc095SDavid du Colombier     gs_matrix_multiply(pfmat, &m, &m);
339*593dc095SDavid du Colombier     for (i = 0; i < pstr->size; ++i) {
340*593dc095SDavid du Colombier 	byte c = pstr->data[i];
341*593dc095SDavid du Colombier 	gs_rect bbox;
342*593dc095SDavid du Colombier 	gs_point wanted, tpt, p0, p1, p2, p3;
343*593dc095SDavid du Colombier 	gs_glyph glyph = font->procs.encode_char((gs_font *)font, c,
344*593dc095SDavid du Colombier 					GLYPH_SPACE_NAME);
345*593dc095SDavid du Colombier 	gs_glyph_info_t info;
346*593dc095SDavid du Colombier 	int code = font->procs.glyph_info((gs_font *)font, glyph, NULL,
347*593dc095SDavid du Colombier 					    GLYPH_INFO_WIDTH0 << WMode,
348*593dc095SDavid du Colombier 					    &info);
349*593dc095SDavid du Colombier 
350*593dc095SDavid du Colombier 	if (code < 0)
351*593dc095SDavid du Colombier 	    return code;
352*593dc095SDavid du Colombier 	gs_point_transform(font->FontBBox.p.x, font->FontBBox.p.y, &m, &p0);
353*593dc095SDavid du Colombier 	gs_point_transform(font->FontBBox.p.x, font->FontBBox.q.y, &m, &p1);
354*593dc095SDavid du Colombier 	gs_point_transform(font->FontBBox.q.x, font->FontBBox.p.y, &m, &p2);
355*593dc095SDavid du Colombier 	gs_point_transform(font->FontBBox.q.x, font->FontBBox.q.y, &m, &p3);
356*593dc095SDavid du Colombier 	bbox.p.x = min(min(p0.x, p1.x), min(p1.x, p2.x)) + total.x;
357*593dc095SDavid du Colombier 	bbox.p.y = min(min(p0.y, p1.y), min(p1.y, p2.y)) + total.y;
358*593dc095SDavid du Colombier 	bbox.q.x = max(max(p0.x, p1.x), max(p1.x, p2.x)) + total.x;
359*593dc095SDavid du Colombier 	bbox.q.y = max(max(p0.y, p1.y), max(p1.y, p2.y)) + total.y;
360*593dc095SDavid du Colombier 	if (i == 0)
361*593dc095SDavid du Colombier 	    *text_bbox = bbox;
362*593dc095SDavid du Colombier 	else
363*593dc095SDavid du Colombier 	    rect_merge(*text_bbox, bbox);
364*593dc095SDavid du Colombier 	if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
365*593dc095SDavid du Colombier 	    gs_text_replaced_width(&pte->text, xy_index++, &tpt);
366*593dc095SDavid du Colombier 	    gs_distance_transform(tpt.x, tpt.y, &ctm_only(pte->pis), &wanted);
367*593dc095SDavid du Colombier 	} else {
368*593dc095SDavid du Colombier 	    gs_distance_transform(info.width[WMode].x,
369*593dc095SDavid du Colombier 				  info.width[WMode].y,
370*593dc095SDavid du Colombier 				  &m, &wanted);
371*593dc095SDavid du Colombier 	    if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
372*593dc095SDavid du Colombier 		gs_distance_transform(pte->text.delta_all.x,
373*593dc095SDavid du Colombier 				      pte->text.delta_all.y,
374*593dc095SDavid du Colombier 				      &ctm_only(pte->pis), &tpt);
375*593dc095SDavid du Colombier 		wanted.x += tpt.x;
376*593dc095SDavid du Colombier 		wanted.y += tpt.y;
377*593dc095SDavid du Colombier 	    }
378*593dc095SDavid du Colombier 	    if (pstr->data[i] == space_char && pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
379*593dc095SDavid du Colombier 		gs_distance_transform(pte->text.delta_space.x,
380*593dc095SDavid du Colombier 				      pte->text.delta_space.y,
381*593dc095SDavid du Colombier 				      &ctm_only(pte->pis), &tpt);
382*593dc095SDavid du Colombier 		wanted.x += tpt.x;
383*593dc095SDavid du Colombier 		wanted.y += tpt.y;
384*593dc095SDavid du Colombier 	    }
385*593dc095SDavid du Colombier 	}
386*593dc095SDavid du Colombier 	total.x += wanted.x;
387*593dc095SDavid du Colombier 	total.y += wanted.y;
388*593dc095SDavid du Colombier     }
389*593dc095SDavid du Colombier     *pdpt = total;
390*593dc095SDavid du Colombier     return 0;
391*593dc095SDavid du Colombier }
392*593dc095SDavid du Colombier 
393*593dc095SDavid du Colombier private void
adjust_first_last_char(pdf_font_resource_t * pdfont,byte * str,int size)394*593dc095SDavid du Colombier adjust_first_last_char(pdf_font_resource_t *pdfont, byte *str, int size)
395*593dc095SDavid du Colombier {
396*593dc095SDavid du Colombier     int i;
397*593dc095SDavid du Colombier 
398*593dc095SDavid du Colombier     for (i = 0; i < size; ++i) {
399*593dc095SDavid du Colombier 	int chr = str[i];
400*593dc095SDavid du Colombier 
401*593dc095SDavid du Colombier 	if (chr < pdfont->u.simple.FirstChar)
402*593dc095SDavid du Colombier 	    pdfont->u.simple.FirstChar = chr;
403*593dc095SDavid du Colombier 	if (chr > pdfont->u.simple.LastChar)
404*593dc095SDavid du Colombier 	    pdfont->u.simple.LastChar = chr;
405*593dc095SDavid du Colombier     }
406*593dc095SDavid du Colombier }
407*593dc095SDavid du Colombier 
408*593dc095SDavid du Colombier int
pdf_shift_text_currentpoint(pdf_text_enum_t * penum,gs_point * wpt)409*593dc095SDavid du Colombier pdf_shift_text_currentpoint(pdf_text_enum_t *penum, gs_point *wpt)
410*593dc095SDavid du Colombier {
411*593dc095SDavid du Colombier     gs_state *pgs;
412*593dc095SDavid du Colombier     extern_st(st_gs_state);
413*593dc095SDavid du Colombier 
414*593dc095SDavid du Colombier     if (gs_object_type(penum->dev->memory, penum->pis) != &st_gs_state) {
415*593dc095SDavid du Colombier 	/* Probably never happens. Not sure though. */
416*593dc095SDavid du Colombier 	return_error(gs_error_unregistered);
417*593dc095SDavid du Colombier     }
418*593dc095SDavid du Colombier     pgs = (gs_state *)penum->pis;
419*593dc095SDavid du Colombier     return gs_moveto_aux(penum->pis, gx_current_path(pgs),
420*593dc095SDavid du Colombier 			      fixed2float(penum->origin.x) + wpt->x,
421*593dc095SDavid du Colombier 			      fixed2float(penum->origin.y) + wpt->y);
422*593dc095SDavid du Colombier }
423*593dc095SDavid du Colombier 
424*593dc095SDavid du Colombier /*
425*593dc095SDavid du Colombier  * Internal procedure to process a string in a non-composite font.
426*593dc095SDavid du Colombier  * Doesn't use or set pte->{data,size,index}; may use/set pte->xy_index;
427*593dc095SDavid du Colombier  * may set penum->returned.total_width.  Sets ppts->values.
428*593dc095SDavid du Colombier  *
429*593dc095SDavid du Colombier  * Note that the caller is responsible for re-encoding the string, if
430*593dc095SDavid du Colombier  * necessary; for adding Encoding entries in pdfont; and for copying any
431*593dc095SDavid du Colombier  * necessary glyphs.  penum->current_font provides the gs_font for getting
432*593dc095SDavid du Colombier  * glyph metrics, but this font's Encoding is not used.
433*593dc095SDavid du Colombier  */
434*593dc095SDavid du Colombier private int process_text_return_width(const pdf_text_enum_t *pte,
435*593dc095SDavid du Colombier 				      gs_font_base *font,
436*593dc095SDavid du Colombier 				      pdf_text_process_state_t *ppts,
437*593dc095SDavid du Colombier 				      const gs_const_string *pstr,
438*593dc095SDavid du Colombier 				      gs_point *pdpt, int *accepted);
439*593dc095SDavid du Colombier private int
pdf_process_string(pdf_text_enum_t * penum,gs_string * pstr,pdf_font_resource_t * pdfont,const gs_matrix * pfmat,pdf_text_process_state_t * ppts)440*593dc095SDavid du Colombier pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr,
441*593dc095SDavid du Colombier 		   pdf_font_resource_t *pdfont, const gs_matrix *pfmat,
442*593dc095SDavid du Colombier 		   pdf_text_process_state_t *ppts)
443*593dc095SDavid du Colombier {
444*593dc095SDavid du Colombier     gx_device_pdf *const pdev = (gx_device_pdf *)penum->dev;
445*593dc095SDavid du Colombier     gs_font_base *font = (gs_font_base *)penum->current_font;
446*593dc095SDavid du Colombier     const gs_text_params_t *text = &penum->text;
447*593dc095SDavid du Colombier     int code = 0, mask;
448*593dc095SDavid du Colombier     gs_point width_pt;
449*593dc095SDavid du Colombier     gs_rect text_bbox;
450*593dc095SDavid du Colombier     int accepted;
451*593dc095SDavid du Colombier 
452*593dc095SDavid du Colombier     if (pfmat == 0)
453*593dc095SDavid du Colombier 	pfmat = &font->FontMatrix;
454*593dc095SDavid du Colombier     if (text->operation & TEXT_RETURN_WIDTH) {
455*593dc095SDavid du Colombier 	code = gx_path_current_point(penum->path, &penum->origin);
456*593dc095SDavid du Colombier 	if (code < 0)
457*593dc095SDavid du Colombier 	    return code;
458*593dc095SDavid du Colombier     }
459*593dc095SDavid du Colombier     if (text->size == 0)
460*593dc095SDavid du Colombier 	return 0;
461*593dc095SDavid du Colombier     if (penum->pis->text_rendering_mode != 3 && !(text->operation & TEXT_DO_NONE)) {
462*593dc095SDavid du Colombier 	/*
463*593dc095SDavid du Colombier 	 * Acrobat Reader can't handle text with huge coordinates,
464*593dc095SDavid du Colombier 	 * so skip the text if it is outside the clip bbox
465*593dc095SDavid du Colombier 	 * (Note : it ever fails with type 3 fonts).
466*593dc095SDavid du Colombier 	 */
467*593dc095SDavid du Colombier 	code = process_text_estimate_bbox(penum, font, (gs_const_string *)pstr, pfmat,
468*593dc095SDavid du Colombier 					  &text_bbox, &width_pt);
469*593dc095SDavid du Colombier 	if (code == 0) {
470*593dc095SDavid du Colombier 	    gs_fixed_rect clip_bbox;
471*593dc095SDavid du Colombier 	    gs_rect rect;
472*593dc095SDavid du Colombier 
473*593dc095SDavid du Colombier 	    gx_cpath_outer_box(penum->pcpath, &clip_bbox);
474*593dc095SDavid du Colombier 	    rect.p.x = fixed2float(clip_bbox.p.x);
475*593dc095SDavid du Colombier 	    rect.p.y = fixed2float(clip_bbox.p.y);
476*593dc095SDavid du Colombier 	    rect.q.x = fixed2float(clip_bbox.q.x);
477*593dc095SDavid du Colombier 	    rect.q.y = fixed2float(clip_bbox.q.y);
478*593dc095SDavid du Colombier 	    rect_intersect(rect, text_bbox);
479*593dc095SDavid du Colombier 	    if (rect.p.x > rect.q.x || rect.p.y > rect.q.y) {
480*593dc095SDavid du Colombier 		penum->index += pstr->size;
481*593dc095SDavid du Colombier 		goto finish;
482*593dc095SDavid du Colombier 	    }
483*593dc095SDavid du Colombier 	}
484*593dc095SDavid du Colombier     } else {
485*593dc095SDavid du Colombier 	/* We have no penum->pcpath. */
486*593dc095SDavid du Colombier     }
487*593dc095SDavid du Colombier 
488*593dc095SDavid du Colombier     /*
489*593dc095SDavid du Colombier      * Note that pdf_update_text_state sets all the members of ppts->values
490*593dc095SDavid du Colombier      * to their current values.
491*593dc095SDavid du Colombier      */
492*593dc095SDavid du Colombier     code = pdf_update_text_state(ppts, penum, pdfont, pfmat);
493*593dc095SDavid du Colombier     if (code > 0) {
494*593dc095SDavid du Colombier 	/* Try not to emulate ADD_TO_WIDTH if we don't have to. */
495*593dc095SDavid du Colombier 	if (code & TEXT_ADD_TO_SPACE_WIDTH) {
496*593dc095SDavid du Colombier 	    if (!memchr(pstr->data, penum->text.space.s_char, pstr->size))
497*593dc095SDavid du Colombier 		code &= ~TEXT_ADD_TO_SPACE_WIDTH;
498*593dc095SDavid du Colombier 	}
499*593dc095SDavid du Colombier     }
500*593dc095SDavid du Colombier     if (code < 0)
501*593dc095SDavid du Colombier 	return code;
502*593dc095SDavid du Colombier     mask = code;
503*593dc095SDavid du Colombier 
504*593dc095SDavid du Colombier     if (text->operation & TEXT_REPLACE_WIDTHS)
505*593dc095SDavid du Colombier 	mask |= TEXT_REPLACE_WIDTHS;
506*593dc095SDavid du Colombier 
507*593dc095SDavid du Colombier     /*
508*593dc095SDavid du Colombier      * The only operations left to handle are TEXT_DO_DRAW and
509*593dc095SDavid du Colombier      * TEXT_RETURN_WIDTH.
510*593dc095SDavid du Colombier      */
511*593dc095SDavid du Colombier     if (mask == 0) {
512*593dc095SDavid du Colombier 	/*
513*593dc095SDavid du Colombier 	 * If any character has real_width != Width, we have to process
514*593dc095SDavid du Colombier 	 * the string character-by-character.  process_text_return_width
515*593dc095SDavid du Colombier 	 * will tell us what we need to know.
516*593dc095SDavid du Colombier 	 */
517*593dc095SDavid du Colombier 	if (!(text->operation & (TEXT_DO_DRAW | TEXT_RETURN_WIDTH)))
518*593dc095SDavid du Colombier 	    return 0;
519*593dc095SDavid du Colombier 	code = process_text_return_width(penum, font, ppts,
520*593dc095SDavid du Colombier 					 (gs_const_string *)pstr,
521*593dc095SDavid du Colombier 					 &width_pt, &accepted);
522*593dc095SDavid du Colombier 	if (code < 0)
523*593dc095SDavid du Colombier 	    return code;
524*593dc095SDavid du Colombier 	if (code == 0) {
525*593dc095SDavid du Colombier 	    /* No characters with redefined widths -- the fast case. */
526*593dc095SDavid du Colombier 	    if (text->operation & TEXT_DO_DRAW || penum->pis->text_rendering_mode == 3) {
527*593dc095SDavid du Colombier 		code = pdf_append_chars(pdev, pstr->data, accepted,
528*593dc095SDavid du Colombier 					width_pt.x, width_pt.y, false);
529*593dc095SDavid du Colombier 		if (code < 0)
530*593dc095SDavid du Colombier 		    return code;
531*593dc095SDavid du Colombier 		adjust_first_last_char(pdfont, pstr->data, accepted);
532*593dc095SDavid du Colombier 		penum->index += accepted;
533*593dc095SDavid du Colombier 	    } else if (text->operation & TEXT_DO_NONE)
534*593dc095SDavid du Colombier 		penum->index += accepted;
535*593dc095SDavid du Colombier 	} else {
536*593dc095SDavid du Colombier 	    /* Use the slow case.  Set mask to any non-zero value. */
537*593dc095SDavid du Colombier 	    mask = TEXT_RETURN_WIDTH;
538*593dc095SDavid du Colombier 	}
539*593dc095SDavid du Colombier     }
540*593dc095SDavid du Colombier     if (mask) {
541*593dc095SDavid du Colombier 	/* process_text_modify_width destroys text parameters, save them now. */
542*593dc095SDavid du Colombier         int index0 = penum->index, xy_index = penum->xy_index;
543*593dc095SDavid du Colombier 	gs_text_params_t text = penum->text;
544*593dc095SDavid du Colombier 	int xy_index_step = (penum->text.x_widths != NULL && /* see gs_text_replaced_width */
545*593dc095SDavid du Colombier 			     penum->text.x_widths == penum->text.y_widths ? 2 : 1);
546*593dc095SDavid du Colombier 
547*593dc095SDavid du Colombier 	if (penum->text.x_widths != NULL) {
548*593dc095SDavid du Colombier 	    penum->text.x_widths += xy_index * xy_index_step;
549*593dc095SDavid du Colombier 	}
550*593dc095SDavid du Colombier 	if (penum->text.y_widths != NULL)
551*593dc095SDavid du Colombier 	    penum->text.y_widths += xy_index * xy_index_step;
552*593dc095SDavid du Colombier 	penum->xy_index = 0;
553*593dc095SDavid du Colombier 	code = process_text_modify_width(penum, (gs_font *)font, ppts,
554*593dc095SDavid du Colombier 					 (gs_const_string *)pstr,
555*593dc095SDavid du Colombier 					 &width_pt);
556*593dc095SDavid du Colombier 	if (penum->text.x_widths != NULL)
557*593dc095SDavid du Colombier 	    penum->text.x_widths -= xy_index * xy_index_step;
558*593dc095SDavid du Colombier 	if (penum->text.y_widths != NULL)
559*593dc095SDavid du Colombier 	    penum->text.y_widths -= xy_index * xy_index_step;
560*593dc095SDavid du Colombier 	penum->xy_index += xy_index;
561*593dc095SDavid du Colombier 	adjust_first_last_char(pdfont, pstr->data, penum->index);
562*593dc095SDavid du Colombier 	penum->text = text;
563*593dc095SDavid du Colombier 	penum->index += index0;
564*593dc095SDavid du Colombier 	if (code < 0)
565*593dc095SDavid du Colombier 	    return code;
566*593dc095SDavid du Colombier     }
567*593dc095SDavid du Colombier 
568*593dc095SDavid du Colombier 
569*593dc095SDavid du Colombier finish:
570*593dc095SDavid du Colombier     /* Finally, return the total width if requested. */
571*593dc095SDavid du Colombier     if (!(text->operation & TEXT_RETURN_WIDTH))
572*593dc095SDavid du Colombier 	return 0;
573*593dc095SDavid du Colombier     if (text->operation & TEXT_DO_NONE) {
574*593dc095SDavid du Colombier 	/* stringwidth needs to transform to user space. */
575*593dc095SDavid du Colombier 	gs_point p;
576*593dc095SDavid du Colombier 
577*593dc095SDavid du Colombier 	gs_distance_transform_inverse(width_pt.x, width_pt.y, &ctm_only(penum->pis), &p);
578*593dc095SDavid du Colombier 	penum->returned.total_width.x += p.x;
579*593dc095SDavid du Colombier 	penum->returned.total_width.y += p.y;
580*593dc095SDavid du Colombier     } else
581*593dc095SDavid du Colombier 	penum->returned.total_width = width_pt;
582*593dc095SDavid du Colombier     return pdf_shift_text_currentpoint(penum, &width_pt);
583*593dc095SDavid du Colombier }
584*593dc095SDavid du Colombier 
585*593dc095SDavid du Colombier /*
586*593dc095SDavid du Colombier  * Get the widths (unmodified and possibly modified) of a given character
587*593dc095SDavid du Colombier  * in a simple font.  May add the widths to the widths cache (pdfont->Widths
588*593dc095SDavid du Colombier  * and pdf_font_cache_elem::real_widths).  Return 1 if the widths were not cached.
589*593dc095SDavid du Colombier  */
590*593dc095SDavid du Colombier private 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)591*593dc095SDavid du Colombier pdf_char_widths(gx_device_pdf *const pdev,
592*593dc095SDavid du Colombier 		pdf_font_resource_t *pdfont, int ch, gs_font_base *font,
593*593dc095SDavid du Colombier 		pdf_glyph_widths_t *pwidths /* may be NULL */)
594*593dc095SDavid du Colombier {
595*593dc095SDavid du Colombier     pdf_glyph_widths_t widths;
596*593dc095SDavid du Colombier     int code;
597*593dc095SDavid du Colombier     byte *glyph_usage;
598*593dc095SDavid du Colombier     double *real_widths;
599*593dc095SDavid du Colombier     int char_cache_size, width_cache_size;
600*593dc095SDavid du Colombier     pdf_font_resource_t *pdfont1;
601*593dc095SDavid du Colombier 
602*593dc095SDavid du Colombier     code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont1,
603*593dc095SDavid du Colombier 				&glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
604*593dc095SDavid du Colombier     if (code < 0)
605*593dc095SDavid du Colombier 	return code;
606*593dc095SDavid du Colombier     if (pdfont1 != pdfont)
607*593dc095SDavid du Colombier 	return_error(gs_error_unregistered); /* Must not happen. */
608*593dc095SDavid du Colombier     if (ch < 0 || ch > 255)
609*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
610*593dc095SDavid du Colombier     if (ch >= width_cache_size)
611*593dc095SDavid du Colombier 	return_error(gs_error_unregistered); /* Must not happen. */
612*593dc095SDavid du Colombier     if (pwidths == 0)
613*593dc095SDavid du Colombier 	pwidths = &widths;
614*593dc095SDavid du Colombier     if (font->FontType != ft_user_defined && real_widths[ch] == 0) {
615*593dc095SDavid du Colombier 	/* Might be an unused char, or just not cached. */
616*593dc095SDavid du Colombier 	gs_glyph glyph = pdfont->u.simple.Encoding[ch].glyph;
617*593dc095SDavid du Colombier 
618*593dc095SDavid du Colombier 	code = pdf_glyph_widths(pdfont, font->WMode, glyph, (gs_font *)font, pwidths, NULL);
619*593dc095SDavid du Colombier 	if (code < 0)
620*593dc095SDavid du Colombier 	    return code;
621*593dc095SDavid du Colombier 	if (font->WMode != 0 && code > 0 && !pwidths->replaced_v) {
622*593dc095SDavid du Colombier 	    /*
623*593dc095SDavid du Colombier 	     * The font has no Metrics2, so it must write
624*593dc095SDavid du Colombier 	     * horizontally due to PS spec.
625*593dc095SDavid du Colombier 	     * Therefore we need to fill the Widths array,
626*593dc095SDavid du Colombier 	     * which is required by PDF spec.
627*593dc095SDavid du Colombier 	     * Take it from WMode==0.
628*593dc095SDavid du Colombier 	     */
629*593dc095SDavid du Colombier 	    code = pdf_glyph_widths(pdfont, 0, glyph, (gs_font *)font, pwidths, NULL);
630*593dc095SDavid du Colombier 	}
631*593dc095SDavid du Colombier 	if (pwidths->replaced_v) {
632*593dc095SDavid du Colombier 	    pdfont->u.simple.v[ch].x = pwidths->real_width.v.x - pwidths->Width.v.x;
633*593dc095SDavid du Colombier 	    pdfont->u.simple.v[ch].y = pwidths->real_width.v.y - pwidths->Width.v.y;
634*593dc095SDavid du Colombier 	} else
635*593dc095SDavid du Colombier 	    pdfont->u.simple.v[ch].x = pdfont->u.simple.v[ch].y = 0;
636*593dc095SDavid du Colombier 	if (code == 0) {
637*593dc095SDavid du Colombier 	    pdfont->Widths[ch] = pwidths->Width.w;
638*593dc095SDavid du Colombier 	    real_widths[ch] = pwidths->real_width.w;
639*593dc095SDavid du Colombier 	}
640*593dc095SDavid du Colombier     } else {
641*593dc095SDavid du Colombier 	if (font->FontType == ft_user_defined) {
642*593dc095SDavid du Colombier 	    if (!(pdfont->used[ch >> 3] & 0x80 >> (ch & 7)))
643*593dc095SDavid du Colombier 		return gs_error_undefined; /* The charproc was not accumulated. */
644*593dc095SDavid du Colombier 	    if (!pdev->charproc_just_accumulated &&
645*593dc095SDavid du Colombier 		!(pdfont->u.simple.s.type3.cached[ch >> 3] & 0x80 >> (ch & 7))) {
646*593dc095SDavid du Colombier 		 /* The charproc uses setcharwidth.
647*593dc095SDavid du Colombier 		    Need to accumulate again to check for a glyph variation. */
648*593dc095SDavid du Colombier 		return gs_error_undefined;
649*593dc095SDavid du Colombier 	    }
650*593dc095SDavid du Colombier 	}
651*593dc095SDavid du Colombier 	pwidths->Width.w = pdfont->Widths[ch];
652*593dc095SDavid du Colombier 	pwidths->Width.v = pdfont->u.simple.v[ch];
653*593dc095SDavid du Colombier 	if (font->FontType == ft_user_defined) {
654*593dc095SDavid du Colombier 	    pwidths->real_width.w = real_widths[ch * 2];
655*593dc095SDavid du Colombier 	    pwidths->Width.xy.x = pwidths->Width.w;
656*593dc095SDavid du Colombier 	    pwidths->Width.xy.y = 0;
657*593dc095SDavid du Colombier 	    pwidths->real_width.xy.x = real_widths[ch * 2 + 0];
658*593dc095SDavid du Colombier 	    pwidths->real_width.xy.y = real_widths[ch * 2 + 1];
659*593dc095SDavid du Colombier 	} else if (font->WMode) {
660*593dc095SDavid du Colombier 	    pwidths->real_width.w = real_widths[ch];
661*593dc095SDavid du Colombier 	    pwidths->Width.xy.x = 0;
662*593dc095SDavid du Colombier 	    pwidths->Width.xy.y = pwidths->Width.w;
663*593dc095SDavid du Colombier 	    pwidths->real_width.xy.x = 0;
664*593dc095SDavid du Colombier 	    pwidths->real_width.xy.y = pwidths->real_width.w;
665*593dc095SDavid du Colombier 	} else {
666*593dc095SDavid du Colombier 	    pwidths->real_width.w = real_widths[ch];
667*593dc095SDavid du Colombier 	    pwidths->Width.xy.x = pwidths->Width.w;
668*593dc095SDavid du Colombier 	    pwidths->Width.xy.y = 0;
669*593dc095SDavid du Colombier 	    pwidths->real_width.xy.x = pwidths->real_width.w;
670*593dc095SDavid du Colombier 	    pwidths->real_width.xy.y = 0;
671*593dc095SDavid du Colombier 	}
672*593dc095SDavid du Colombier 	code = 0;
673*593dc095SDavid du Colombier     }
674*593dc095SDavid du Colombier     return code;
675*593dc095SDavid du Colombier }
676*593dc095SDavid du Colombier 
677*593dc095SDavid du Colombier /*
678*593dc095SDavid du Colombier  * Compute the total text width (in user space).  Return 1 if any
679*593dc095SDavid du Colombier  * character had real_width != Width, otherwise 0.
680*593dc095SDavid du Colombier  */
681*593dc095SDavid du Colombier private 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,gs_point * pdpt,int * accepted)682*593dc095SDavid du Colombier process_text_return_width(const pdf_text_enum_t *pte, gs_font_base *font,
683*593dc095SDavid du Colombier 			  pdf_text_process_state_t *ppts,
684*593dc095SDavid du Colombier 			  const gs_const_string *pstr,
685*593dc095SDavid du Colombier 			  gs_point *pdpt, int *accepted)
686*593dc095SDavid du Colombier {
687*593dc095SDavid du Colombier     int i;
688*593dc095SDavid du Colombier     gs_point w;
689*593dc095SDavid du Colombier     double scale;
690*593dc095SDavid du Colombier     gs_point dpt;
691*593dc095SDavid du Colombier     int num_spaces = 0;
692*593dc095SDavid du Colombier     int space_char =
693*593dc095SDavid du Colombier 	(pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
694*593dc095SDavid du Colombier 	 pte->text.space.s_char : -1);
695*593dc095SDavid du Colombier     int widths_differ = 0, code;
696*593dc095SDavid du Colombier     gx_device_pdf *pdev = (gx_device_pdf *)pte->dev;
697*593dc095SDavid du Colombier     pdf_font_resource_t *pdfont;
698*593dc095SDavid du Colombier 
699*593dc095SDavid du Colombier     code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont, NULL, NULL, NULL, NULL);
700*593dc095SDavid du Colombier     if (code < 0)
701*593dc095SDavid du Colombier 	return code;
702*593dc095SDavid du Colombier     if (font->FontType == ft_user_defined) {
703*593dc095SDavid du Colombier 	pdf_font3_scale(pdev, (gs_font *)font, &scale);
704*593dc095SDavid du Colombier 	scale *= ppts->values.size;
705*593dc095SDavid du Colombier     } else
706*593dc095SDavid du Colombier 	scale = 0.001 * ppts->values.size;
707*593dc095SDavid du Colombier     for (i = 0, w.x = w.y = 0; i < pstr->size; ++i) {
708*593dc095SDavid du Colombier 	pdf_glyph_widths_t cw;
709*593dc095SDavid du Colombier 	gs_char ch = pstr->data[i];
710*593dc095SDavid du Colombier 
711*593dc095SDavid du Colombier 	if (font->FontType == ft_user_defined &&
712*593dc095SDavid du Colombier 	    (i > 0 || !pdev->charproc_just_accumulated) &&
713*593dc095SDavid du Colombier 	    !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7))))
714*593dc095SDavid du Colombier 	    code = gs_error_undefined;
715*593dc095SDavid du Colombier 	else
716*593dc095SDavid du Colombier 	    code = pdf_char_widths((gx_device_pdf *)pte->dev,
717*593dc095SDavid du Colombier 	                           ppts->values.pdfont, ch, font,
718*593dc095SDavid du Colombier 				   &cw);
719*593dc095SDavid du Colombier 	if (code < 0) {
720*593dc095SDavid du Colombier 	    if (i)
721*593dc095SDavid du Colombier 		break;
722*593dc095SDavid du Colombier 	    *accepted = 0;
723*593dc095SDavid du Colombier 	    return code;
724*593dc095SDavid du Colombier 	}
725*593dc095SDavid du Colombier 	w.x += cw.real_width.xy.x;
726*593dc095SDavid du Colombier 	w.y += cw.real_width.xy.y;
727*593dc095SDavid du Colombier 	if (cw.real_width.xy.x != cw.Width.xy.x ||
728*593dc095SDavid du Colombier 	    cw.real_width.xy.y != cw.Width.xy.y
729*593dc095SDavid du Colombier 	    )
730*593dc095SDavid du Colombier 	    widths_differ = 1;
731*593dc095SDavid du Colombier 	if (pstr->data[i] == space_char)
732*593dc095SDavid du Colombier 	    ++num_spaces;
733*593dc095SDavid du Colombier     }
734*593dc095SDavid du Colombier     *accepted = i;
735*593dc095SDavid du Colombier     gs_distance_transform(w.x * scale, w.y * scale,
736*593dc095SDavid du Colombier 			  &ppts->values.matrix, &dpt);
737*593dc095SDavid du Colombier     if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
738*593dc095SDavid du Colombier 	int num_chars = *accepted;
739*593dc095SDavid du Colombier 	gs_point tpt;
740*593dc095SDavid du Colombier 
741*593dc095SDavid du Colombier 	gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
742*593dc095SDavid du Colombier 			      &ctm_only(pte->pis), &tpt);
743*593dc095SDavid du Colombier 	dpt.x += tpt.x * num_chars;
744*593dc095SDavid du Colombier 	dpt.y += tpt.y * num_chars;
745*593dc095SDavid du Colombier     }
746*593dc095SDavid du Colombier     if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
747*593dc095SDavid du Colombier 	gs_point tpt;
748*593dc095SDavid du Colombier 
749*593dc095SDavid du Colombier 	gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
750*593dc095SDavid du Colombier 			      &ctm_only(pte->pis), &tpt);
751*593dc095SDavid du Colombier 	dpt.x += tpt.x * num_spaces;
752*593dc095SDavid du Colombier 	dpt.y += tpt.y * num_spaces;
753*593dc095SDavid du Colombier     }
754*593dc095SDavid du Colombier     *pdpt = dpt;
755*593dc095SDavid du Colombier 
756*593dc095SDavid du Colombier     return widths_differ;
757*593dc095SDavid du Colombier }
758*593dc095SDavid du Colombier 
759*593dc095SDavid du Colombier #define RIGHT_SBW 1 /* Old code = 0, new code = 1. */
760*593dc095SDavid du Colombier #if !RIGHT_SBW
761*593dc095SDavid du Colombier /*
762*593dc095SDavid du Colombier  * Retrieve glyph origing shift for WMode = 1 in design units.
763*593dc095SDavid du Colombier  */
764*593dc095SDavid du Colombier private void
pdf_glyph_origin(pdf_font_resource_t * pdfont,int ch,int WMode,gs_point * p)765*593dc095SDavid du Colombier pdf_glyph_origin(pdf_font_resource_t *pdfont, int ch, int WMode, gs_point *p)
766*593dc095SDavid du Colombier {
767*593dc095SDavid du Colombier     /* For CID fonts PDF viewers provide glyph origin shift automatically.
768*593dc095SDavid du Colombier      * Therefore we only need to do for non-CID fonts.
769*593dc095SDavid du Colombier      */
770*593dc095SDavid du Colombier     switch (pdfont->FontType) {
771*593dc095SDavid du Colombier 	case ft_encrypted:
772*593dc095SDavid du Colombier 	case ft_encrypted2:
773*593dc095SDavid du Colombier 	case ft_TrueType:
774*593dc095SDavid du Colombier 	case ft_user_defined:
775*593dc095SDavid du Colombier 	    *p = pdfont->u.simple.v[ch];
776*593dc095SDavid du Colombier 	    break;
777*593dc095SDavid du Colombier 	default:
778*593dc095SDavid du Colombier 	    p->x = p->y = 0;
779*593dc095SDavid du Colombier 	    break;
780*593dc095SDavid du Colombier     }
781*593dc095SDavid du Colombier }
782*593dc095SDavid du Colombier #endif
783*593dc095SDavid du Colombier 
784*593dc095SDavid du Colombier /*
785*593dc095SDavid du Colombier  * Emulate TEXT_ADD_TO_ALL_WIDTHS and/or TEXT_ADD_TO_SPACE_WIDTH,
786*593dc095SDavid du Colombier  * and implement TEXT_REPLACE_WIDTHS if requested.
787*593dc095SDavid du Colombier  * Uses and updates ppts->values.matrix; uses ppts->values.pdfont.
788*593dc095SDavid du Colombier  *
789*593dc095SDavid du Colombier  * Destroys the text parameters in *pte.
790*593dc095SDavid du Colombier  * The caller must restore them.
791*593dc095SDavid du Colombier  */
792*593dc095SDavid du Colombier 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)793*593dc095SDavid du Colombier process_text_modify_width(pdf_text_enum_t *pte, gs_font *font,
794*593dc095SDavid du Colombier 			  pdf_text_process_state_t *ppts,
795*593dc095SDavid du Colombier 			  const gs_const_string *pstr,
796*593dc095SDavid du Colombier 			  gs_point *pdpt)
797*593dc095SDavid du Colombier {
798*593dc095SDavid du Colombier     gx_device_pdf *const pdev = (gx_device_pdf *)pte->dev;
799*593dc095SDavid du Colombier     double scale;
800*593dc095SDavid du Colombier     int space_char =
801*593dc095SDavid du Colombier 	(pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
802*593dc095SDavid du Colombier 	 pte->text.space.s_char : -1);
803*593dc095SDavid du Colombier     int code = 0;
804*593dc095SDavid du Colombier     gs_point start, total;
805*593dc095SDavid du Colombier     bool composite = (ppts->values.pdfont->FontType == ft_composite);
806*593dc095SDavid du Colombier 
807*593dc095SDavid du Colombier     if (font->FontType == ft_user_defined) {
808*593dc095SDavid du Colombier 	gx_device_pdf *pdev = (gx_device_pdf *)pte->dev;
809*593dc095SDavid du Colombier 
810*593dc095SDavid du Colombier 	pdf_font3_scale(pdev, font, &scale);
811*593dc095SDavid du Colombier 	scale *= ppts->values.size;
812*593dc095SDavid du Colombier     } else
813*593dc095SDavid du Colombier 	scale = 0.001 * ppts->values.size;
814*593dc095SDavid du Colombier     pte->text.data.bytes = pstr->data;
815*593dc095SDavid du Colombier     pte->text.size = pstr->size;
816*593dc095SDavid du Colombier     pte->index = 0;
817*593dc095SDavid du Colombier     pte->text.operation &= ~TEXT_FROM_ANY;
818*593dc095SDavid du Colombier     pte->text.operation |= TEXT_FROM_STRING;
819*593dc095SDavid du Colombier     start.x = ppts->values.matrix.tx;
820*593dc095SDavid du Colombier     start.y = ppts->values.matrix.ty;
821*593dc095SDavid du Colombier     total.x = total.y = 0;	/* user space */
822*593dc095SDavid du Colombier     /*
823*593dc095SDavid du Colombier      * Note that character widths are in design space, but text.delta_*
824*593dc095SDavid du Colombier      * values and the width value returned in *pdpt are in user space,
825*593dc095SDavid du Colombier      * and the width values for pdf_append_chars are in device space.
826*593dc095SDavid du Colombier      */
827*593dc095SDavid du Colombier     for (;;) {
828*593dc095SDavid du Colombier 	pdf_glyph_widths_t cw;	/* design space */
829*593dc095SDavid du Colombier 	gs_point did, wanted, tpt;	/* user space */
830*593dc095SDavid du Colombier 	gs_point v = {0, 0}; /* design space */
831*593dc095SDavid du Colombier 	gs_char chr;
832*593dc095SDavid du Colombier 	gs_glyph glyph;
833*593dc095SDavid du Colombier 	int code, index = pte->index;
834*593dc095SDavid du Colombier 	gs_text_enum_t pte1 = *(gs_text_enum_t *)pte;
835*593dc095SDavid du Colombier 	int FontType;
836*593dc095SDavid du Colombier #if RIGHT_SBW
837*593dc095SDavid du Colombier 	bool use_cached_v = true;
838*593dc095SDavid du Colombier #endif
839*593dc095SDavid du Colombier 
840*593dc095SDavid du Colombier 	code = pte1.orig_font->procs.next_char_glyph(&pte1, &chr, &glyph);
841*593dc095SDavid du Colombier 	if (code == 2) { /* end of string */
842*593dc095SDavid du Colombier 	    gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true);
843*593dc095SDavid du Colombier 	    break;
844*593dc095SDavid du Colombier 	}
845*593dc095SDavid du Colombier 	if (code < 0)
846*593dc095SDavid du Colombier 	    return code;
847*593dc095SDavid du Colombier 	if (composite) { /* from process_cmap_text */
848*593dc095SDavid du Colombier 	    gs_font *subfont = pte1.fstack.items[pte1.fstack.depth].font;
849*593dc095SDavid du Colombier 	    pdf_font_resource_t *pdsubf = ppts->values.pdfont->u.type0.DescendantFont;
850*593dc095SDavid du Colombier 
851*593dc095SDavid du Colombier 	    FontType = pdsubf->FontType;
852*593dc095SDavid du Colombier 	    code = pdf_glyph_widths(pdsubf, font->WMode, glyph, subfont, &cw,
853*593dc095SDavid du Colombier 		pte->cdevproc_callout ? pte->cdevproc_result : NULL);
854*593dc095SDavid du Colombier 	} else {/* must be a base font */
855*593dc095SDavid du Colombier 	    FontType = font->FontType;
856*593dc095SDavid du Colombier 	    if (chr == GS_NO_CHAR && glyph != GS_NO_GLYPH) {
857*593dc095SDavid du Colombier 		/* glyphshow, we have no char code. Bug 686988.*/
858*593dc095SDavid du Colombier 		code = pdf_glyph_widths(ppts->values.pdfont, font->WMode, glyph, font, &cw, NULL);
859*593dc095SDavid du Colombier 		use_cached_v = false; /* Since we have no chr and don't call pdf_char_widths. */
860*593dc095SDavid du Colombier 	    } else
861*593dc095SDavid du Colombier 		code = pdf_char_widths((gx_device_pdf *)pte->dev,
862*593dc095SDavid du Colombier 				       ppts->values.pdfont, chr, (gs_font_base *)font,
863*593dc095SDavid du Colombier 				       &cw);
864*593dc095SDavid du Colombier 	}
865*593dc095SDavid du Colombier 	if (code < 0) {
866*593dc095SDavid du Colombier 	    if (index > 0)
867*593dc095SDavid du Colombier 		break;
868*593dc095SDavid du Colombier 	    return code;
869*593dc095SDavid du Colombier 	}
870*593dc095SDavid du Colombier 	gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true);
871*593dc095SDavid du Colombier #if RIGHT_SBW
872*593dc095SDavid du Colombier 	if (composite || !use_cached_v) {
873*593dc095SDavid du Colombier 	    if (cw.replaced_v) {
874*593dc095SDavid du Colombier 		v.x = cw.real_width.v.x - cw.Width.v.x;
875*593dc095SDavid du Colombier 		v.y = cw.real_width.v.y - cw.Width.v.y;
876*593dc095SDavid du Colombier 	    }
877*593dc095SDavid du Colombier 	} else
878*593dc095SDavid du Colombier 	    v = ppts->values.pdfont->u.simple.v[chr];
879*593dc095SDavid du Colombier 	if (font->WMode) {
880*593dc095SDavid du Colombier 	    /* With WMode 1 v-vector is (WMode 1 origin) - (WMode 0 origin).
881*593dc095SDavid du Colombier 	       The glyph shifts in the opposite direction.  */
882*593dc095SDavid du Colombier 	    v.x = - v.x;
883*593dc095SDavid du Colombier 	    v.y = - v.y;
884*593dc095SDavid du Colombier 	} else {
885*593dc095SDavid du Colombier 	    /* With WMode 0 v-vector is (Metrics sb) - (native sb).
886*593dc095SDavid du Colombier 	       The glyph shifts in same direction.  */
887*593dc095SDavid du Colombier 	}
888*593dc095SDavid du Colombier 	/* pdf_glyph_origin is not longer used. */
889*593dc095SDavid du Colombier #else
890*593dc095SDavid du Colombier 	if ((pte->text.operation & TEXT_FROM_SINGLE_GLYPH) ||
891*593dc095SDavid du Colombier 	    (pte->text.operation & TEXT_FROM_GLYPHS)) {
892*593dc095SDavid du Colombier 	    v.x = v.y = 0;
893*593dc095SDavid du Colombier 	} else if (composite) {
894*593dc095SDavid du Colombier 	    if (cw.replaced_v) {
895*593dc095SDavid du Colombier 		v.x = cw.real_width.v.x - cw.Width.v.x;
896*593dc095SDavid du Colombier 		v.y = cw.real_width.v.y - cw.Width.v.y;
897*593dc095SDavid du Colombier 	    }
898*593dc095SDavid du Colombier 	} else
899*593dc095SDavid du Colombier 	    pdf_glyph_origin(ppts->values.pdfont, chr, font->WMode, &v);
900*593dc095SDavid du Colombier #endif
901*593dc095SDavid du Colombier 	if (v.x != 0 || v.y != 0) {
902*593dc095SDavid du Colombier 	    gs_point glyph_origin_shift;
903*593dc095SDavid du Colombier 	    double scale0;
904*593dc095SDavid du Colombier 
905*593dc095SDavid du Colombier 	    if (FontType == ft_TrueType || FontType == ft_CID_TrueType)
906*593dc095SDavid du Colombier 		scale0 = (float)0.001;
907*593dc095SDavid du Colombier 	    else
908*593dc095SDavid du Colombier 		scale0 = 1;
909*593dc095SDavid du Colombier #if RIGHT_SBW
910*593dc095SDavid du Colombier 	    glyph_origin_shift.x = v.x * scale0;
911*593dc095SDavid du Colombier 	    glyph_origin_shift.y = v.y * scale0;
912*593dc095SDavid du Colombier #else
913*593dc095SDavid du Colombier 	    glyph_origin_shift.x = - v.x * scale0;
914*593dc095SDavid du Colombier 	    glyph_origin_shift.y = - v.y * scale0;
915*593dc095SDavid du Colombier #endif
916*593dc095SDavid du Colombier 	    if (composite) {
917*593dc095SDavid du Colombier 		gs_font *subfont = pte->fstack.items[pte->fstack.depth].font;
918*593dc095SDavid du Colombier 
919*593dc095SDavid du Colombier 		gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
920*593dc095SDavid du Colombier 				      &subfont->FontMatrix, &glyph_origin_shift);
921*593dc095SDavid du Colombier 	    }
922*593dc095SDavid du Colombier 	    gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
923*593dc095SDavid du Colombier 				  &font->FontMatrix, &glyph_origin_shift);
924*593dc095SDavid du Colombier 	    gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
925*593dc095SDavid du Colombier 				  &ctm_only(pte->pis), &glyph_origin_shift);
926*593dc095SDavid du Colombier 	    if (glyph_origin_shift.x != 0 || glyph_origin_shift.y != 0) {
927*593dc095SDavid du Colombier 		ppts->values.matrix.tx = start.x + total.x + glyph_origin_shift.x;
928*593dc095SDavid du Colombier 		ppts->values.matrix.ty = start.y + total.y + glyph_origin_shift.y;
929*593dc095SDavid du Colombier 		code = pdf_set_text_state_values(pdev, &ppts->values);
930*593dc095SDavid du Colombier 		if (code < 0)
931*593dc095SDavid du Colombier 		    break;
932*593dc095SDavid du Colombier 	    }
933*593dc095SDavid du Colombier 	}
934*593dc095SDavid du Colombier 	if (pte->text.operation & TEXT_DO_DRAW) {
935*593dc095SDavid du Colombier 	    gs_distance_transform(cw.Width.xy.x * scale,
936*593dc095SDavid du Colombier 				  cw.Width.xy.y * scale,
937*593dc095SDavid du Colombier 				  &ppts->values.matrix, &did);
938*593dc095SDavid du Colombier 	    gs_distance_transform((font->WMode ? 0 : ppts->values.character_spacing),
939*593dc095SDavid du Colombier 				  (font->WMode ? ppts->values.character_spacing : 0),
940*593dc095SDavid du Colombier 				  &ppts->values.matrix, &tpt);
941*593dc095SDavid du Colombier 	    did.x += tpt.x;
942*593dc095SDavid du Colombier 	    did.y += tpt.y;
943*593dc095SDavid du Colombier 	    if (chr == space_char) {
944*593dc095SDavid du Colombier 		gs_distance_transform((font->WMode ? 0 : ppts->values.word_spacing),
945*593dc095SDavid du Colombier 				      (font->WMode ? ppts->values.word_spacing : 0),
946*593dc095SDavid du Colombier 				      &ppts->values.matrix, &tpt);
947*593dc095SDavid du Colombier 		did.x += tpt.x;
948*593dc095SDavid du Colombier 		did.y += tpt.y;
949*593dc095SDavid du Colombier 	    }
950*593dc095SDavid du Colombier 	    code = pdf_append_chars(pdev, pstr->data + index, pte->index - index, did.x, did.y, composite);
951*593dc095SDavid du Colombier 	    if (code < 0)
952*593dc095SDavid du Colombier 		break;
953*593dc095SDavid du Colombier 	} else
954*593dc095SDavid du Colombier 	    did.x = did.y = 0;
955*593dc095SDavid du Colombier 	if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
956*593dc095SDavid du Colombier 	    gs_point dpt;
957*593dc095SDavid du Colombier 
958*593dc095SDavid du Colombier 	    code = gs_text_replaced_width(&pte->text, pte->xy_index++, &dpt);
959*593dc095SDavid du Colombier 	    if (code < 0)
960*593dc095SDavid du Colombier 		return_error(gs_error_unregistered);
961*593dc095SDavid du Colombier 	    gs_distance_transform(dpt.x, dpt.y, &ctm_only(pte->pis), &wanted);
962*593dc095SDavid du Colombier 	} else {
963*593dc095SDavid du Colombier 	    gs_distance_transform(cw.real_width.xy.x * scale,
964*593dc095SDavid du Colombier 				  cw.real_width.xy.y * scale,
965*593dc095SDavid du Colombier 				  &ppts->values.matrix, &wanted);
966*593dc095SDavid du Colombier 	    if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
967*593dc095SDavid du Colombier 		gs_distance_transform(pte->text.delta_all.x,
968*593dc095SDavid du Colombier 				      pte->text.delta_all.y,
969*593dc095SDavid du Colombier 				      &ctm_only(pte->pis), &tpt);
970*593dc095SDavid du Colombier 		wanted.x += tpt.x;
971*593dc095SDavid du Colombier 		wanted.y += tpt.y;
972*593dc095SDavid du Colombier 	    }
973*593dc095SDavid du Colombier 	    if (chr == space_char && pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
974*593dc095SDavid du Colombier 		gs_distance_transform(pte->text.delta_space.x,
975*593dc095SDavid du Colombier 				      pte->text.delta_space.y,
976*593dc095SDavid du Colombier 				      &ctm_only(pte->pis), &tpt);
977*593dc095SDavid du Colombier 		wanted.x += tpt.x;
978*593dc095SDavid du Colombier 		wanted.y += tpt.y;
979*593dc095SDavid du Colombier 	    }
980*593dc095SDavid du Colombier 	}
981*593dc095SDavid du Colombier 	total.x += wanted.x;
982*593dc095SDavid du Colombier 	total.y += wanted.y;
983*593dc095SDavid du Colombier 	if (wanted.x != did.x || wanted.y != did.y) {
984*593dc095SDavid du Colombier 	    ppts->values.matrix.tx = start.x + total.x;
985*593dc095SDavid du Colombier 	    ppts->values.matrix.ty = start.y + total.y;
986*593dc095SDavid du Colombier 	    code = pdf_set_text_state_values(pdev, &ppts->values);
987*593dc095SDavid du Colombier 	    if (code < 0)
988*593dc095SDavid du Colombier 		break;
989*593dc095SDavid du Colombier 	}
990*593dc095SDavid du Colombier 	pdev->charproc_just_accumulated = false;
991*593dc095SDavid du Colombier     }
992*593dc095SDavid du Colombier     *pdpt = total;
993*593dc095SDavid du Colombier     return code;
994*593dc095SDavid du Colombier }
995*593dc095SDavid du Colombier 
996*593dc095SDavid du Colombier /*
997*593dc095SDavid du Colombier  * Get character code from a glyph code.
998*593dc095SDavid du Colombier  * An usage of this function is very undesirable,
999*593dc095SDavid du Colombier  * because a glyph may be unlisted in Encoding.
1000*593dc095SDavid du Colombier  */
1001*593dc095SDavid du Colombier int
pdf_encode_glyph(gs_font_base * bfont,gs_glyph glyph0,byte * buf,int buf_size,int * char_code_length)1002*593dc095SDavid du Colombier pdf_encode_glyph(gs_font_base *bfont, gs_glyph glyph0,
1003*593dc095SDavid du Colombier 	    byte *buf, int buf_size, int *char_code_length)
1004*593dc095SDavid du Colombier {
1005*593dc095SDavid du Colombier     gs_char c;
1006*593dc095SDavid du Colombier 
1007*593dc095SDavid du Colombier     *char_code_length = 1;
1008*593dc095SDavid du Colombier     if (*char_code_length > buf_size)
1009*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck); /* Must not happen. */
1010*593dc095SDavid du Colombier     for (c = 0; c < 255; c++) {
1011*593dc095SDavid du Colombier 	gs_glyph glyph1 = bfont->procs.encode_char((gs_font *)bfont, c,
1012*593dc095SDavid du Colombier 		    GLYPH_SPACE_NAME);
1013*593dc095SDavid du Colombier 	if (glyph1 == glyph0) {
1014*593dc095SDavid du Colombier 	    buf[0] = (byte)c;
1015*593dc095SDavid du Colombier 	    return 0;
1016*593dc095SDavid du Colombier 	}
1017*593dc095SDavid du Colombier     }
1018*593dc095SDavid du Colombier     return_error(gs_error_rangecheck); /* Can't encode. */
1019*593dc095SDavid du Colombier }
1020*593dc095SDavid du Colombier 
1021*593dc095SDavid du Colombier /* ---------------- Type 1 or TrueType font ---------------- */
1022*593dc095SDavid du Colombier 
1023*593dc095SDavid du Colombier /*
1024*593dc095SDavid du Colombier  * Process a text string in a simple font.
1025*593dc095SDavid du Colombier  */
1026*593dc095SDavid du Colombier int
process_plain_text(gs_text_enum_t * pte,void * vbuf,uint bsize)1027*593dc095SDavid du Colombier process_plain_text(gs_text_enum_t *pte, void *vbuf, uint bsize)
1028*593dc095SDavid du Colombier {
1029*593dc095SDavid du Colombier     byte *const buf = vbuf;
1030*593dc095SDavid du Colombier     uint count;
1031*593dc095SDavid du Colombier     uint operation = pte->text.operation;
1032*593dc095SDavid du Colombier     pdf_text_enum_t *penum = (pdf_text_enum_t *)pte;
1033*593dc095SDavid du Colombier     int code;
1034*593dc095SDavid du Colombier     gs_string str;
1035*593dc095SDavid du Colombier     pdf_text_process_state_t text_state;
1036*593dc095SDavid du Colombier     const gs_glyph *gdata = NULL;
1037*593dc095SDavid du Colombier 
1038*593dc095SDavid du Colombier     if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) {
1039*593dc095SDavid du Colombier 	count = pte->text.size - pte->index;
1040*593dc095SDavid du Colombier 	if (bsize < count)
1041*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
1042*593dc095SDavid du Colombier 	memcpy(buf, (const byte *)pte->text.data.bytes + pte->index, count);
1043*593dc095SDavid du Colombier     } else if (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)) {
1044*593dc095SDavid du Colombier 	/* Check that all chars fit in a single byte. */
1045*593dc095SDavid du Colombier 	const gs_char *cdata;
1046*593dc095SDavid du Colombier 	int i;
1047*593dc095SDavid du Colombier 
1048*593dc095SDavid du Colombier 	if (operation & TEXT_FROM_CHARS) {
1049*593dc095SDavid du Colombier 	    cdata = pte->text.data.chars;
1050*593dc095SDavid du Colombier 	    count = (pte->text.size - pte->index);
1051*593dc095SDavid du Colombier 	} else {
1052*593dc095SDavid du Colombier 	    cdata = &pte->text.data.d_char;
1053*593dc095SDavid du Colombier 	    count = 1;
1054*593dc095SDavid du Colombier 	}
1055*593dc095SDavid du Colombier 	if (bsize < count * sizeof(gs_char))
1056*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
1057*593dc095SDavid du Colombier 	for (i = 0; i < count; ++i) {
1058*593dc095SDavid du Colombier 	    gs_char chr = cdata[pte->index + i];
1059*593dc095SDavid du Colombier 
1060*593dc095SDavid du Colombier 	    if (chr & ~0xff)
1061*593dc095SDavid du Colombier 		return_error(gs_error_rangecheck);
1062*593dc095SDavid du Colombier 	    buf[i] = (byte)chr;
1063*593dc095SDavid du Colombier 	}
1064*593dc095SDavid du Colombier     } else if (operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH)) {
1065*593dc095SDavid du Colombier 	/*
1066*593dc095SDavid du Colombier 	 * Since PDF has no analogue of 'glyphshow',
1067*593dc095SDavid du Colombier 	 * we try to encode glyphs with the current
1068*593dc095SDavid du Colombier 	 * font's encoding. If the current font has no encoding,
1069*593dc095SDavid du Colombier 	 * or the encoding doesn't contain necessary glyphs,
1070*593dc095SDavid du Colombier 	 * the text will be represented with a Type 3 font with
1071*593dc095SDavid du Colombier 	 * bitmaps or outlines.
1072*593dc095SDavid du Colombier 	 *
1073*593dc095SDavid du Colombier 	 * When we fail with encoding (136-01.ps is an example),
1074*593dc095SDavid du Colombier 	 * we could locate a PDF font resource or create a new one
1075*593dc095SDavid du Colombier 	 * with same outlines and an appropriate encoding.
1076*593dc095SDavid du Colombier 	 * Also we could change .notdef entries in the
1077*593dc095SDavid du Colombier 	 * copied font (assuming that document designer didn't use
1078*593dc095SDavid du Colombier 	 * .notdef for a meanful printing).
1079*593dc095SDavid du Colombier 	 * fixme: Not implemented yet.
1080*593dc095SDavid du Colombier 	 */
1081*593dc095SDavid du Colombier 	gs_font *font = pte->current_font;
1082*593dc095SDavid du Colombier 	uint size;
1083*593dc095SDavid du Colombier 	int i;
1084*593dc095SDavid du Colombier 
1085*593dc095SDavid du Colombier 	if (operation & TEXT_FROM_GLYPHS) {
1086*593dc095SDavid du Colombier 	    gdata = pte->text.data.glyphs;
1087*593dc095SDavid du Colombier 	    size = pte->text.size - pte->index;
1088*593dc095SDavid du Colombier 	} else {
1089*593dc095SDavid du Colombier 	    gdata = &pte->text.data.d_glyph;
1090*593dc095SDavid du Colombier 	    size = 1;
1091*593dc095SDavid du Colombier 	}
1092*593dc095SDavid du Colombier 	if (!pdf_is_simple_font(font))
1093*593dc095SDavid du Colombier 	    return_error(gs_error_unregistered); /* Must not happen. */
1094*593dc095SDavid du Colombier 	count = 0;
1095*593dc095SDavid du Colombier 	for (i = 0; i < size; ++i) {
1096*593dc095SDavid du Colombier 	    gs_glyph glyph = gdata[pte->index + i];
1097*593dc095SDavid du Colombier 	    int char_code_length;
1098*593dc095SDavid du Colombier 
1099*593dc095SDavid du Colombier 	    code = pdf_encode_glyph((gs_font_base *)font, glyph,
1100*593dc095SDavid du Colombier 			 buf + count, size - count, &char_code_length);
1101*593dc095SDavid du Colombier 	    if (code < 0)
1102*593dc095SDavid du Colombier 		break;
1103*593dc095SDavid du Colombier 	    count += char_code_length;
1104*593dc095SDavid du Colombier 	    if (operation & TEXT_INTERVENE)
1105*593dc095SDavid du Colombier 		break; /* Just do one character. */
1106*593dc095SDavid du Colombier 	}
1107*593dc095SDavid du Colombier 	if (i < size) {
1108*593dc095SDavid du Colombier 	    pdf_font_resource_t *pdfont;
1109*593dc095SDavid du Colombier 
1110*593dc095SDavid du Colombier 	    str.data = buf;
1111*593dc095SDavid du Colombier 	    str.size = size;
1112*593dc095SDavid du Colombier 	    if (pdf_obtain_font_resource_unencoded(penum, &str, &pdfont, gdata) != 0) {
1113*593dc095SDavid du Colombier 		/*
1114*593dc095SDavid du Colombier 		 * pdf_text_process will fall back
1115*593dc095SDavid du Colombier 		 * to default implementation.
1116*593dc095SDavid du Colombier 		 */
1117*593dc095SDavid du Colombier 		return code;
1118*593dc095SDavid du Colombier 	    }
1119*593dc095SDavid du Colombier 	    count = size;
1120*593dc095SDavid du Colombier 	}
1121*593dc095SDavid du Colombier 	/*  So far we will use TEXT_FROM_STRING instead
1122*593dc095SDavid du Colombier 	    TEXT_FROM_*_GLYPH*. Since we used a single
1123*593dc095SDavid du Colombier 	    byte encoding, the character index appears invariant
1124*593dc095SDavid du Colombier 	    during this substitution.
1125*593dc095SDavid du Colombier 	 */
1126*593dc095SDavid du Colombier     } else
1127*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
1128*593dc095SDavid du Colombier     str.data = buf;
1129*593dc095SDavid du Colombier     if (count > 1 && (operation & TEXT_INTERVENE)) {
1130*593dc095SDavid du Colombier 	/* Just do one character. */
1131*593dc095SDavid du Colombier 	str.size = 1;
1132*593dc095SDavid du Colombier 	code = pdf_encode_process_string(penum, &str, gdata, NULL, &text_state);
1133*593dc095SDavid du Colombier 	if (code >= 0) {
1134*593dc095SDavid du Colombier 	    pte->returned.current_char = buf[0];
1135*593dc095SDavid du Colombier 	    code = TEXT_PROCESS_INTERVENE;
1136*593dc095SDavid du Colombier 	}
1137*593dc095SDavid du Colombier     } else {
1138*593dc095SDavid du Colombier 	str.size = count;
1139*593dc095SDavid du Colombier 	code = pdf_encode_process_string(penum, &str, gdata, NULL, &text_state);
1140*593dc095SDavid du Colombier     }
1141*593dc095SDavid du Colombier     return code;
1142*593dc095SDavid du Colombier }
1143