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, "<"); break;
637 case 0x3E: sprintf(Buf, ">"); break;
638 case 0x26: sprintf(Buf, "&"); break;
639 case 0x22: sprintf(Buf, """); break;
640 case 0x27: sprintf(Buf, "'"); 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