1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 /*$Id: gdevtxtw.c 7795 2007-03-23 13:56:11Z tim $ */
17 /* Device for Unicode (UTF-8 or UCS2) text extraction */
18 #include "memory_.h"
19 #include "string_.h"
20 #include "gp.h"			/* for gp_file_name_sizeof */
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsparam.h"
24 #include "gsutil.h"
25 #include "gxdevice.h"
26 #include "gsdevice.h"		/* requires gsmatrix.h */
27 #include "gxfont.h"
28 #include "gxfont0.h"
29 #include "gstext.h"
30 #include "gxfcid.h"
31 #include "gxistate.h"
32 #include "gxpath.h"
33 #include "gdevagl.h"
34 
35 /* #define TRACE_TXTWRITE 1 */
36 
37 extern single_glyph_list_t *SingleGlyphList;
38 extern double_glyph_list_t *DoubleGlyphList;
39 extern treble_glyph_list_t *TrebleGlyphList;
40 extern quad_glyph_list_t *QuadGlyphList;
41 /*
42  * Define the structure used to return glyph width information.  Note that
43  * there are two different sets of width information: real-number (x,y)
44  * values, which give the true advance width, and an integer value, which
45  * gives an X advance width for WMode = 0 or a Y advance width for WMode = 1.
46  * The return value from txt_glyph_width() indicates which of these is/are
47  * valid.
48  */
49 typedef struct txt_glyph_width_s {
50     double w;
51     gs_point xy;
52     gs_point v;				/* glyph origin shift */
53 } txt_glyph_width_t;
54 typedef struct txt_glyph_widths_s {
55     txt_glyph_width_t Width;		/* unmodified, for Widths */
56     txt_glyph_width_t real_width;	/* possibly modified, for rendering */
57     bool replaced_v;
58 } txt_glyph_widths_t;
59 
60 /* Structure to record the Unicode characters, the total width of the text
61  * recorded, and various useful attributes such as the font, size, colour
62  * rendering mode, writing mode etc. These are stored as a series of x-ordered
63  * entries in a list, using the starting x co-ordinate.
64  */
65 typedef struct text_list_entry_s {
66     struct text_list_entry_s *previous;
67     struct text_list_entry_s *next;
68 
69     gs_point start;
70     gs_point end;
71     gs_point FontBBox_bottomleft, FontBBox_topright;
72     float *Widths;
73     unsigned short *Unicode_Text;
74     int Unicode_Text_Size;
75     int render_mode;
76 
77     gs_matrix matrix;		/* Tm et al */
78 
79     gs_font *font;
80     char *FontName;
81     int wmode;			/* WMode of font */
82     double PaintType0Width;
83     double size;
84 } text_list_entry_t;
85 
86 /* Structure to maintain a list of text fragments, ordered by X co-ordinate.
87  * These structures are themselves maintained in a Y-ordered list.
88  */
89 typedef struct page_text_list_s {
90     struct page_text_list_s *previous;
91     struct page_text_list_s *next;
92     gs_point start;
93     float MinY, MaxY;
94     text_list_entry_t *x_ordered_list;
95 } page_text_list_t;
96 
97 /* A simple structure to maintain the lists of text fragments, it is also
98  * a convenient place to record the page number and anything else we may
99  * want to record that is relevant to the page rather than the text.
100  */
101 typedef struct page_text_s {
102     int PageNum;
103     page_text_list_t *y_ordered_list;
104     text_list_entry_t *unsorted_text_list;
105 } page_text_t;
106 
107 /* The custom sub-classed device structure */
108 typedef struct gx_device_txtwrite_s {
109     gx_device_common;
110     page_text_t PageData;
111     char fname[gp_file_name_sizeof];	/* OutputFile */
112     FILE *file;
113     int TextFormat;
114 #ifdef TRACE_TXTWRITE
115     FILE *DebugFile;
116 #endif
117 } gx_device_txtwrite_t;
118 
119 /* GC descriptor */
120 gs_private_st_suffix_add0_final(st_device_txtwrite, gx_device_txtwrite_t,
121    "gx_device_txtwrite", device_txtwrite_enum_ptrs, device_txtwrite_reloc_ptrs,
122     gx_device_finalize, st_device_forward);
123 
124 /* Device procedures */
125 static dev_proc_open_device(txtwrite_open_device);
126 static dev_proc_close_device(txtwrite_close_device);
127 static dev_proc_output_page(txtwrite_output_page);
128 static dev_proc_fill_rectangle(txtwrite_fill_rectangle);
129 static dev_proc_get_params(txtwrite_get_params);
130 static dev_proc_put_params(txtwrite_put_params);
131 static dev_proc_fill_path(txtwrite_fill_path);
132 static dev_proc_stroke_path(txtwrite_stroke_path);
133 static dev_proc_text_begin(txtwrite_text_begin);
134 static dev_proc_strip_copy_rop(txtwrite_strip_copy_rop);
135 
136 /* The device prototype */
137 #define X_DPI 72
138 #define Y_DPI 72
139 
140 /* Define the text enumerator. */
141 typedef struct textw_text_enum_s {
142     gs_text_enum_common;
143     gs_fixed_point origin;
144     bool charproc_accum;
145     bool cdevproc_callout;
146     double cdevproc_result[10];
147     float *Widths;
148     unsigned short *TextBuffer;
149     int TextBufferIndex;
150     text_list_entry_t *text_state;
151 } textw_text_enum_t;
152 #define private_st_textw_text_enum()\
153   extern_st(st_gs_text_enum);\
154   gs_private_st_suffix_add0(st_textw_text_enum, textw_text_enum_t,\
155     "textw_text_enum_t", textw_text_enum_enum_ptrs, textw_text_enum_reloc_ptrs,\
156     st_gs_text_enum)
157 
158 private_st_textw_text_enum();
159 
160 const gx_device_txtwrite_t gs_txtwrite_device =
161 {
162     /* Define the device as 8-bit gray scale to avoid computing halftones. */
163     std_device_dci_body(gx_device_txtwrite_t, 0, "txtwrite",
164                         DEFAULT_WIDTH_10THS * X_DPI / 10,
165                         DEFAULT_HEIGHT_10THS * Y_DPI / 10,
166                         X_DPI, Y_DPI,
167                         1, 8, 255, 0, 256, 1),
168     {txtwrite_open_device,
169      NULL, /*gx_upright_get_initial_matrix,*/
170      NULL, /*gx_default_sync_output,*/
171      txtwrite_output_page,
172      txtwrite_close_device,
173      NULL, /*gx_default_gray_map_rgb_color,*/
174      NULL, /*gx_default_gray_map_color_rgb,*/
175      txtwrite_fill_rectangle,               /* Can't be NULL and there is no gx_default_fill_rectangle! */
176      NULL, /*gx_default_tile_rectangle,*/
177      NULL, /*gx_default_copy_mono,*/
178      NULL, /*gx_default_copy_color,*/
179      NULL, /*gx_default_draw_line,*/
180      NULL, /*gx_default_get_bits,*/
181      txtwrite_get_params,
182      txtwrite_put_params,
183      NULL, /*gx_default_map_cmyk_color,*/
184      NULL, /*gx_default_get_xfont_procs,*/
185      NULL, /*gx_default_get_xfont_device,*/
186      NULL, /*gx_default_map_rgb_alpha_color,*/
187      gx_page_device_get_page_device, /*gx_page_device_get_page_device,*/
188      NULL,			/* get_alpha_bits */
189      NULL, /*gx_default_copy_alpha,*/
190      NULL,			/* get_band */
191      NULL,			/* copy_rop */
192      txtwrite_fill_path,
193      txtwrite_stroke_path,
194      NULL, /*gx_default_fill_mask,*/
195      NULL, /*gx_default_fill_trapezoid,*/
196      NULL, /*gx_default_fill_parallelogram,*/
197      NULL, /*gx_default_fill_triangle,*/
198      NULL, /*gx_default_draw_thin_line,*/
199      NULL,                      /* begin image */
200      NULL,			/* image_data */
201      NULL,			/* end_image */
202      NULL, /*gx_default_strip_tile_rectangle,*/
203      txtwrite_strip_copy_rop,
204      NULL,			/* get_clipping_box */
205      NULL, /* txtwrite_begin_typed_image */
206      NULL,			/* get_bits_rectangle */
207      NULL, /*gx_default_map_color_rgb_alpha,*/
208      gx_null_create_compositor,
209      NULL,			/* get_hardware_params */
210      txtwrite_text_begin,
211      NULL,			/* finish_copydevice */
212      NULL,			/* begin_transparency_group */
213      NULL,			/* end_transparency_group */
214      NULL,			/* begin_transparency_mask */
215      NULL,			/* end_transparency_mask */
216      NULL,			/* discard_transparency_layer */
217      NULL,			/* get_color_mapping_procs */
218      NULL,			/* get_color_comp_index */
219      NULL,			/* encode_color */
220      NULL,			/* decode_color */
221      NULL,                      /* pattern manager */
222      NULL,                      /* fill_rectangle_hl_color */
223      NULL,                      /* include_color_space */
224      NULL,                      /* fill_linear_color_scanline */
225      NULL,                      /* fill_linear_color_trapezoid */
226      NULL,                      /* fill_linear_color_triangle */
227      NULL,                      /* update_spot_equivalent_colors */
228      NULL,                      /* ret_devn_params */
229      NULL,                      /* fillpage */
230      NULL,                      /* push_transparency_state */
231      NULL,                      /* pop_transparency_state */
232      NULL,                      /* put_image */
233      NULL,                      /* dev_spec_op */
234      NULL,                      /* copy_planes */
235      NULL,                      /* get_profile */
236      NULL,                      /* set_graphics_type_tag */
237      NULL,                      /* strip_copy_rop2 */
238      NULL                       /* strip_tile_rect_devn */
239     },
240     { 0 },			/* Page Data */
241     { 0 },			/* Output Filename */
242     0,				/* Output FILE * */
243     3				/* TextFormat */
244 };
245 
246 #ifndef gx_device_textw_DEFINED
247 #  define gx_device_textw_DEFINED
248 typedef struct gx_device_textwrite_s gx_device_textw;
249 #endif
250 
251 static const gs_param_item_t txt_param_items[] = {
252 #define pi(key, type, memb) { key, type, offset_of(gx_device_txtwrite_t, memb) }
253     pi("TextFormat", gs_param_type_int, TextFormat),
254 #undef pi
255     gs_param_item_end
256 };
257 
258 /* ---------------- Open/close/page ---------------- */
259 
260 static int
txtwrite_open_device(gx_device * dev)261 txtwrite_open_device(gx_device * dev)
262 {
263     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
264 
265     gx_device_fill_in_procs(dev);
266     if (tdev->fname[0] == 0)
267         return_error(gs_error_undefinedfilename);
268 
269     tdev->PageData.PageNum = 0;
270     tdev->PageData.y_ordered_list = NULL;
271     tdev->file = NULL;
272 #ifdef TRACE_TXTWRITE
273     tdev->DebugFile = fopen("/temp/txtw_dbg.txt", "wb+");
274 #endif
275     return 0;
276 }
277 
278 static int
txtwrite_close_device(gx_device * dev)279 txtwrite_close_device(gx_device * dev)
280 {
281     int code = 0;
282     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
283 
284     if (tdev->file)
285         code = gx_device_close_output_file(dev, tdev->fname, tdev->file);
286 
287 #ifdef TRACE_TXTWRITE
288     fclose(tdev->DebugFile);
289 #endif
290     return code;
291 }
292 
293 /* Routine inspects horizontal lines of text to see if they can be collapsed
294  * into a single line. This essentially detects superscripts and subscripts
295  * as well as lines which are slightly mis-aligned.
296  */
merge_vertically(gx_device_txtwrite_t * tdev)297 static int merge_vertically(gx_device_txtwrite_t *tdev)
298 {
299 #ifdef TRACE_TXTWRITE
300     text_list_entry_t *debug_x;
301 #endif
302     page_text_list_t *y_list = tdev->PageData.y_ordered_list;
303 
304     while (y_list && y_list->next) {
305         page_text_list_t *next = y_list->next;
306         bool collision = false;
307         float overlap = (y_list->start.y + y_list->MaxY) - (next->start.y + next->MinY);
308 
309         if (overlap >= (y_list->MaxY - y_list->MinY) / 4) {
310             /* At least a 25% overlap, lets test for x collisions */
311             text_list_entry_t *upper = y_list->x_ordered_list, *lower;
312             while (upper && !collision) {
313                 lower = next->x_ordered_list;
314                 while (lower && !collision) {
315                     if (upper->start.x >= lower->start.x) {
316                         if (upper->start.x <= lower->end.x) {
317                             /* Collision */
318                             collision = true;
319                             break;
320                         }
321                     } else {
322                         if (upper->end.x > lower->start.x) {
323                             /* Collision */
324                             collision = true;
325                             break;
326                         }
327                     }
328                     lower = lower->next;
329                 }
330                 upper = upper->next;
331             }
332             if (!collision) {
333                 text_list_entry_t *from, *to, *new_order, *current;
334                 /* Consolidate y lists */
335                 to = y_list->x_ordered_list;
336                 from = next->x_ordered_list;
337 #ifdef TRACE_TXTWRITE
338                 fprintf(tdev->DebugFile, "\nConsolidating two horizontal lines, line 1:");
339                 debug_x = from;
340                 while (debug_x) {
341                     fprintf(tdev->DebugFile, "\n\t");
342                     fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
343                     debug_x = debug_x->next;
344                 }
345                 fprintf(tdev->DebugFile, "\nConsolidating two horizontal lines, line 2");
346                 debug_x = to;
347                 while (debug_x) {
348                     fprintf(tdev->DebugFile, "\n\t");
349                     fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
350                     debug_x = debug_x->next;
351                 }
352 #endif
353                 if (from->start.x < to->start.x) {
354                     current = new_order = from;
355                     from = from->next;
356                 } else {
357                     current = new_order = to;
358                     to = to->next;
359                 }
360                 while (to && from) {
361                     if (to->start.x < from->start.x) {
362                         current->next = to;
363                         to->previous = current;
364                         to = to->next;
365                     } else {
366                         current->next = from;
367                         from->previous = current;
368                         from = from->next;
369                     }
370                     current = current->next;
371                 }
372                 if (to) {
373                     to->previous = current;
374                     current->next = to;
375                 } else {
376                     if (from) {
377                         from->previous = current;
378                         current->next = from;
379                     }
380                 }
381                 y_list->x_ordered_list = new_order;
382 #ifdef TRACE_TXTWRITE
383                 fprintf(tdev->DebugFile, "\nAfter:");
384                 debug_x = new_order;
385                 while (debug_x) {
386                     fprintf(tdev->DebugFile, "\n\t");
387                     fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
388                     debug_x = debug_x->next;
389                 }
390                 fprintf(tdev->DebugFile, "\n");
391 #endif
392                 y_list->next = next->next;
393                 if (next->next)
394                     next->next->previous = y_list;
395                 gs_free(tdev->memory, next, 1, sizeof(page_text_list_entry_t), "txtwrite free text list");
396             } else
397                 y_list = next;
398         } else
399             y_list = next;
400     }
401     return 0;
402 }
403 
404 /* Routine to merge horizontally adjacent text fragments. If the distance
405  * between two horizontal fragments is small, then they are treated as one
406  * frament of text, if its larger then we insert a space (and set the Width
407  * entry appropriately). Otherwise we leave them as separate.
408  */
merge_horizontally(gx_device_txtwrite_t * tdev)409 static int merge_horizontally(gx_device_txtwrite_t *tdev)
410 {
411 #ifdef TRACE_TXTWRITE
412     text_list_entry_t *debug_x;
413 #endif
414     unsigned short UnicodeSpace = 0x20;
415     page_text_list_t *y_list = tdev->PageData.y_ordered_list;
416 
417     while (y_list) {
418         float average_width;
419         text_list_entry_t *from, *to;
420         from = y_list->x_ordered_list;
421         to = from->next;
422 
423         while (from && to) {
424             average_width = (from->end.x - from->start.x) / from->Unicode_Text_Size;
425 
426             if (to->start.x - from->end.x < average_width / 2) {
427                 /* consolidate fragments */
428                 unsigned short *NewText;
429                 float *NewWidths;
430 
431                 NewText = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
432                     (from->Unicode_Text_Size + to->Unicode_Text_Size), sizeof(unsigned short), "txtwrite alloc working text buffer");
433                 NewWidths = (float *)gs_malloc(tdev->memory->stable_memory,
434                     (from->Unicode_Text_Size + to->Unicode_Text_Size), sizeof(float), "txtwrite alloc Widths array");
435                 if (!NewText || !NewWidths) {
436                     if (NewText)
437                         gs_free(tdev->memory, NewText, from->Unicode_Text_Size + to->Unicode_Text_Size, sizeof (unsigned short), "free working text fragment");
438                     /* ran out of memory, don't consolidate */
439                     from = from->next;
440                     to = to->next;
441                 } else {
442 #ifdef TRACE_TXTWRITE
443                     fprintf(tdev->DebugFile, "Consolidating two horizontal fragments in one line, before:\n\t");
444                     fwrite(from->Unicode_Text, sizeof(unsigned short), from->Unicode_Text_Size, tdev->DebugFile);
445                     fprintf(tdev->DebugFile, "\n\t");
446                     fwrite(to->Unicode_Text, sizeof(unsigned short), to->Unicode_Text_Size, tdev->DebugFile);
447 #endif
448                     memcpy(NewText, from->Unicode_Text, from->Unicode_Text_Size * sizeof(unsigned short));
449                     memcpy(&NewText[from->Unicode_Text_Size], to->Unicode_Text, to->Unicode_Text_Size * sizeof(unsigned short));
450                     memcpy(NewWidths, from->Widths, from->Unicode_Text_Size * sizeof(float));
451                     memcpy(&NewWidths[from->Unicode_Text_Size], to->Widths, to->Unicode_Text_Size * sizeof(float));
452                     gs_free(tdev->memory, from->Unicode_Text, from->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
453                     gs_free(tdev->memory, to->Unicode_Text, to->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
454                     gs_free(tdev->memory, from->Widths, from->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
455                     gs_free(tdev->memory, to->Widths, to->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
456                     gs_free(tdev->memory, to->FontName, 1, strlen(from->FontName) + 1, "free FontName");
457                     from->Unicode_Text = NewText;
458                     from->Unicode_Text_Size += to->Unicode_Text_Size;
459                     from->Widths = NewWidths;
460 #ifdef TRACE_TXTWRITE
461                     fprintf(tdev->DebugFile, "After:\n\t");
462                     fwrite(from->Unicode_Text, sizeof(unsigned short), from->Unicode_Text_Size, tdev->DebugFile);
463 #endif
464                     from->end = to->end;
465                     from->next = to->next;
466                     if (from->next)
467                         from->next->previous = from;
468                     gs_free(tdev->memory, to, 1, sizeof(text_list_entry_t), "free consolidated fragment");
469                     to = from->next;
470                 }
471             } else {
472                 if (to->start.x - from->end.x < average_width *2){
473                     unsigned short *NewText;
474                     float *NewWidths;
475 
476                     NewText = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
477                         (from->Unicode_Text_Size + to->Unicode_Text_Size + 1), sizeof(unsigned short), "txtwrite alloc text state");
478                     NewWidths = (float *)gs_malloc(tdev->memory->stable_memory,
479                         (from->Unicode_Text_Size + to->Unicode_Text_Size + 1), sizeof(float), "txtwrite alloc Widths array");
480                     if (!NewText || !NewWidths) {
481                         if (NewText)
482                             gs_free(tdev->memory, NewText, from->Unicode_Text_Size + to->Unicode_Text_Size, sizeof (unsigned short), "free working text fragment");
483                         /* ran out of memory, don't consolidate */
484                         from = from->next;
485                         to = to->next;
486                     } else {
487                         memcpy(NewText, from->Unicode_Text, from->Unicode_Text_Size * sizeof(unsigned short));
488                         memcpy(&NewText[from->Unicode_Text_Size], &UnicodeSpace, sizeof(unsigned short));
489                         memcpy(&NewText[from->Unicode_Text_Size + 1], to->Unicode_Text, to->Unicode_Text_Size * sizeof(unsigned short));
490                         memcpy(NewWidths, from->Widths, from->Unicode_Text_Size * sizeof(float));
491                         NewWidths[from->Unicode_Text_Size] = to->start.x - from->end.x;
492                         memcpy(&NewWidths[from->Unicode_Text_Size + 1], to->Widths, to->Unicode_Text_Size * sizeof(float));
493                         gs_free(tdev->memory, from->Unicode_Text, from->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
494                         gs_free(tdev->memory, to->Unicode_Text, to->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
495                         gs_free(tdev->memory, from->Widths, from->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
496                         gs_free(tdev->memory, to->Widths, to->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
497                         gs_free(tdev->memory, to->FontName, 1, strlen(from->FontName) + 1, "free FontName");
498                         from->Unicode_Text = NewText;
499                         from->Unicode_Text_Size += to->Unicode_Text_Size + 1;
500                         from->Widths = NewWidths;
501                         from->end = to->end;
502                         from->next = to->next;
503                         if (from->next)
504                             from->next->previous = from;
505                         gs_free(tdev->memory, to, 1, sizeof(text_list_entry_t), "free consolidated fragment");
506                         to = from->next;
507                     }
508                 } else {
509                     from = from->next;
510                     to = to->next;
511                 }
512             }
513         }
514         y_list = y_list->next;
515     }
516     return 0;
517 }
518 
write_simple_text(unsigned short * text,int count,gx_device_txtwrite_t * tdev)519 static int write_simple_text(unsigned short *text, int count, gx_device_txtwrite_t *tdev)
520 {
521     switch(tdev->TextFormat) {
522         case 2:
523             fwrite(text, sizeof (unsigned short), count, tdev->file);
524             break;
525         case 3:
526             {
527                 int i;
528                 unsigned short *UTF16 = (unsigned short *)text;
529                 unsigned char UTF8[3];
530 
531                 for (i=0;i<count;i++) {
532                     if (*UTF16 < 0x80) {
533                         UTF8[0] = *UTF16 & 0xff;
534                         fwrite (UTF8, sizeof(unsigned char), 1, tdev->file);
535                     } else {
536                         if (*UTF16 < 0x800) {
537                             UTF8[0] = (*UTF16 >> 11) + 0xC0;
538                             UTF8[1] = (*UTF16 & 0x3F) + 0x80;
539                             fwrite (UTF8, sizeof(unsigned char), 2, tdev->file);
540                         } else {
541                             UTF8[0] = (*UTF16 >> 12) + 0xE0;
542                             UTF8[1] = ((*UTF16 >> 6) & 0x3F) + 0x80;
543                             UTF8[2] = (*UTF16 & 0x3F) + 0x80;
544                             fwrite (UTF8, sizeof(unsigned char), 3, tdev->file);
545                         }
546                     }
547                     UTF16++;
548                 }
549             }
550             break;
551         default:
552             return gs_note_error(gs_error_rangecheck);
553             break;
554     }
555     return 0;
556 }
557 
simple_text_output(gx_device_txtwrite_t * tdev)558 static int simple_text_output(gx_device_txtwrite_t *tdev)
559 {
560     int chars_wide;
561     float char_size, min_size, min_width_size;
562 #ifdef TRACE_TXTWRITE
563     text_list_entry_t *debug_x;
564 #endif
565     text_list_entry_t * x_entry;
566     page_text_list_t *y_list;
567     unsigned short UnicodeSpace = 0x20, UnicodeEOL[2] = {0x00D, 0x0a};
568 
569     merge_vertically(tdev);
570 
571     merge_horizontally(tdev);
572 
573     min_size = (float)tdev->width;
574     /* Estimate maximum text density */
575     y_list = tdev->PageData.y_ordered_list;
576     while (y_list) {
577         x_entry = y_list->x_ordered_list;
578         while (x_entry) {
579             if (x_entry->size < min_size)
580                 min_size = x_entry->size;
581             x_entry = x_entry->next;
582         }
583         y_list = y_list->next;
584     }
585 
586     min_width_size = min_size;
587     y_list = tdev->PageData.y_ordered_list;
588     while (y_list) {
589         float width;
590 
591         x_entry = y_list->x_ordered_list;
592         while (x_entry) {
593             width = (x_entry->end.x - x_entry->start.x) / x_entry->Unicode_Text_Size;
594             if (width < min_width_size && width >= (float)min_size * 0.75)
595                 min_width_size = width;
596             x_entry = x_entry->next;
597         }
598         y_list = y_list->next;
599     }
600 
601     min_size = min_width_size;
602     chars_wide = (int)ceil(tdev->width / min_size);
603     char_size = (float)tdev->width / (float)chars_wide;
604 
605     y_list = tdev->PageData.y_ordered_list;
606     while (y_list) {
607         float xpos = 0;
608         x_entry = y_list->x_ordered_list;
609         while (x_entry) {
610             while (xpos < x_entry->start.x) {
611                 write_simple_text(&UnicodeSpace, 1, tdev);
612                 xpos += char_size;
613             }
614             write_simple_text(x_entry->Unicode_Text, x_entry->Unicode_Text_Size, tdev);
615             xpos += x_entry->Unicode_Text_Size * char_size;
616             if (x_entry->next) {
617                 x_entry = x_entry->next;
618             } else {
619                 x_entry = NULL;
620             }
621         }
622         write_simple_text((unsigned short *)&UnicodeEOL, 2, tdev);
623         if (y_list->next) {
624             y_list = y_list->next;
625         } else {
626             y_list = NULL;
627         }
628     }
629     return 0;
630 }
631 
escaped_Unicode(unsigned short Unicode,char * Buf)632 static int escaped_Unicode (unsigned short Unicode, char *Buf)
633 {
634     switch (Unicode)
635     {
636     case 0x3C: sprintf(Buf, "&lt;"); break;
637     case 0x3E: sprintf(Buf, "&gt;"); break;
638     case 0x26: sprintf(Buf, "&amp;"); break;
639     case 0x22: sprintf(Buf, "&quot;"); break;
640     case 0x27: sprintf(Buf, "&apos;"); break;
641     default:
642         if (Unicode >= 32 && Unicode <= 127)
643             sprintf(Buf, "%c", Unicode);
644         else
645             sprintf(Buf, "&#x%x;", Unicode);
646         break;
647     }
648 
649     return 0;
650 }
651 
decorated_text_output(gx_device_txtwrite_t * tdev)652 static int decorated_text_output(gx_device_txtwrite_t *tdev)
653 {
654     int i;
655     text_list_entry_t * x_entry, *next_x;
656     char TextBuffer[512], Escaped[32];
657     float xpos;
658     page_text_list_t *y_list;
659 #ifdef TRACE_TXTWRITE
660     text_list_entry_t *debug_x;
661 #endif
662 
663     if (tdev->TextFormat == 0) {
664         fwrite("<page>\n", sizeof(unsigned char), 7, tdev->file);
665         x_entry = tdev->PageData.unsorted_text_list;
666         while (x_entry) {
667             next_x = x_entry->next;
668             sprintf(TextBuffer, "<span bbox=\"%0.0f %0.0f %0.0f %0.0f\" font=\"%s\" size=\"%0.4f\">\n", x_entry->start.x, x_entry->start.y,
669                 x_entry->end.x, x_entry->end.y, x_entry->FontName,x_entry->size);
670             fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
671             xpos = x_entry->start.x;
672             for (i=0;i<x_entry->Unicode_Text_Size;i++) {
673                 escaped_Unicode(x_entry->Unicode_Text[i], (char *)&Escaped);
674                 sprintf(TextBuffer, "<char bbox=\"%0.0f %0.0f %0.0f %0.0f\" c=\"%s\">\n", xpos,
675                     x_entry->start.y, xpos + x_entry->Widths[i], x_entry->end.y, Escaped);
676                 fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
677                 xpos += x_entry->Widths[i];
678             }
679             fwrite("</span>\n", sizeof(unsigned char), 8, tdev->file);
680 
681             x_entry = next_x;
682         }
683         fwrite("</page>\n", sizeof(unsigned char), 8, tdev->file);
684     } else {
685 
686         merge_vertically(tdev);
687         merge_horizontally(tdev);
688 
689         y_list = tdev->PageData.y_ordered_list;
690         fwrite("<page>\n", sizeof(unsigned char), 7, tdev->file);
691         /* Walk the list looking for 'blocks' */
692         do {
693             page_text_list_t *temp;
694             page_text_t block;
695             page_text_list_t *block_line;
696             float BBox[4];
697 
698             memset(&block, 0x00, sizeof(page_text_t));
699             memset(BBox, 0x00, sizeof(float) * 4);
700 
701             while (y_list) {
702                 if (block.y_ordered_list) {
703                     text_list_entry_t *x_entry = y_list->x_ordered_list;
704 
705                     block_line = block.y_ordered_list;
706                     while (x_entry) {
707                         if (x_entry->start.x > BBox[2] || x_entry->end.x < BBox[0] ||
708                             x_entry->start.y > (BBox[1] + (BBox[3] - BBox[1]))) {
709                                 ;
710                         } else {
711                             block_line->next = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
712                                 sizeof(page_text_list_t), "txtwrite alloc Y-list");
713                             memset(block_line->next, 0x00, sizeof(page_text_list_t));
714                             block_line = block_line->next;
715                             block_line->x_ordered_list = x_entry;
716                             if(x_entry->next)
717                                 x_entry->next->previous = x_entry->previous;
718                             if (x_entry->previous)
719                                 x_entry->previous->next = x_entry->next;
720                             else {
721                                 if (x_entry->next == 0x00) {
722                                     /* remove Y entry */
723                                     temp = y_list->next;
724                                     if (y_list->previous)
725                                         y_list->previous->next = y_list->next;
726                                     if (y_list->next)
727                                         y_list->next->previous = y_list->previous;
728                                     else {
729                                         if (y_list->previous == 0x00) {
730                                             tdev->PageData.y_ordered_list = 0x00;
731                                         }
732                                     }
733                                     gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
734                                     if (tdev->PageData.y_ordered_list == y_list)
735                                         tdev->PageData.y_ordered_list = temp;
736                                     y_list = temp;
737                                     x_entry = x_entry->next;
738                                     continue;
739                                 }
740                             }
741                             if (block_line->x_ordered_list->start.x < BBox[0])
742                                 BBox[0] = block_line->x_ordered_list->start.x;
743                             if (block_line->x_ordered_list->start.y < BBox[1])
744                                 BBox[1] = block_line->x_ordered_list->start.y;
745                             if (block_line->x_ordered_list->end.x < BBox[2])
746                                 BBox[2] = block_line->x_ordered_list->end.x;
747                             if (block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y < BBox[3])
748                                 BBox[3] = block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y;
749                         }
750                         x_entry = x_entry->next;
751                     }
752                 } else {
753                     block.y_ordered_list = block_line = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
754                         sizeof(page_text_list_t), "txtwrite alloc Y-list");
755                     memset(block_line, 0x00, sizeof(page_text_list_t));
756                     block_line->x_ordered_list = y_list->x_ordered_list;
757                     y_list->x_ordered_list = y_list->x_ordered_list->next;
758                     if (y_list->x_ordered_list == 0x00) {
759                         temp = y_list->next;
760                         if (y_list->previous)
761                             y_list->previous->next = y_list->next;
762                         if (y_list->next)
763                             y_list->next->previous = y_list->previous;
764                         else {
765                             if (y_list->previous == 0x00) {
766                                 tdev->PageData.y_ordered_list = 0x00;
767                             }
768                         }
769                         gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
770                         if (tdev->PageData.y_ordered_list == y_list)
771                             tdev->PageData.y_ordered_list = temp;
772                         y_list = temp;
773                         continue;
774                     }
775                     block_line->x_ordered_list->next = block_line->x_ordered_list->previous = 0x00;
776                     BBox[0] = block_line->x_ordered_list->start.x;
777                     BBox[1] = block_line->x_ordered_list->start.y;
778                     BBox[2] = block_line->x_ordered_list->end.x;
779                     BBox[3] = block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y;
780                 }
781                 if (y_list)
782                     y_list = y_list->next;
783             }
784             /* FIXME - need to free the used memory in here */
785             fwrite("<block>\n", sizeof(unsigned char), 8, tdev->file);
786             block_line = block.y_ordered_list;
787             while (block_line) {
788                 fwrite("<line>\n", sizeof(unsigned char), 7, tdev->file);
789                 x_entry = block_line->x_ordered_list;
790                 while(x_entry) {
791                     sprintf(TextBuffer, "<span bbox=\"%0.0f %0.0f %0.0f %0.0f\" font=\"%s\" size=\"%0.4f\">\n", x_entry->start.x, x_entry->start.y,
792                         x_entry->end.x, x_entry->end.y, x_entry->FontName,x_entry->size);
793                     fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
794                     xpos = x_entry->start.x;
795                     for (i=0;i<x_entry->Unicode_Text_Size;i++) {
796                         escaped_Unicode(x_entry->Unicode_Text[i], (char *)&Escaped);
797                         sprintf(TextBuffer, "<char bbox=\"%0.0f %0.0f %0.0f %0.0f\" c=\"%s\">\n", xpos,
798                             x_entry->start.y, xpos + x_entry->Widths[i], x_entry->end.y, Escaped);
799                         fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
800                         xpos += x_entry->Widths[i];
801                     }
802                     fwrite("</span>\n", sizeof(unsigned char), 8, tdev->file);
803                     x_entry = x_entry->next;
804                 }
805                 fwrite("</line>\n", sizeof(unsigned char), 8, tdev->file);
806                 block_line = block_line->next;
807             }
808             fwrite("</block>\n", sizeof(unsigned char), 9, tdev->file);
809             y_list = tdev->PageData.y_ordered_list;
810         } while (y_list);
811 
812         fwrite("</page>\n", sizeof(unsigned char), 8, tdev->file);
813     }
814     return 0;
815 }
816 
817 static int
txtwrite_output_page(gx_device * dev,int num_copies,int flush)818 txtwrite_output_page(gx_device * dev, int num_copies, int flush)
819 {
820     int code;
821     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
822     text_list_entry_t * x_entry, *next_x;
823     page_text_list_t *y_list;
824     gs_parsed_file_name_t parsed;
825     const char *fmt;
826 
827     if (!tdev->file) {
828         /* Either this is the first page, or we're doing one file per page */
829         code = gx_device_open_output_file(dev, tdev->fname,
830                 true, false, &tdev->file); /* binary, sequential */
831         if (code < 0)
832             return code;
833     }
834 
835     switch(tdev->TextFormat) {
836         case 0:
837         case 1:
838             code = decorated_text_output(tdev);
839             if (code < 0)
840                 return code;
841             break;
842 
843         case 2:
844         case 3:
845             code = simple_text_output(tdev);
846             if (code < 0)
847                 return code;
848             break;
849 
850         default:
851             return gs_note_error(gs_error_rangecheck);
852             break;
853     }
854 
855     code =  gx_default_output_page(dev, num_copies, flush);
856     if (code < 0)
857         return code;
858 
859     /* free the sorted fragment list! */
860     y_list = tdev->PageData.y_ordered_list;
861     while (y_list) {
862         x_entry = y_list->x_ordered_list;
863         while (x_entry) {
864             gs_free(tdev->memory, x_entry->Unicode_Text, x_entry->Unicode_Text_Size, sizeof (usnigned short), "txtwrite free text fragment text buffer");
865             gs_free(tdev->memory, x_entry->Widths, x_entry->Unicode_Text_Size, sizeof (float), "txtwrite free widths array");
866             gs_free(tdev->memory, x_entry->FontName, 1, strlen(x_entry->FontName) + 1, "txtwrite free Font Name");
867             if (x_entry->next) {
868                 x_entry = x_entry->next;
869                 gs_free(tdev->memory, x_entry->previous, 1, sizeof(text_list_entry_t), "txtwrite free text fragment");
870             } else {
871                 gs_free(tdev->memory, x_entry, 1, sizeof(text_list_entry_t), "txtwrite free text fragment");
872                 x_entry = NULL;
873             }
874         }
875         if (y_list->next) {
876             y_list = y_list->next;
877             gs_free(tdev->memory, y_list->previous, 1, sizeof(page_text_list_t), "txtwrite free text list");
878         } else {
879             gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
880             y_list = NULL;
881         }
882     }
883     tdev->PageData.y_ordered_list = NULL;
884 
885     /* free the unsorted fragment list */
886     x_entry = tdev->PageData.unsorted_text_list;
887     while (x_entry) {
888         next_x = x_entry->next;
889         gs_free(tdev->memory, x_entry->Unicode_Text, x_entry->Unicode_Text_Size, sizeof (usnigned short), "txtwrite free unsorted text fragment text buffer");
890         gs_free(tdev->memory, x_entry->Widths, x_entry->Unicode_Text_Size, sizeof (float), "txtwrite free widths array");
891         gs_free(tdev->memory, x_entry->FontName, 1, strlen(x_entry->FontName) + 1, "txtwrite free Font Name");
892         gs_free(tdev->memory, x_entry, 1, sizeof(text_list_entry_t), "txtwrite free unsorted text fragment");
893         x_entry = next_x;
894     }
895     tdev->PageData.unsorted_text_list = NULL;
896 
897     code = gx_parse_output_file_name(&parsed, &fmt, tdev->fname,
898                                          strlen(tdev->fname), tdev->memory);
899 
900     if (code >= 0 && fmt) { /* file per page */
901         code = gx_device_close_output_file(dev, tdev->fname, tdev->file);
902         tdev->file = NULL;
903     }
904     return code;
905 }
906 
907 /* ---------------- Low-level drawing ---------------- */
908 
909 static int
txtwrite_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)910 txtwrite_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
911                     gx_color_index color)
912 {
913     return 0;
914 }
915 
916 /*static int
917 txtwrite_copy_alpha(gx_device * dev, const byte * data, int data_x,
918                 int raster, gx_bitmap_id id, int x, int y, int w, int h,
919                 gx_color_index color, int depth)
920 {
921     return 0;
922 }
923 
924 static int
925 txtwrite_copy_mono(gx_device * dev, const byte * data, int dx, int raster,
926                gx_bitmap_id id, int x, int y, int w, int h,
927                gx_color_index zero, gx_color_index one)
928 {
929     return 0;
930 }
931 static int
932 txtwrite_copy_color(gx_device * dev, const byte * data,
933                 int data_x, int raster, gx_bitmap_id id,
934                 int x, int y, int width, int height)
935 {
936     return 0;
937 }
938 
939 static int
940 txtwrite_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
941    int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
942                           int px, int py)
943 {
944     return 0;
945 }
946 
947 static int
948 txtwrite_strip_copy_rop(gx_device * dev,
949                     const byte * sdata, int sourcex, uint sraster,
950                     gx_bitmap_id id,
951                     const gx_color_index * scolors,
952                     const gx_strip_bitmap * textures,
953                     const gx_color_index * tcolors,
954                     int x, int y, int w, int h,
955                     int phase_x, int phase_y, gs_logical_operation_t lop)
956 {
957     return 0;
958 }*/
959 
960 /* ---------------- Parameters ---------------- */
961 
962 static int
txtwrite_get_params(gx_device * dev,gs_param_list * plist)963 txtwrite_get_params(gx_device * dev, gs_param_list * plist)
964 {
965     int code;
966     bool bool_T = true;
967     gs_param_string ofns;
968     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
969 
970     code = gx_default_get_params(dev, plist);
971     if (code < 0)
972         return code;
973 
974     ofns.data = (const byte *)tdev->fname,
975     ofns.size = strlen(tdev->fname),
976     ofns.persistent = false;
977     code = param_write_string(plist, "OutputFile", &ofns);
978     if (code < 0)
979         return code;
980 
981     code = param_write_bool(plist, "WantsToUnicode", &bool_T);
982     if (code < 0)
983         return code;
984 
985     code = param_write_bool(plist, "PreserveTrMode", &bool_T);
986     if (code < 0)
987         return code;
988 
989     code = param_write_bool(plist, "HighLevelDevice", &bool_T);
990     if (code < 0)
991         return code;
992 
993    code = gs_param_write_items(plist, tdev, NULL, txt_param_items);
994    return code;
995 }
996 
997 /* We implement put_params to ensure that we keep the important */
998 /* device parameters up to date, and to prevent an /undefined error */
999 static int
txtwrite_put_params(gx_device * dev,gs_param_list * plist)1000 txtwrite_put_params(gx_device * dev, gs_param_list * plist)
1001 {
1002     gx_device_txtwrite_t *tdev = (gx_device_txtwrite_t *) dev;
1003     int ecode = 0;
1004     int code;
1005     const char *param_name;
1006     gs_param_string ofs;
1007     bool dummy;
1008 
1009     switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofs)) {
1010         case 0:
1011             if (dev->LockSafetyParams &&
1012                     bytes_compare(ofs.data, ofs.size,
1013                         (const byte *)tdev->fname, strlen(tdev->fname))) {
1014                 ecode = gs_note_error(gs_error_invalidaccess);
1015                 goto ofe;
1016             }
1017             if (ofs.size >= gp_file_name_sizeof)
1018                 ecode = gs_error_limitcheck;
1019             else
1020                 break;
1021             goto ofe;
1022         default:
1023             ecode = code;
1024           ofe:param_signal_error(plist, param_name, ecode);
1025         case 1:
1026             ofs.data = 0;
1027             break;
1028     }
1029 
1030     if (ecode < 0)
1031         return ecode;
1032 
1033     code = param_read_int(plist, "TextFormat", &tdev->TextFormat);
1034     if (code < 0)
1035         return code;
1036 
1037     code = param_read_bool(plist, "WantsToUnicode", &dummy);
1038     if (code < 0)
1039         return code;
1040 
1041     code = param_read_bool(plist, "HighLevelDevice", &dummy);
1042     if (code < 0)
1043         return code;
1044 
1045     code = param_read_bool(plist, "PreserveTrMode", &dummy);
1046     if (code < 0)
1047         return code;
1048 
1049     code = gx_default_put_params(dev, plist);
1050     if (code < 0)
1051         return code;
1052 
1053     if (ofs.data != 0) {	/* Close the file if it's open. */
1054         if (tdev->file != 0) {
1055             fclose(tdev->file);
1056             tdev->file = 0;
1057         }
1058         memcpy(tdev->fname, ofs.data, ofs.size);
1059         tdev->fname[ofs.size] = 0;
1060     }
1061     return 0;
1062 }
1063 
1064 /* ---------------- Polygon drawing ---------------- */
1065 
1066 /*static int
1067 txtwrite_fill_trapezoid(gx_device * dev,
1068                     const gs_fixed_edge * left, const gs_fixed_edge * right,
1069                     fixed ybot, fixed ytop, bool swap_axes,
1070                     const gx_device_color * pdevc, gs_logical_operation_t lop)
1071 {
1072     return 0;
1073 }
1074 
1075 static int
1076 txtwrite_fill_parallelogram(gx_device * dev,
1077                         fixed px, fixed py, fixed ax, fixed ay,
1078                         fixed bx, fixed by, const gx_device_color * pdevc,
1079                         gs_logical_operation_t lop)
1080 {
1081     return 0;
1082 }
1083 
1084 static int
1085 txtwrite_fill_triangle(gx_device * dev,
1086                    fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
1087                    const gx_device_color * pdevc, gs_logical_operation_t lop)
1088 {
1089     return 0;
1090 }
1091 
1092 static int
1093 txtwrite_draw_thin_line(gx_device * dev,
1094                     fixed fx0, fixed fy0, fixed fx1, fixed fy1,
1095                     const gx_device_color * pdevc, gs_logical_operation_t lop,
1096                     fixed adjustx, fixed adjusty)
1097 {
1098     return 0;
1099 }*/
1100 
1101 /* ---------------- High-level drawing ---------------- */
1102 
1103 static int
txtwrite_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_device_color * pdevc,const gx_clip_path * pcpath)1104 txtwrite_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
1105                const gx_fill_params * params, const gx_device_color * pdevc,
1106                const gx_clip_path * pcpath)
1107 {
1108         return 0;
1109 }
1110 
1111 static int
txtwrite_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdevc,const gx_clip_path * pcpath)1112 txtwrite_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
1113                  const gx_stroke_params * params,
1114                  const gx_drawing_color * pdevc, const gx_clip_path * pcpath)
1115 {
1116     return 0;
1117 }
1118 
1119 #if 0
1120 static int
1121 txtwrite_fill_mask(gx_device * dev,
1122                const byte * data, int dx, int raster, gx_bitmap_id id,
1123                int x, int y, int w, int h,
1124                const gx_drawing_color * pdcolor, int depth,
1125                gs_logical_operation_t lop, const gx_clip_path * pcpath)
1126 {
1127     return 0;
1128 }
1129 
1130 int
1131 txtwrite_begin_typed_image(gx_device * dev,
1132                        const gs_imager_state * pis, const gs_matrix * pmat,
1133                    const gs_image_common_t * pic, const gs_int_rect * prect,
1134                        const gx_drawing_color * pdcolor,
1135                        const gx_clip_path * pcpath,
1136                        gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
1137 {
1138     return 0;
1139 }
1140 #endif
1141 
1142 /* ------ Text imaging ------ */
1143 
1144 static int
txtwrite_font_orig_matrix(const gs_font * font,gs_glyph cid,gs_matrix * pmat)1145 txtwrite_font_orig_matrix(const gs_font *font, gs_glyph cid, gs_matrix *pmat)
1146 {
1147     int code;
1148 
1149     switch (font->FontType) {
1150     case ft_composite:                /* subfonts have their own FontMatrix */
1151     case ft_TrueType:
1152     case ft_CID_TrueType:
1153         /* The TrueType FontMatrix is 1 unit per em, which is what we want. */
1154         gs_make_identity(pmat);
1155         return 0;
1156     case ft_encrypted:
1157     case ft_encrypted2:
1158     case ft_CID_encrypted:
1159     case ft_user_defined:
1160     case ft_PCL_user_defined:
1161     case ft_GL2_stick_user_defined:
1162         /*
1163          * Type 1 fonts are supposed to use a standard FontMatrix of
1164          * [0.001 0 0 0.001 0 0], with a 1000-unit cell.  However,
1165          * Windows NT 4.0 creates Type 1 fonts, apparently derived from
1166          * TrueType fonts, that use a 2048-unit cell and corresponding
1167          * FontMatrix.  Also, some PS programs perform font scaling by
1168          * replacing FontMatrix like this :
1169          *
1170          *   /f12 /Times-Roman findfont
1171          *   copyfont          % (remove FID)
1172          *   dup /FontMatrix [0.012 0 0 0.012 0 0] put
1173          *   definefont
1174          *   /f12 1 selectfont
1175          *
1176          * Such fonts are their own "base font", but the orig_matrix
1177          * must still be set to 0.001, not 0.012 .
1178          *
1179          * The old code used a heuristic to detect and correct for this here.
1180          * Unfortunately it doesn't work properly when it meets a font
1181          * with FontMatrix like this :
1182          *
1183          *   /FontMatrix [1 2288 div 0 0 1 2288 div 0 0 ] def
1184          *
1185          * (the bug 686970). Also comparefiles\455690.pdf appears to
1186          * have similar problem. Therefore we added a support to lib/gs_fonts.ps,
1187          * src/zbfont.c, src/gsfont.c that provides an acces to the original
1188          * font via a special key OrigFont added to the font dictionary while definefont.
1189          * Now we work through this access with PS interpreter,
1190          * but keep the old heuristic for other clients.
1191          */
1192         {
1193             const gs_font *base_font = font;
1194 
1195             while (base_font->base != base_font)
1196                 base_font = base_font->base;
1197             if (font->FontType == ft_user_defined ||
1198                 font->FontType == ft_PCL_user_defined ||
1199                 font->FontType == ft_GL2_stick_user_defined)
1200                 *pmat = base_font->FontMatrix;
1201             else if (base_font->orig_FontMatrix.xx != 0 || base_font->orig_FontMatrix.xy != 0 ||
1202                 base_font->orig_FontMatrix.yx != 0 || base_font->orig_FontMatrix.yy != 0)
1203                 *pmat = base_font->orig_FontMatrix;
1204             else {
1205                 /*  Must not happen with PS interpreter.
1206                     Provide a hewuristic for other clients.
1207                 */
1208                 if (base_font->FontMatrix.xx == 1.0/2048 &&
1209                     base_font->FontMatrix.xy == 0 &&
1210                     base_font->FontMatrix.yx == 0 &&
1211                     any_abs(base_font->FontMatrix.yy) == 1.0/2048
1212                     )
1213                     *pmat = base_font->FontMatrix;
1214                 else
1215                     gs_make_scaling(0.001, 0.001, pmat);
1216             }
1217         }
1218         if (font->FontType == ft_CID_encrypted && cid != -1) {
1219             int fidx;
1220 
1221             if (cid < GS_MIN_CID_GLYPH)
1222                 cid = GS_MIN_CID_GLYPH;
1223             code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
1224                                 cid, NULL, &fidx);
1225             if (code < 0) {
1226                 code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
1227                                 (gs_glyph)GS_MIN_CID_GLYPH, NULL, &fidx);
1228             }
1229             if (code >= 0) {
1230                 gs_matrix_multiply(&(gs_cid0_indexed_font(font, fidx)->FontMatrix),
1231                                 pmat, pmat);
1232             }
1233         }
1234         return 0;
1235     default:
1236         return_error(gs_error_rangecheck);
1237     }
1238 }
1239 
1240 /*
1241  * Special version of txtwrite_font_orig_matrix(), that considers FDArray font's FontMatrix too.
1242  * Called only by txt_glyph_width().
1243  * 'cid' is only consulted if 'font' is a CIDFontType 0 CID font.
1244  */
1245 static int
glyph_orig_matrix(const gs_font * font,gs_glyph cid,gs_matrix * pmat)1246 glyph_orig_matrix(const gs_font *font, gs_glyph cid, gs_matrix *pmat)
1247 {
1248     int code = txtwrite_font_orig_matrix(font, cid, pmat);
1249     if (code >= 0) {
1250         if (font->FontType == ft_CID_encrypted) {
1251             int fidx;
1252 
1253             if (cid < GS_MIN_CID_GLYPH)
1254                 cid = GS_MIN_CID_GLYPH;
1255             code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
1256                                 cid, NULL, &fidx);
1257             if (code < 0) {
1258                 code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
1259                                 (gs_glyph)GS_MIN_CID_GLYPH, NULL, &fidx);
1260             }
1261             if (code >= 0) {
1262                 gs_matrix_multiply(&(gs_cid0_indexed_font(font, fidx)->FontMatrix),
1263                                 pmat, pmat);
1264             }
1265         }
1266     }
1267     return code;
1268 }
1269 
1270 /*
1271  * Compute the cached values in the text processing state from the text
1272  * parameters, current_font, and pis->ctm.  Return either an error code (<
1273  * 0) or a mask of operation attributes that the caller must emulate.
1274  * Currently the only such attributes are TEXT_ADD_TO_ALL_WIDTHS and
1275  * TEXT_ADD_TO_SPACE_WIDTH.  Note that this procedure fills in all the
1276  * values in ppts->values, not just the ones that need to be set now.
1277  */
1278 static int
transform_delta_inverse(const gs_point * pdelta,const gs_matrix * pmat,gs_point * ppt)1279 transform_delta_inverse(const gs_point *pdelta, const gs_matrix *pmat,
1280                         gs_point *ppt)
1281 {
1282     int code = gs_distance_transform_inverse(pdelta->x, pdelta->y, pmat, ppt);
1283     gs_point delta;
1284 
1285     if (code < 0)
1286         return code;
1287     if (ppt->y == 0)
1288         return 0;
1289     /* Check for numerical fuzz. */
1290     code = gs_distance_transform(ppt->x, 0.0, pmat, &delta);
1291     if (code < 0)
1292         return 0;                /* punt */
1293     if (fabs(delta.x - pdelta->x) < 0.01 && fabs(delta.y - pdelta->y) < 0.01) {
1294         /* Close enough to y == 0: device space error < 0.01 pixel. */
1295         ppt->y = 0;
1296     }
1297     return 0;
1298 }
1299 
1300 static
txt_calculate_text_size(gs_imager_state * pis,gs_font * ofont,const gs_matrix * pfmat,gs_matrix * smat,gs_matrix * tmat,gs_font * font,gx_device * pdev)1301 float txt_calculate_text_size(gs_imager_state *pis, gs_font *ofont,
1302                               const gs_matrix *pfmat, gs_matrix *smat, gs_matrix *tmat,
1303                               gs_font *font, gx_device *pdev)
1304 {
1305     gs_matrix orig_matrix;
1306     double
1307         sx = pdev->HWResolution[0] / 72.0,
1308         sy = pdev->HWResolution[1] / 72.0;
1309     float size;
1310 
1311     /* Get the original matrix of the base font. */
1312 
1313     txtwrite_font_orig_matrix(ofont, -1, &orig_matrix);
1314     /* Compute the scaling matrix and combined matrix. */
1315 
1316     gs_matrix_invert(&orig_matrix, smat);
1317     gs_matrix_multiply(smat, pfmat, smat);
1318     *tmat = ctm_only(pis);
1319     tmat->tx = tmat->ty = 0;
1320     gs_matrix_multiply(smat, tmat, tmat);
1321 
1322     /* Try to find a reasonable size value. */
1323 
1324     size = hypot(tmat->yx, tmat->yy) / sy;
1325     if (size < 0.01)
1326         size = hypot(tmat->xx, tmat->xy) / sx;
1327     if (size < 0.01)
1328         size = 1;
1329 
1330     return(size);
1331 }
1332 
1333 static int
txt_update_text_state(text_list_entry_t * ppts,const textw_text_enum_t * penum,gs_font * ofont,const gs_matrix * pfmat)1334 txt_update_text_state(text_list_entry_t *ppts,
1335                       const textw_text_enum_t *penum,
1336                       gs_font *ofont, const gs_matrix *pfmat)
1337 {
1338     gx_device *const pdev = penum->dev;
1339     gs_font *font = penum->current_font;
1340     gs_fixed_point cpt;
1341     gs_matrix smat, tmat;
1342     float size;
1343     float c_s = 0, w_s = 0;
1344     int mask = 0;
1345     int code = gx_path_current_point(penum->path, &cpt);
1346 
1347     if (code < 0)
1348         return code;
1349 
1350     size = txt_calculate_text_size(penum->pis, ofont, pfmat, &smat, &tmat, penum->current_font, pdev);
1351     /* Check for spacing parameters we can handle, and transform them. */
1352 
1353     if (penum->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
1354         if (penum->current_font->WMode == 0) {
1355             gs_point pt;
1356 
1357             code = transform_delta_inverse(&penum->text.delta_all, &smat, &pt);
1358             if (code >= 0 && pt.y == 0)
1359                 c_s = pt.x * size;
1360             else
1361                 mask |= TEXT_ADD_TO_ALL_WIDTHS;
1362         }
1363         else
1364             mask |= TEXT_ADD_TO_ALL_WIDTHS;
1365     }
1366 
1367     if (penum->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
1368         gs_point pt;
1369 
1370         code = transform_delta_inverse(&penum->text.delta_space, &smat, &pt);
1371         if (code >= 0 && pt.y == 0 && penum->text.space.s_char == 32)
1372             w_s = pt.x * size;
1373         else
1374             mask |= TEXT_ADD_TO_SPACE_WIDTH;
1375     }
1376     /* Store the updated values. */
1377 
1378     tmat.xx /= size;
1379     tmat.xy /= size;
1380     tmat.yx /= size;
1381     tmat.yy /= size;
1382     tmat.tx += fixed2float(cpt.x);
1383     tmat.ty += fixed2float(cpt.y);
1384 
1385     ppts->size = size;
1386     ppts->matrix = tmat;
1387     ppts->render_mode = penum->pis->text_rendering_mode;
1388     ppts->FontName = (char *)gs_malloc(pdev->memory->stable_memory, 1,
1389         font->font_name.size + 1, "txtwrite alloc font name");
1390     if (!ppts->FontName)
1391         return gs_note_error(gs_error_VMerror);
1392     memcpy(ppts->FontName, font->font_name.chars, font->font_name.size);
1393     ppts->FontName[font->font_name.size] = 0x00;
1394     ppts->render_mode = font->WMode;
1395 
1396     if (font->PaintType == 2 && penum->pis->text_rendering_mode == 0)
1397     {
1398         gs_imager_state *pis = penum->pis;
1399         gs_font *font = penum->current_font;
1400         double scaled_width = font->StrokeWidth != 0 ? font->StrokeWidth : 0.001;
1401         double saved_width = pis->line_params.half_width;
1402         /*
1403          * See stream_to_text in gdevpdfu.c re the computation of
1404          * the scaling value.
1405          */
1406         double scale = 72.0 / pdev->HWResolution[1];
1407 
1408         if (font->FontMatrix.yy != 0)
1409             scaled_width *= fabs(font->orig_FontMatrix.yy) * size * tmat.yy * scale;
1410         else
1411             scaled_width *= fabs(font->orig_FontMatrix.xy) * size * tmat.xy * scale;
1412 
1413         ppts->render_mode = 1;
1414         ppts->PaintType0Width = scaled_width;
1415 
1416         pis->line_params.half_width = scaled_width / 2;
1417         if (code < 0)
1418             return code;
1419 
1420         pis->line_params.half_width = saved_width;
1421     }
1422     return (code < 0 ? code : mask);
1423 }
1424 
1425 static int
store_glyph_width(txt_glyph_width_t * pwidth,int wmode,const gs_matrix * scale,const gs_glyph_info_t * pinfo)1426 store_glyph_width(txt_glyph_width_t *pwidth, int wmode, const gs_matrix *scale,
1427                   const gs_glyph_info_t *pinfo)
1428 {
1429     double w, v;
1430 
1431     gs_distance_transform(pinfo->width[wmode].x, pinfo->width[wmode].y, scale, &pwidth->xy);
1432     if (wmode)
1433         w = pwidth->xy.y, v = pwidth->xy.x;
1434     else
1435         w = pwidth->xy.x, v = pwidth->xy.y;
1436     if (v != 0)
1437         return 1;
1438     pwidth->w = w;
1439     gs_distance_transform(pinfo->v.x, pinfo->v.y, scale, &pwidth->v);
1440     return 0;
1441 }
1442 
1443 static int
get_missing_width(gs_font * font,int wmode,const gs_matrix * scale_c,txt_glyph_widths_t * pwidths)1444 get_missing_width(gs_font *font, int wmode, const gs_matrix *scale_c,
1445                     txt_glyph_widths_t *pwidths)
1446 {
1447     gs_font_info_t finfo;
1448     int code;
1449 
1450     code = font->procs.font_info((gs_font *)font, NULL,
1451                                   FONT_INFO_MISSING_WIDTH, &finfo);
1452     if (code < 0)
1453         return code;
1454     if (wmode) {
1455         gs_distance_transform(0.0, -finfo.MissingWidth, scale_c, &pwidths->real_width.xy);
1456         pwidths->Width.xy.x = 0;
1457         pwidths->Width.xy.y = pwidths->real_width.xy.y;
1458         pwidths->Width.w = pwidths->real_width.w =
1459                 pwidths->Width.xy.y;
1460         pwidths->Width.v.x = - pwidths->Width.xy.y / 2;
1461         pwidths->Width.v.y = - pwidths->Width.xy.y;
1462     } else {
1463         gs_distance_transform(finfo.MissingWidth, 0.0, scale_c, &pwidths->real_width.xy);
1464         pwidths->Width.xy.x = pwidths->real_width.xy.x;
1465         pwidths->Width.xy.y = 0;
1466         pwidths->Width.w = pwidths->real_width.w =
1467                 pwidths->Width.xy.x;
1468         pwidths->Width.v.x = pwidths->Width.v.y = 0;
1469     }
1470     /*
1471      * Don't mark the width as known, just in case this is an
1472      * incrementally defined font.
1473      */
1474     return 1;
1475 }
1476 
1477 /*
1478  * Get the widths (unmodified from the copied font,
1479  * and possibly modified from the original font) of a given glyph.
1480  * Return 1 if the width was defaulted to MissingWidth.
1481  * Return TEXT_PROCESS_CDEVPROC if a CDevProc callout is needed.
1482  * cdevproc_result != NULL if we restart after a CDevProc callout.
1483  */
1484 static int
txt_glyph_widths(gs_font * font,int wmode,gs_glyph glyph,gs_font * orig_font,txt_glyph_widths_t * pwidths,const double cdevproc_result[10])1485 txt_glyph_widths(gs_font *font, int wmode, gs_glyph glyph,
1486                  gs_font *orig_font, txt_glyph_widths_t *pwidths,
1487                  const double cdevproc_result[10])
1488 {
1489     gs_font *ofont = orig_font;
1490     gs_glyph_info_t info;
1491     gs_matrix scale_c, scale_o;
1492     int code, rcode = 0;
1493     gs_point v;
1494     int allow_cdevproc_callout = (orig_font->FontType == ft_CID_TrueType
1495                 || orig_font->FontType == ft_CID_encrypted
1496                 ? GLYPH_INFO_CDEVPROC : 0); /* fixme : allow more font types. */
1497 
1498     if (ofont->FontType == ft_composite)
1499         return_error(gs_error_unregistered); /* Must not happen. */
1500     code = glyph_orig_matrix((const gs_font *)font, glyph, &scale_c);
1501     if (code < 0)
1502         return code;
1503     code = glyph_orig_matrix(ofont, glyph, &scale_o);
1504     if (code < 0)
1505         return code;
1506     gs_matrix_scale(&scale_c, 1000.0, 1000.0, &scale_c);
1507     gs_matrix_scale(&scale_o, 1000.0, 1000.0, &scale_o);
1508     pwidths->Width.v.x = pwidths->Width.v.y = 0;
1509     pwidths->real_width.v.x = pwidths->real_width.v.y = 0;
1510     pwidths->replaced_v = false;
1511     if (glyph == GS_NO_GLYPH)
1512         return get_missing_width(font, wmode, &scale_c, pwidths);
1513     code = font->procs.glyph_info((gs_font *)font, glyph, NULL,
1514                                     GLYPH_INFO_WIDTH0 |
1515                                     (GLYPH_INFO_WIDTH0 << wmode) |
1516                                     GLYPH_INFO_OUTLINE_WIDTHS |
1517                                     (GLYPH_INFO_VVECTOR0 << wmode),
1518                                     &info);
1519     /* For CID fonts the PDF spec requires the x-component of v-vector
1520        to be equal to half glyph width, and AR5 takes it from W, DW.
1521        So make a compatibe data here.
1522      */
1523     if (font->FontType != ft_PCL_user_defined && font->FontType != ft_GL2_stick_user_defined
1524         && (code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))) {
1525         code = get_missing_width(font, wmode, &scale_c, pwidths);
1526         if (code < 0)
1527             v.y = 0;
1528         else
1529             v.y = pwidths->Width.v.y;
1530         if (wmode && (ofont->FontType == ft_CID_encrypted ||
1531             ofont->FontType == ft_CID_TrueType)) {
1532             txt_glyph_widths_t widths1;
1533 
1534             if (get_missing_width(font, 0, &scale_c, &widths1) < 0)
1535                 v.x = 0;
1536             else
1537                 v.x = widths1.Width.w / 2;
1538         } else
1539             v.x = pwidths->Width.v.x;
1540     } else if (code < 0)
1541         return code;
1542     else {
1543         code = store_glyph_width(&pwidths->Width, wmode, &scale_c, &info);
1544         if (code < 0)
1545             return code;
1546         rcode |= code;
1547         if (info.members  & (GLYPH_INFO_VVECTOR0 << wmode))
1548             gs_distance_transform(info.v.x, info.v.y, &scale_c, &v);
1549         else
1550             v.x = v.y = 0;
1551         if (wmode && (ofont->FontType == ft_CID_encrypted ||
1552             ofont->FontType == ft_CID_TrueType)) {
1553             if (info.members & (GLYPH_INFO_WIDTH0 << wmode)) {
1554                 gs_point xy;
1555 
1556                 gs_distance_transform(info.width[0].x, info.width[0].y, &scale_c, &xy);
1557                 v.x = xy.x / 2;
1558             } else {
1559                 txt_glyph_widths_t widths1;
1560 
1561                 if (get_missing_width(font, 0, &scale_c, &widths1) < 0)
1562                     v.x = 0;
1563                 else
1564                     v.x = widths1.Width.w / 2;
1565             }
1566         }
1567     }
1568     pwidths->Width.v = v;
1569     /* Skip only if not paralel to the axis. */
1570     if (code > 0 && ofont->FontType != ft_CID_encrypted &&
1571             ofont->FontType != ft_CID_TrueType)
1572         pwidths->Width.xy.x = pwidths->Width.xy.y = pwidths->Width.w = 0;
1573     if (cdevproc_result == NULL) {
1574         info.members = 0;
1575         code = ofont->procs.glyph_info(ofont, glyph, NULL,
1576                                             (GLYPH_INFO_WIDTH0 << wmode) |
1577                                             (GLYPH_INFO_VVECTOR0 << wmode) |
1578                                             allow_cdevproc_callout,
1579                                             &info);
1580         /* fixme : Move this call before cfont->procs.glyph_info. */
1581         if (info.members & GLYPH_INFO_CDEVPROC) {
1582             if (allow_cdevproc_callout)
1583                 return TEXT_PROCESS_CDEVPROC;
1584         else
1585             return_error(gs_error_rangecheck);
1586         }
1587     } else {
1588         info.width[0].x = cdevproc_result[0];
1589         info.width[0].y = cdevproc_result[1];
1590         info.width[1].x = cdevproc_result[6];
1591         info.width[1].y = cdevproc_result[7];
1592         info.v.x = (wmode ? cdevproc_result[8] : 0);
1593         info.v.y = (wmode ? cdevproc_result[9] : 0);
1594         info.members = (GLYPH_INFO_WIDTH0 << wmode) |
1595                        (wmode ? GLYPH_INFO_VVECTOR1 : 0);
1596         code = 0;
1597     }
1598     if (code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))
1599         pwidths->real_width = pwidths->Width;
1600     else if (code < 0)
1601         return code;
1602     else {
1603         if ((info.members & (GLYPH_INFO_VVECTOR0 | GLYPH_INFO_VVECTOR1)) != 0)
1604             pwidths->replaced_v = true;
1605         else
1606             info.v.x = info.v.y = 0;
1607         code = store_glyph_width(&pwidths->real_width, wmode, &scale_o, &info);
1608         if (code < 0)
1609             return code;
1610         rcode |= code;
1611         gs_distance_transform(info.v.x, info.v.y, &scale_o, &pwidths->real_width.v);
1612     }
1613     return rcode;
1614 }
1615 
1616 static void
txt_char_widths_to_uts(gs_font * font,txt_glyph_widths_t * pwidths)1617 txt_char_widths_to_uts(gs_font *font /* may be NULL for non-Type3 */,
1618                        txt_glyph_widths_t *pwidths)
1619 {
1620     if (font && (font->FontType == ft_user_defined ||
1621         font->FontType == ft_PCL_user_defined ||
1622         font->FontType == ft_GL2_stick_user_defined)) {
1623         gs_matrix *pmat = &font->orig_FontMatrix;
1624 
1625         pwidths->Width.xy.x *= pmat->xx; /* formula simplified based on wy in glyph space == 0 */
1626         pwidths->Width.xy.y  = 0.0; /* WMode == 0 for PDF Type 3 fonts */
1627         gs_distance_transform(pwidths->real_width.xy.x, pwidths->real_width.xy.y, pmat, &pwidths->real_width.xy);
1628     } else {
1629         /*
1630          * For other font types:
1631          * - PDF design->text space is a simple scaling by 0.001.
1632          * - The Width.xy.x/y that should be zeroed-out per 5.3.3 "Text Space Details" is already 0.
1633          */
1634         pwidths->Width.xy.x /= 1000.0;
1635         pwidths->Width.xy.y /= 1000.0;
1636         pwidths->real_width.xy.x /= 1000.0;
1637         pwidths->real_width.xy.y /= 1000.0;
1638     }
1639 }
1640 
1641 /* Simple routine to update the current point by the accumulated width of the
1642  * text.
1643  */
1644 static int
txt_shift_text_currentpoint(textw_text_enum_t * penum,gs_point * wpt)1645 txt_shift_text_currentpoint(textw_text_enum_t *penum, gs_point *wpt)
1646 {
1647     gs_state *pgs;
1648     extern_st(st_gs_state);
1649 
1650     if (gs_object_type(penum->dev->memory, penum->pis) != &st_gs_state) {
1651         /* Probably never happens. Not sure though. */
1652         return_error(gs_error_unregistered);
1653     }
1654     pgs = (gs_state *)penum->pis;
1655     return gs_moveto_aux(penum->pis, gx_current_path(pgs),
1656                               fixed2float(penum->origin.x) + wpt->x,
1657                               fixed2float(penum->origin.y) + wpt->y);
1658 }
1659 
1660 /* Try to convert glyph names/character codes to Unicode. We first try to see
1661  * if we have any Unicode information either from a ToUnicode CMap or GlyphNames2Unicode
1662  * table. If that fails we look at the glyph name to see if it starts 'uni'
1663  * in which case we assume hte remainder of the name is the Unicode value. If
1664  * that fails we currently just return the character code. We could go further
1665  * and see if the glyph name is one we recognise (eg /A etc) and convert that.
1666  * For CIDFonts we might be able to look at the Regiostry and Ordering and
1667  * perhaps convert the CID into Unicode frmo that information. These are
1668  * future enhancements for now.
1669  */
get_unicode(gs_font * font,gs_glyph glyph,gs_char ch,unsigned short * Buffer)1670 static int get_unicode(gs_font *font, gs_glyph glyph, gs_char ch, unsigned short *Buffer)
1671 {
1672     unsigned short unicode;
1673     int code, cid;
1674     gs_const_string gnstr;
1675 
1676     unicode = font->procs.decode_glyph((gs_font *)font, glyph, ch);
1677     cid = glyph - GS_MIN_CID_GLYPH;
1678     if (unicode == GS_NO_CHAR) {
1679         code = font->procs.glyph_name(font, glyph, &gnstr);
1680         if (code >= 0 && gnstr.size == 7) {
1681             if (!memcmp(gnstr.data, "uni", 3)) {
1682                 static const char *hexdigits = "0123456789ABCDEF";
1683                 char *d0 = strchr(hexdigits, gnstr.data[3]);
1684                 char *d1 = strchr(hexdigits, gnstr.data[4]);
1685                 char *d2 = strchr(hexdigits, gnstr.data[5]);
1686                 char *d3 = strchr(hexdigits, gnstr.data[6]);
1687 
1688                 if (d0 != NULL && d1 != NULL && d2 != NULL && d3 != NULL)
1689                     unicode = ((d0 - hexdigits) << 12) + ((d1 - hexdigits) << 8) +
1690                       ((d2 - hexdigits) << 4 ) +  (d3 - hexdigits);
1691             }
1692         }
1693         if (unicode == GS_NO_CHAR) {
1694             int index = 0;
1695 
1696             /* Search glyph to single Unicode value table */
1697             while (index >= 0 && SingleGlyphList[index].Glyph != 0) {
1698                 if (SingleGlyphList[index].Glyph[0] < gnstr.data[0])
1699                     index++;
1700                 if (SingleGlyphList[index].Glyph[0] > gnstr.data[0]){
1701                     index = -1;
1702                     break;
1703                 }
1704                 if (strlen(SingleGlyphList[index].Glyph) == gnstr.size) {
1705                     if(memcmp(gnstr.data, SingleGlyphList[index].Glyph, gnstr.size))
1706                         break;
1707                 }
1708             }
1709             if (index != -1) {
1710                 *Buffer = SingleGlyphList[index].Unicode;
1711                 return 1;
1712             }
1713             /* Search glyph to double Unicode value table */
1714             while (index >= 0 && DoubleGlyphList[index].Glyph != 0) {
1715                 if (DoubleGlyphList[index].Glyph[0] < gnstr.data[0])
1716                     index++;
1717                 if (DoubleGlyphList[index].Glyph[0] > gnstr.data[0]){
1718                     index = -1;
1719                     break;
1720                 }
1721                 if (strlen(DoubleGlyphList[index].Glyph) == gnstr.size) {
1722                     if(memcmp(gnstr.data, DoubleGlyphList[index].Glyph, gnstr.size))
1723                         break;
1724                 }
1725             }
1726             if (index != -1) {
1727                 memcpy(Buffer, DoubleGlyphList[index].Unicode, 2);
1728                 return 2;
1729             }
1730 
1731             /* Search glyph to triple Unicode value table */
1732             while (index >= 0 && TrebleGlyphList[index].Glyph != 0) {
1733                 if (TrebleGlyphList[index].Glyph[0] < gnstr.data[0])
1734                     index++;
1735                 if (TrebleGlyphList[index].Glyph[0] > gnstr.data[0]){
1736                     index = -1;
1737                     break;
1738                 }
1739                 if (strlen(TrebleGlyphList[index].Glyph) == gnstr.size) {
1740                     if(memcmp(gnstr.data, TrebleGlyphList[index].Glyph, gnstr.size))
1741                         break;
1742                 }
1743             }
1744             if (index != -1) {
1745                 memcpy(Buffer, TrebleGlyphList[index].Unicode, 3);
1746                 return 3;
1747             }
1748 
1749             /* Search glyph to quadruple Unicode value table */
1750             while (index >= 0 && QuadGlyphList[index].Glyph != 0) {
1751                 if (QuadGlyphList[index].Glyph[0] < gnstr.data[0])
1752                     index++;
1753                 if (QuadGlyphList[index].Glyph[0] > gnstr.data[0]){
1754                     index = -1;
1755                     break;
1756                 }
1757                 if (strlen(QuadGlyphList[index].Glyph) == gnstr.size) {
1758                     if(memcmp(gnstr.data, QuadGlyphList[index].Glyph, gnstr.size))
1759                         break;
1760                 }
1761             }
1762             if (index != -1) {
1763                 memcpy(Buffer, QuadGlyphList[index].Unicode, 4);
1764                 return 4;
1765             }
1766         }
1767     }
1768     *Buffer = unicode;
1769     return 1;
1770 }
1771 
1772 /* Routines to enumerate each glyph/character code in turn, find its width
1773  * so that we can update the current point and find the end of the text, convert
1774  * to Unicode if at all possible, and store some state such as the font, colour
1775  * text rendering mode, writing mode, etc.
1776  */
1777 
1778 static int
txtwrite_process_cmap_text(gs_text_enum_t * pte)1779 txtwrite_process_cmap_text(gs_text_enum_t *pte)
1780 {
1781     textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
1782     unsigned int rcode = 0;
1783     gs_text_enum_t scan = *(gs_text_enum_t *)pte;
1784 
1785     /* Composite font using a CMap */
1786     for ( ; ; ) {
1787         gs_glyph glyph;
1788         int font_code, font_index, code;
1789         gs_font *subfont;
1790         gs_char chr;
1791         txt_glyph_widths_t widths;
1792         gs_matrix m3;
1793         gs_point wanted;	/* user space */
1794         gs_point dpt = {0,0};
1795 
1796         font_code = scan.orig_font->procs.next_char_glyph
1797                 (&scan, &chr, &glyph);
1798 
1799         subfont = scan.fstack.items[scan.fstack.depth].font;
1800         font_index = scan.fstack.items[scan.fstack.depth - 1].index;
1801 
1802         switch (font_code) {
1803             case 0:		/* no font change */
1804             case 1:		/* font change */
1805                 code = txt_glyph_widths(subfont, scan.orig_font->WMode, glyph, (gs_font *)subfont, &widths,
1806                         penum->cdevproc_callout ? penum->cdevproc_result : NULL);
1807                 if (code == TEXT_PROCESS_CDEVPROC) {
1808                     penum->cdevproc_callout = true;
1809                     scan.returned.current_glyph = glyph;
1810                     pte->current_font = subfont;
1811                     scan.current_font = subfont;
1812                     rcode = TEXT_PROCESS_CDEVPROC;
1813                     break;
1814                 }
1815                 else {
1816                     penum->cdevproc_callout = false;
1817                     pte->index = scan.index;
1818                 }
1819                 code = gs_matrix_multiply(&subfont->FontMatrix, &pte->orig_font->FontMatrix, &m3);
1820                 code = txt_update_text_state(penum->text_state, (textw_text_enum_t *)pte, pte->orig_font, &m3);
1821                 txt_char_widths_to_uts(pte->orig_font, &widths); /* convert design->text space */
1822                 gs_distance_transform(widths.real_width.xy.x * penum->text_state->size,
1823                           widths.real_width.xy.y * penum->text_state->size,
1824                           &penum->text_state->matrix, &wanted);
1825                 pte->returned.total_width.x += wanted.x;
1826                 pte->returned.total_width.y += wanted.y;
1827                 penum->Widths[pte->index - 1] = wanted.x;
1828 
1829                 if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
1830                     gs_point tpt;
1831 
1832                     gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
1833                               &ctm_only(pte->pis), &tpt);
1834                     dpt.x += tpt.x;
1835                     dpt.y += tpt.y;
1836                 }
1837                 if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH && chr == pte->text.space.s_char) {
1838                     gs_point tpt;
1839 
1840                     gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
1841                               &ctm_only(pte->pis), &tpt);
1842                     dpt.x += tpt.x;
1843                     dpt.y += tpt.y;
1844                 }
1845                 pte->returned.total_width.x += dpt.x;
1846                 pte->returned.total_width.y += dpt.y;
1847 
1848                 penum->TextBufferIndex += get_unicode((gs_font *)pte->orig_font, glyph, chr, &penum->TextBuffer[penum->TextBufferIndex]);
1849                 penum->Widths[pte->index - 1] += dpt.x;
1850                 break;
1851             case 2:		/* end of string */
1852                 return 0;
1853             default:	        /* error */
1854                 return font_code;
1855         }
1856         if (rcode || pte->index >= pte->text.size)
1857             break;
1858     }
1859     return rcode;
1860 }
1861 
1862 static int
txtwrite_process_plain_text(gs_text_enum_t * pte)1863 txtwrite_process_plain_text(gs_text_enum_t *pte)
1864 {
1865     /* one byte regular font */
1866     textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
1867     gs_font *font = pte->orig_font;
1868     const gs_glyph *gdata = NULL;
1869     gs_glyph glyph;
1870     gs_char ch;
1871     int i, code;
1872     uint operation = pte->text.operation;
1873     txt_glyph_widths_t widths;
1874     gs_point wanted;	/* user space */
1875     gs_point dpt = {0,0};
1876 
1877     for (i=0;i<pte->text.size;i++) {
1878         if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) {
1879             ch = pte->text.data.bytes[pte->index++];
1880         } else if (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)) {
1881             ch = pte->text.data.bytes[pte->index++];
1882         } else if (operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH)) {
1883             if (operation & TEXT_FROM_GLYPHS) {
1884                 gdata = pte->text.data.glyphs + (pte->index++ * sizeof (gs_glyph));
1885             } else {
1886                 gdata = &pte->text.data.d_glyph;
1887             }
1888         }
1889         glyph = (gdata == NULL ? pte->orig_font->procs.encode_char(pte->orig_font, ch, GLYPH_SPACE_NAME)
1890                            : *gdata);
1891 
1892         code = txt_glyph_widths(font, font->WMode, glyph, (gs_font *)font, &widths, NULL);
1893 
1894         penum->cdevproc_callout = false;
1895         code = txt_update_text_state(penum->text_state, (textw_text_enum_t *)pte, pte->orig_font, &font->FontMatrix);
1896         txt_char_widths_to_uts(pte->orig_font, &widths); /* convert design->text space */
1897         gs_distance_transform(widths.real_width.xy.x * penum->text_state->size,
1898                           widths.real_width.xy.y * penum->text_state->size,
1899                           &penum->text_state->matrix, &wanted);
1900         pte->returned.total_width.x += wanted.x;
1901         pte->returned.total_width.y += wanted.y;
1902         penum->Widths[pte->index - 1] = wanted.x;
1903 
1904         if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
1905             gs_point tpt;
1906 
1907             gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
1908                               &ctm_only(pte->pis), &tpt);
1909             dpt.x += tpt.x;
1910             dpt.y += tpt.y;
1911         }
1912         if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH && ch == pte->text.space.s_char) {
1913             gs_point tpt;
1914 
1915             gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
1916                               &ctm_only(pte->pis), &tpt);
1917             dpt.x += tpt.x;
1918             dpt.y += tpt.y;
1919         }
1920         pte->returned.total_width.x += dpt.x;
1921         pte->returned.total_width.y += dpt.y;
1922 
1923         penum->TextBufferIndex += get_unicode((gs_font *)pte->orig_font, glyph, ch, &penum->TextBuffer[penum->TextBufferIndex]);
1924         penum->Widths[pte->index - 1] += dpt.x;
1925     }
1926     return 0;
1927 }
1928 
1929 /* Routine to add the accumulated text, and its recorded properties to our
1930  * lists. We maintain a list of text on a per-page basis. Each fragment is
1931  * sorted by Y co-ordinate, then by X co-ordinate, and stored that way.
1932  * Eventually we will want to merge 'adjacent' fragments with the same
1933  * properties, at least when outputting a simple representation. We won't
1934  * do this for languages which don't read left/right or right/left though.
1935  */
1936 static int
txt_add_sorted_fragment(gx_device_txtwrite_t * tdev,textw_text_enum_t * penum)1937 txt_add_sorted_fragment(gx_device_txtwrite_t *tdev, textw_text_enum_t *penum)
1938 {
1939     if (!tdev->PageData.y_ordered_list) {
1940         /* first entry, no need to sort, just store it */
1941         tdev->PageData.y_ordered_list = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
1942             sizeof(page_text_list_t), "txtwrite alloc Y list entry");
1943         if (!tdev->PageData.y_ordered_list)
1944             return gs_note_error(gs_error_VMerror);
1945         memset(tdev->PageData.y_ordered_list, 0x00, sizeof(page_text_list_t));
1946         tdev->PageData.y_ordered_list->x_ordered_list = penum->text_state;
1947         tdev->PageData.y_ordered_list->next = tdev->PageData.y_ordered_list->previous = NULL;
1948         tdev->PageData.y_ordered_list->start = penum->text_state->start;
1949     } else {
1950         page_text_list_t *Y_List = tdev->PageData.y_ordered_list;
1951 
1952         while (Y_List->next && Y_List->start.y < penum->text_state->start.y)
1953             Y_List = Y_List->next;
1954 
1955         if (Y_List->start.y == penum->text_state->start.y) {
1956             /* Already have text at this y-position */
1957             text_list_entry_t *X_List = Y_List->x_ordered_list;
1958 
1959             while (X_List->next && X_List->start.x < penum->text_state->start.x)
1960                 X_List = X_List->next;
1961 
1962             if (X_List->start.x > penum->text_state->start.x) {
1963                 /* Insert before */
1964                 penum->text_state->next = X_List;
1965                 penum->text_state->previous = X_List->previous;
1966                 X_List->previous = penum->text_state;
1967                 if (!penum->text_state->previous)
1968                     /* New head of list */
1969                     Y_List->x_ordered_list = penum->text_state;
1970                 else
1971                     penum->text_state->previous->next = penum->text_state;
1972             } else {
1973                 /* Insert after */
1974                 penum->text_state->previous = X_List;
1975                 penum->text_state->next = X_List->next;
1976                 X_List->next = penum->text_state;
1977                 if (penum->text_state->next)
1978                     penum->text_state->next->previous = penum->text_state;
1979             }
1980             if (penum->text_state->FontBBox_bottomleft.y < Y_List->MinY)
1981                 Y_List->MinY = penum->text_state->FontBBox_bottomleft.y;
1982             if (penum->text_state->FontBBox_bottomleft.y > Y_List->MaxY)
1983                 Y_List->MaxY = penum->text_state->FontBBox_bottomleft.y;
1984             if (penum->text_state->FontBBox_topright.y < Y_List->MinY)
1985                 Y_List->MinY = penum->text_state->FontBBox_topright.y;
1986             if (penum->text_state->FontBBox_topright.y > Y_List->MaxY)
1987                 Y_List->MaxY = penum->text_state->FontBBox_topright.y;
1988         } else {
1989             /* New y-position, make a Y list new record */
1990             page_text_list_t *Y_Entry = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
1991                 sizeof(page_text_list_t), "txtwrite alloc Y-list");
1992             if (!Y_Entry)
1993                 return gs_note_error(gs_error_VMerror);
1994 
1995             Y_Entry->x_ordered_list = penum->text_state;
1996             Y_Entry->start = penum->text_state->start;
1997             if (penum->text_state->FontBBox_bottomleft.y > penum->text_state->FontBBox_topright.y) {
1998                 Y_Entry->MinY = penum->text_state->FontBBox_topright.y;
1999                 Y_Entry->MaxY = penum->text_state->FontBBox_bottomleft.y;
2000             } else {
2001                 Y_Entry->MaxY = penum->text_state->FontBBox_topright.y;
2002                 Y_Entry->MinY = penum->text_state->FontBBox_bottomleft.y;
2003             }
2004 
2005             if (Y_List->start.y > penum->text_state->start.y) {
2006                 /* Insert before */
2007                 Y_Entry->next = Y_List;
2008                 Y_Entry->previous = Y_List->previous;
2009                 Y_List->previous = Y_Entry;
2010                 if (!Y_Entry->previous)
2011                     /* New head of list */
2012                     tdev->PageData.y_ordered_list = Y_Entry;
2013                 else
2014                     ((page_text_list_t *)Y_Entry->previous)->next = Y_Entry;
2015             } else {
2016                 /* Insert after */
2017                 Y_Entry->next = Y_List->next;
2018                 Y_Entry->previous = Y_List;
2019                 Y_List->next = Y_Entry;
2020                 if (Y_Entry->next)
2021                     ((page_text_list_t *)(Y_Entry->next))->previous = Y_Entry;
2022             }
2023         }
2024     }
2025     penum->text_state = NULL;
2026     return 0;
2027 }
2028 
2029 static int
txt_add_fragment(gx_device_txtwrite_t * tdev,textw_text_enum_t * penum)2030 txt_add_fragment(gx_device_txtwrite_t *tdev, textw_text_enum_t *penum)
2031 {
2032     text_list_entry_t *unsorted_entry, *t;
2033 
2034     /* Create a duplicate entry for the unsorted list */
2035     unsorted_entry = (text_list_entry_t *)gs_malloc(tdev->memory->stable_memory, 1,
2036             sizeof(text_list_entry_t), "txtwrite alloc sorted text state");
2037     if (!unsorted_entry)
2038         return gs_note_error(gs_error_VMerror);
2039 
2040     /* Calculate the start and end points of the text */
2041     penum->text_state->start.x = fixed2float(penum->origin.x);
2042     penum->text_state->start.y = fixed2float(penum->origin.y);
2043     penum->text_state->end.x = penum->text_state->start.x + penum->returned.total_width.x;
2044     penum->text_state->end.y = penum->text_state->start.y + penum->returned.total_width.y;
2045     penum->text_state->Unicode_Text_Size = penum->TextBufferIndex;
2046 
2047     *unsorted_entry = *(penum->text_state);
2048 
2049     /* Update the saved text state with the acccumulated Unicode data */
2050     /* The working buffer (penum->TextBuffer) is freed in the text_release method */
2051     penum->text_state->Unicode_Text = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
2052         penum->TextBufferIndex, sizeof(unsigned short), "txtwrite alloc text buffer");
2053     if (!penum->text_state->Unicode_Text)
2054         return gs_note_error(gs_error_VMerror);
2055     memcpy(penum->text_state->Unicode_Text, penum->TextBuffer, penum->TextBufferIndex * sizeof(unsigned short));
2056 
2057     penum->text_state->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
2058         penum->TextBufferIndex, sizeof(float), "txtwrite alloc widths array");
2059     if (!penum->text_state->Widths)
2060         return gs_note_error(gs_error_VMerror);
2061     memcpy(penum->text_state->Widths, penum->Widths, penum->TextBufferIndex * sizeof(float));
2062 
2063     unsorted_entry->Unicode_Text = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
2064         penum->TextBufferIndex, sizeof(unsigned short), "txtwrite alloc sorted text buffer");
2065     if (!unsorted_entry->Unicode_Text)
2066         return gs_note_error(gs_error_VMerror);
2067     memcpy(unsorted_entry->Unicode_Text, penum->TextBuffer, penum->TextBufferIndex * sizeof(unsigned short));
2068 
2069     unsorted_entry->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
2070         penum->TextBufferIndex, sizeof(float), "txtwrite alloc widths array");
2071     if (!unsorted_entry->Widths)
2072         return gs_note_error(gs_error_VMerror);
2073     memcpy(unsorted_entry->Widths, penum->Widths, penum->TextBufferIndex * sizeof(float));
2074 
2075     unsorted_entry->FontName = (char *)gs_malloc(tdev->memory->stable_memory,
2076         (strlen(penum->text_state->FontName) + 1), sizeof(unsigned short), "txtwrite alloc sorted text buffer");
2077     if (!unsorted_entry->Unicode_Text)
2078         return gs_note_error(gs_error_VMerror);
2079     memcpy(unsorted_entry->FontName, penum->text_state->FontName, strlen(penum->text_state->FontName) * sizeof(unsigned short));
2080     unsorted_entry->FontName[strlen(penum->text_state->FontName)] = 0x00;
2081 
2082     /* First add one entry to the unsorted list */
2083     if (!tdev->PageData.unsorted_text_list) {
2084         tdev->PageData.unsorted_text_list = unsorted_entry;
2085         unsorted_entry->next = unsorted_entry->previous = NULL;
2086     } else {
2087         t = tdev->PageData.unsorted_text_list;
2088         while (t->next)
2089             t = t->next;
2090         t->next = unsorted_entry;
2091         unsorted_entry->next = NULL;
2092         unsorted_entry->previous = t;
2093     }
2094 
2095     /* Then add the other entry to the sorted list */
2096     return txt_add_sorted_fragment(tdev, penum);
2097 }
2098 
2099 /* This routine selects whether the text needs to be handled as regular glyphs
2100  * and character codes, or as CIDs, depending on the font type. This is required
2101  * because there are ways that regular text can be handled that aren't possible
2102  * with CIDFonts.
2103  */
2104 static int
textw_text_process(gs_text_enum_t * pte)2105 textw_text_process(gs_text_enum_t *pte)
2106 {
2107     textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
2108     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) pte->dev;
2109     gs_font *font = pte->orig_font;
2110     gs_font_base *font_base = (gs_font_base *)pte->current_font;
2111     int code = 0;
2112 
2113     if (!penum->TextBuffer) {
2114         /* We can get up to 4 Unicode points per glyph, and a glyph can be
2115          * be represented by as little as one byte. So we make a very large
2116          * temporary buffer to hold the Unicode string as we assemble it. When
2117          * we copy it to the text fragment we will allocate only as many bytes
2118          * as are required to hold the actual nukmber of Unicode values we
2119          * decoded, and this temporary buffer will be discarded.
2120          */
2121         penum->TextBuffer = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
2122             pte->text.size * 4, sizeof(unsigned short), "txtwrite temporary text buffer");
2123         if (!penum->TextBuffer)
2124             return gs_note_error(gs_error_VMerror);
2125         penum->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
2126             pte->text.size, sizeof(float), "txtwrite temporary widths array");
2127         if (!penum->Widths)
2128             return gs_note_error(gs_error_VMerror);
2129     }
2130     {
2131         switch (font->FontType) {
2132         case ft_CID_encrypted:
2133         case ft_CID_TrueType:
2134         case ft_composite:
2135               code = txtwrite_process_cmap_text(pte);
2136             break;
2137         case ft_encrypted:
2138         case ft_encrypted2:
2139         case ft_TrueType:
2140         case ft_user_defined:
2141         case ft_PCL_user_defined:
2142         case ft_GL2_stick_user_defined:
2143             code = txtwrite_process_plain_text(pte);
2144             break;
2145         default:
2146             return gs_error_rangecheck;
2147             break;
2148         }
2149         if (code == 0) {
2150             if (font_base->FontBBox.p.x != font_base->FontBBox.q.x ||
2151                 font_base->FontBBox.p.y != font_base->FontBBox.q.y) {
2152                 gs_point p0, p1, p2, p3;
2153                 gs_matrix m;
2154 
2155                 m = ctm_only(pte->pis);
2156                 m.tx = m.ty = fixed2float(0);
2157                 gs_matrix_multiply(&font_base->FontMatrix, &m, &m);
2158                 gs_point_transform(font_base->FontBBox.p.x, font_base->FontBBox.p.y, &m, &p0);
2159                 gs_point_transform(font_base->FontBBox.p.x, font_base->FontBBox.q.y, &m, &p1);
2160                 gs_point_transform(font_base->FontBBox.q.x, font_base->FontBBox.p.y, &m, &p2);
2161                 gs_point_transform(font_base->FontBBox.q.x, font_base->FontBBox.q.y, &m, &p3);
2162                 penum->text_state->FontBBox_bottomleft.x = min(min(p0.x, p1.x), min(p1.x, p2.x));
2163                 penum->text_state->FontBBox_topright.x = max(max(p0.x, p1.x), max(p1.x, p2.x));
2164                 penum->text_state->FontBBox_bottomleft.y = min(min(p0.y, p1.y), min(p1.y, p2.y));
2165                 penum->text_state->FontBBox_topright.y = max(max(p0.y, p1.y), max(p1.y, p2.y));
2166             }
2167             code = txt_shift_text_currentpoint(penum, &penum->returned.total_width);
2168             if (code != 0)
2169                 return code;
2170 
2171             code = txt_add_fragment(tdev, penum);
2172         }
2173     }
2174     return code;
2175 }
2176 
2177 /* Begin processing text. */
2178 
2179 /* Define the auxiliary procedures for text processing. */
2180 static int
textw_text_resync(gs_text_enum_t * pte,const gs_text_enum_t * pfrom)2181 textw_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
2182 {
2183     return gs_text_resync(pte, pfrom);
2184 }
2185 static bool
textw_text_is_width_only(const gs_text_enum_t * pte)2186 textw_text_is_width_only(const gs_text_enum_t *pte)
2187 {
2188     return false;
2189 }
2190 static int
textw_text_current_width(const gs_text_enum_t * pte,gs_point * pwidth)2191 textw_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
2192 {
2193     return gs_text_current_width(pte, pwidth);
2194 }
2195 static int
textw_text_set_cache(gs_text_enum_t * pte,const double * pw,gs_text_cache_control_t control)2196 textw_text_set_cache(gs_text_enum_t *pte, const double *pw,
2197                    gs_text_cache_control_t control)
2198 {
2199     textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
2200 
2201     switch (control) {
2202         case TEXT_SET_CHAR_WIDTH:
2203         case TEXT_SET_CACHE_DEVICE:
2204             return gs_text_set_cache(pte, pw, control);
2205         case TEXT_SET_CACHE_DEVICE2:
2206             if (penum->cdevproc_callout) {
2207                 memcpy(penum->cdevproc_result, pw, sizeof(penum->cdevproc_result));
2208                 return 0;
2209             }
2210             return gs_text_set_cache(pte, pw, control);
2211         default:
2212             return_error(gs_error_rangecheck);
2213     }
2214     return 0;
2215 }
2216 
2217 static int
textw_text_retry(gs_text_enum_t * pte)2218 textw_text_retry(gs_text_enum_t *pte)
2219 {
2220     return gs_text_retry(pte);
2221 }
2222 static void
textw_text_release(gs_text_enum_t * pte,client_name_t cname)2223 textw_text_release(gs_text_enum_t *pte, client_name_t cname)
2224 {
2225     textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
2226     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) pte->dev;
2227 
2228     /* Free the working buffer where the Unicode was assembled from the enumerated text */
2229     if (penum->TextBuffer)
2230         gs_free(tdev->memory, penum->TextBuffer, 1, penum->TextBufferIndex, "txtwrite free temporary text buffer");
2231     if (penum->Widths)
2232         gs_free(tdev->memory, penum->Widths, sizeof(float), pte->text.size, "txtwrite free temporary widths array");
2233     /* If this is copied away when we complete the text enumeration succesfully, then
2234      * we set the pointer to NULL, if we get here with it non-NULL , then there was
2235      * an error.
2236      */
2237     if (penum->text_state)
2238         gs_free(tdev->memory, penum->text_state, 1, sizeof(penum->text_state), "txtwrite free text state");
2239 
2240     gs_text_release(pte, cname);
2241 }
2242 
2243 /* This is the list of methods for the text enumerator */
2244 static const gs_text_enum_procs_t textw_text_procs = {
2245     textw_text_resync, textw_text_process,
2246     textw_text_is_width_only, textw_text_current_width,
2247     textw_text_set_cache, textw_text_retry,
2248     textw_text_release
2249 };
2250 
2251 /* This device method gets called by the interpreter to deal with text. It
2252  * must create a text enumerator, which contains the methods for dealing with
2253  * the text itself. The interpreter will use the enumerator methods to deal with
2254  * the text, it won't refer to the device methods again for this text.
2255  */
2256 static int
txtwrite_text_begin(gx_device * dev,gs_imager_state * pis,const gs_text_params_t * text,gs_font * font,gx_path * path,const gx_device_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gs_text_enum_t ** ppenum)2257 txtwrite_text_begin(gx_device * dev, gs_imager_state * pis,
2258                 const gs_text_params_t * text, gs_font * font,
2259                 gx_path * path, const gx_device_color * pdcolor,
2260                 const gx_clip_path * pcpath,
2261                 gs_memory_t * mem, gs_text_enum_t ** ppenum)
2262 {
2263     gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
2264     textw_text_enum_t *penum;
2265     int code;
2266 
2267     /* Allocate and initialize one of our text enumerators. */
2268     rc_alloc_struct_1(penum, textw_text_enum_t, &st_textw_text_enum, mem,
2269                       return_error(gs_error_VMerror), "gdev_textw_text_begin");
2270     penum->rc.free = rc_free_text_enum;
2271     penum->charproc_accum = false;
2272     penum->cdevproc_callout = false;
2273     penum->returned.total_width.x = penum->returned.total_width.y = 0;
2274     penum->TextBuffer = NULL;
2275     penum->TextBufferIndex = 0;
2276     /* The enumerator's text_release method frees this memory */
2277     penum->text_state = (text_list_entry_t *)gs_malloc(tdev->memory->stable_memory, 1,
2278             sizeof(text_list_entry_t), "txtwrite alloc text state");
2279     if (!penum->text_state)
2280         return gs_note_error(gs_error_VMerror);
2281     memset(penum->text_state, 0x00, sizeof(text_list_entry_t));
2282 
2283     code = gs_text_enum_init((gs_text_enum_t *)penum, &textw_text_procs,
2284                              dev, pis, text, font, path, pdcolor, pcpath, mem);
2285     if (code < 0) {
2286         /* Belt and braces; I'm not certain this is required, but its safe */
2287         gs_free(tdev->memory, penum->text_state, 1, sizeof(text_list_entry_t), "txtwrite free text state");
2288         penum->text_state = NULL;
2289         gs_free_object(mem, penum, "textwrite_text_begin");
2290         return code;
2291     }
2292 
2293     code = gx_path_current_point(penum->path, &penum->origin);
2294     if (code != 0)
2295        return code;
2296 
2297     *ppenum = (gs_text_enum_t *)penum;
2298 
2299     return 0;
2300 }
2301 
2302 static int
txtwrite_strip_copy_rop(gx_device * dev,const byte * sdata,int sourcex,uint sraster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int w,int h,int phase_x,int phase_y,gs_logical_operation_t lop)2303 txtwrite_strip_copy_rop(gx_device * dev,
2304                     const byte * sdata, int sourcex, uint sraster,
2305                     gx_bitmap_id id,
2306                     const gx_color_index * scolors,
2307                     const gx_strip_bitmap * textures,
2308                     const gx_color_index * tcolors,
2309                     int x, int y, int w, int h,
2310                     int phase_x, int phase_y, gs_logical_operation_t lop)
2311 {
2312     return 0;
2313 }
2314