Coverage Report

Created: 2025-04-22 06:20

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