Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "mupdf/pdf.h"
2
3
#include <ft2build.h>
4
#include FT_FREETYPE_H
5
#include FT_ADVANCES_H
6
7
#define ALLOWED_TEXT_POS_ERROR (0.001f)
8
9
typedef struct pdf_device_s pdf_device;
10
11
typedef struct gstate_s gstate;
12
13
struct gstate_s
14
{
15
/* The first few entries aren't really graphics state things, but
16
* they are recorded here as they are fundamentally intertwined with
17
* the push/pulling of the gstates. */
18
fz_buffer *buf;
19
void (*on_pop)(fz_context*,pdf_device*,void *);
20
void *on_pop_arg;
21
/* The graphics state proper */
22
fz_colorspace *colorspace[2];
23
float color[2][4];
24
fz_matrix ctm;
25
fz_stroke_state *stroke_state;
26
float alpha[2];
27
int font;
28
float font_size;
29
float char_spacing;
30
float word_spacing;
31
float horizontal_scaling;
32
float leading;
33
int text_rendering_mode;
34
float rise;
35
int knockout;
36
fz_matrix tm;
37
};
38
39
typedef struct image_entry_s image_entry;
40
41
struct image_entry_s
42
{
43
char digest[16];
44
int id;
45
pdf_obj *ref;
46
};
47
48
typedef struct alpha_entry_s alpha_entry;
49
50
struct alpha_entry_s
51
{
52
float alpha;
53
int stroke;
54
};
55
56
typedef struct font_entry_s font_entry;
57
58
struct font_entry_s
59
{
60
fz_font *font;
61
};
62
63
typedef struct group_entry_s group_entry;
64
65
struct group_entry_s
66
{
67
int alpha;
68
int isolated;
69
int knockout;
70
fz_colorspace *colorspace;
71
pdf_obj *ref;
72
};
73
74
struct pdf_device_s
75
{
76
fz_device super;
77
78
pdf_document *doc;
79
pdf_obj *contents;
80
pdf_obj *resources;
81
fz_buffer *buffer;
82
83
int in_text;
84
85
int num_forms;
86
int num_smasks;
87
88
int num_gstates;
89
int max_gstates;
90
gstate *gstates;
91
92
int num_imgs;
93
int max_imgs;
94
image_entry *images;
95
96
int num_alphas;
97
int max_alphas;
98
alpha_entry *alphas;
99
100
int num_fonts;
101
int max_fonts;
102
font_entry *fonts;
103
104
int num_groups;
105
int max_groups;
106
group_entry *groups;
107
};
108
109
#define CURRENT_GSTATE(pdev) (&(pdev)->gstates[(pdev)->num_gstates-1])
110
111
/* Helper functions */
112
113
static int
114
send_image(fz_context *ctx, pdf_device *pdev, fz_image *image, int mask, int smask)
115
{
116
fz_pixmap *pixmap = NULL;
117
pdf_obj *imobj = NULL;
118
pdf_obj *imref = NULL;
119
fz_compressed_buffer *cbuffer = NULL;
120
fz_compression_params *cp = NULL;
121
fz_buffer *buffer = NULL;
122
int i, num;
123
fz_md5 state;
124
unsigned char digest[16];
125
fz_colorspace *colorspace = image->colorspace;
126
pdf_document *doc = pdev->doc;
127
128
/* If we can maintain compression, do so */
129
cbuffer = image->buffer;
130
131
fz_var(pixmap);
132
fz_var(buffer);
133
fz_var(imobj);
134
fz_var(imref);
135
136
fz_try(ctx)
137
{
138
if (cbuffer != NULL && cbuffer->params.type != FZ_IMAGE_PNG && cbuffer->params.type != FZ_IMAGE_TIFF)
139
{
140
buffer = fz_keep_buffer(ctx, cbuffer->buffer);
141
cp = &cbuffer->params;
142
}
143
else
144
{
145
unsigned int size;
146
int n;
147
/* Currently, set to maintain resolution; should we consider
148
* subsampling here according to desired output res? */
149
pixmap = image->get_pixmap(ctx, image, image->w, image->h);
150
colorspace = pixmap->colorspace; /* May be different to image->colorspace! */
151
n = (pixmap->n == 1 ? 1 : pixmap->n-1);
152
size = image->w * image->h * n;
153
buffer = fz_new_buffer(ctx, size);
154
buffer->len = size;
155
if (pixmap->n == 1)
156
{
157
memcpy(buffer->data, pixmap->samples, size);
158
}
159
else
160
{
161
/* Need to remove the alpha plane */
162
unsigned char *d = buffer->data;
163
unsigned char *s = pixmap->samples;
164
int mod = n;
165
while (size--)
166
{
167
*d++ = *s++;
168
mod--;
169
if (mod == 0)
170
s++, mod = n;
171
}
172
}
173
}
174
175
fz_md5_init(&state);
176
fz_md5_update(&state, buffer->data, buffer->len);
177
fz_md5_final(&state, digest);
178
for(i=0; i < pdev->num_imgs; i++)
179
{
180
if (!memcmp(&digest, pdev->images[i].digest, sizeof(16)))
181
{
182
num = i;
183
break;
184
}
185
}
186
187
if (i < pdev->num_imgs)
188
break;
189
190
if (pdev->num_imgs == pdev->max_imgs)
191
{
192
int newmax = pdev->max_imgs * 2;
193
if (newmax == 0)
194
newmax = 4;
195
pdev->images = fz_resize_array(ctx, pdev->images, newmax, sizeof(*pdev->images));
196
pdev->max_imgs = newmax;
197
}
198
num = pdev->num_imgs++;
199
memcpy(pdev->images[num].digest,digest,16);
200
pdev->images[num].ref = NULL; /* Will be filled in later */
201
202
imobj = pdf_new_dict(ctx, doc, 3);
203
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Type, PDF_NAME_XObject);
204
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Subtype, PDF_NAME_Image);
205
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Width, pdf_new_int(ctx, doc, image->w));
206
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Height, pdf_new_int(ctx, doc, image->h));
207
if (mask)
208
{}
209
else if (!colorspace || colorspace->n == 1)
210
pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceGray);
211
else if (colorspace->n == 3)
212
pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceRGB);
213
else if (colorspace->n == 4)
214
pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceCMYK);
215
if (!mask)
216
pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
217
switch (cp ? cp->type : FZ_IMAGE_UNKNOWN)
218
{
219
case FZ_IMAGE_UNKNOWN: /* Unknown also means raw */
220
default:
221
break;
222
case FZ_IMAGE_JPEG:
223
if (cp->u.jpeg.color_transform != -1)
224
pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorTransform, pdf_new_int(ctx, doc, cp->u.jpeg.color_transform));
225
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_DCTDecode);
226
break;
227
case FZ_IMAGE_JPX:
228
if (cp->u.jpx.smask_in_data)
229
pdf_dict_put_drop(ctx, imobj, PDF_NAME_SMaskInData, pdf_new_int(ctx, doc, cp->u.jpx.smask_in_data));
230
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_JPXDecode);
231
break;
232
case FZ_IMAGE_FAX:
233
if (cp->u.fax.columns)
234
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.fax.columns));
235
if (cp->u.fax.rows)
236
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Rows, pdf_new_int(ctx, doc, cp->u.fax.rows));
237
if (cp->u.fax.k)
238
pdf_dict_put_drop(ctx, imobj, PDF_NAME_K, pdf_new_int(ctx, doc, cp->u.fax.k));
239
if (cp->u.fax.end_of_line)
240
pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfLine, pdf_new_int(ctx, doc, cp->u.fax.end_of_line));
241
if (cp->u.fax.encoded_byte_align)
242
pdf_dict_put_drop(ctx, imobj, PDF_NAME_EncodedByteAlign, pdf_new_int(ctx, doc, cp->u.fax.encoded_byte_align));
243
if (cp->u.fax.end_of_block)
244
pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfBlock, pdf_new_int(ctx, doc, cp->u.fax.end_of_block));
245
if (cp->u.fax.black_is_1)
246
pdf_dict_put_drop(ctx, imobj, PDF_NAME_BlackIs1, pdf_new_int(ctx, doc, cp->u.fax.black_is_1));
247
if (cp->u.fax.damaged_rows_before_error)
248
pdf_dict_put_drop(ctx, imobj, PDF_NAME_DamagedRowsBeforeError, pdf_new_int(ctx, doc, cp->u.fax.damaged_rows_before_error));
249
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_CCITTFaxDecode);
250
break;
251
case FZ_IMAGE_JBIG2:
252
/* FIXME - jbig2globals */
253
cp->type = FZ_IMAGE_UNKNOWN;
254
break;
255
case FZ_IMAGE_FLATE:
256
if (cp->u.flate.columns)
257
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.flate.columns));
258
if (cp->u.flate.colors)
259
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.flate.colors));
260
if (cp->u.flate.predictor)
261
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.flate.predictor));
262
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_FlateDecode);
263
pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
264
break;
265
case FZ_IMAGE_LZW:
266
if (cp->u.lzw.columns)
267
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.lzw.columns));
268
if (cp->u.lzw.colors)
269
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.lzw.colors));
270
if (cp->u.lzw.predictor)
271
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.lzw.predictor));
272
if (cp->u.lzw.early_change)
273
pdf_dict_put_drop(ctx, imobj, PDF_NAME_EarlyChange, pdf_new_int(ctx, doc, cp->u.lzw.early_change));
274
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_LZWDecode);
275
break;
276
case FZ_IMAGE_RLD:
277
pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_RunLengthDecode);
278
break;
279
}
280
if (mask)
281
{
282
pdf_dict_put_drop(ctx, imobj, PDF_NAME_ImageMask, pdf_new_bool(ctx, doc, 1));
283
}
284
if (image->mask)
285
{
286
int smasknum = send_image(ctx, pdev, image->mask, 0, 1);
287
pdf_dict_put(ctx, imobj, PDF_NAME_SMask, pdev->images[smasknum].ref);
288
}
289
290
imref = pdf_new_ref(ctx, doc, imobj);
291
pdf_update_stream(ctx, doc, imref, buffer, 1);
292
293
{
294
char text[32];
295
snprintf(text, sizeof(text), "XObject/Img%d", num);
296
pdf_dict_putp(ctx, pdev->resources, text, imref);
297
}
298
pdev->images[num].ref = imref;
299
}
300
fz_always(ctx)
301
{
302
fz_drop_buffer(ctx, buffer);
303
pdf_drop_obj(ctx, imobj);
304
fz_drop_pixmap(ctx, pixmap);
305
}
306
fz_catch(ctx)
307
{
308
pdf_drop_obj(ctx, imref);
309
fz_rethrow(ctx);
310
}
311
return num;
312
}
313
314
static void
315
pdf_dev_stroke_state(fz_context *ctx, pdf_device *pdev, fz_stroke_state *stroke_state)
316
{
317
gstate *gs = CURRENT_GSTATE(pdev);
318
319
if (stroke_state == gs->stroke_state)
320
return;
321
if (gs->stroke_state && !memcmp(stroke_state, gs->stroke_state, sizeof(*stroke_state)))
322
return;
323
if (!gs->stroke_state || gs->stroke_state->linewidth != stroke_state->linewidth)
324
{
325
fz_buffer_printf(ctx, gs->buf, "%f w\n", stroke_state->linewidth);
326
}
327
if (!gs->stroke_state || gs->stroke_state->start_cap != stroke_state->start_cap)
328
{
329
int cap = stroke_state->start_cap;
330
/* FIXME: Triangle caps aren't supported in pdf */
331
if (cap == FZ_LINECAP_TRIANGLE)
332
cap = FZ_LINECAP_BUTT;
333
fz_buffer_printf(ctx, gs->buf, "%d J\n", cap);
334
}
335
if (!gs->stroke_state || gs->stroke_state->linejoin != stroke_state->linejoin)
336
{
337
int join = stroke_state->linejoin;
338
if (join == FZ_LINEJOIN_MITER_XPS)
339
join = FZ_LINEJOIN_MITER;
340
fz_buffer_printf(ctx, gs->buf, "%d j\n", join);
341
}
342
if (!gs->stroke_state || gs->stroke_state->miterlimit != stroke_state->miterlimit)
343
{
344
fz_buffer_printf(ctx, gs->buf, "%f M\n", stroke_state->miterlimit);
345
}
346
if (gs->stroke_state == NULL && stroke_state->dash_len == 0)
347
{}
348
else if (!gs->stroke_state || gs->stroke_state->dash_phase != stroke_state->dash_phase || gs->stroke_state->dash_len != stroke_state->dash_len ||
349
memcmp(gs->stroke_state->dash_list, stroke_state->dash_list, sizeof(float)*stroke_state->dash_len))
350
{
351
int i;
352
if (stroke_state->dash_len == 0)
353
fz_buffer_printf(ctx, gs->buf, "[");
354
for (i = 0; i < stroke_state->dash_len; i++)
355
fz_buffer_printf(ctx, gs->buf, "%c%f", (i == 0 ? '[' : ' '), stroke_state->dash_list[i]);
356
fz_buffer_printf(ctx, gs->buf, "]%f d\n", stroke_state->dash_phase);
357
358
}
359
fz_drop_stroke_state(ctx, gs->stroke_state);
360
gs->stroke_state = fz_keep_stroke_state(ctx, stroke_state);
361
}
362
363
typedef struct
364
{
365
fz_context *ctx;
366
fz_buffer *buf;
367
} pdf_dev_path_arg;
368
369
static void
370
pdf_dev_path_moveto(fz_context *ctx, void *arg, float x, float y)
371
{
372
fz_buffer *buf = (fz_buffer *)arg;
373
374
fz_buffer_printf(ctx, buf, "%f %f m\n", x, y);
375
}
376
377
static void
378
pdf_dev_path_lineto(fz_context *ctx, void *arg, float x, float y)
379
{
380
fz_buffer *buf = (fz_buffer *)arg;
381
382
fz_buffer_printf(ctx, buf, "%f %f l\n", x, y);
383
}
384
385
static void
386
pdf_dev_path_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3)
387
{
388
fz_buffer *buf = (fz_buffer *)arg;
389
390
fz_buffer_printf(ctx, buf, "%f %f %f %f %f %f c\n", x1, y1, x2, y2, x3, y3);
391
}
392
393
static void
394
pdf_dev_path_close(fz_context *ctx, void *arg)
395
{
396
fz_buffer *buf = (fz_buffer *)arg;
397
398
fz_buffer_printf(ctx, buf, "h\n");
399
}
400
401
static const fz_path_processor pdf_dev_path_proc =
402
{
403
pdf_dev_path_moveto,
404
pdf_dev_path_lineto,
405
pdf_dev_path_curveto,
406
pdf_dev_path_close
407
};
408
409
static void
410
pdf_dev_path(fz_context *ctx, pdf_device *pdev, fz_path *path)
411
{
412
gstate *gs = CURRENT_GSTATE(pdev);
413
414
fz_process_path(ctx, &pdf_dev_path_proc, (void *)gs->buf, path);
415
}
416
417
static void
418
pdf_dev_ctm(fz_context *ctx, pdf_device *pdev, const fz_matrix *ctm)
419
{
420
fz_matrix inverse;
421
gstate *gs = CURRENT_GSTATE(pdev);
422
423
if (memcmp(&gs->ctm, ctm, sizeof(*ctm)) == 0)
424
return;
425
fz_invert_matrix(&inverse, &gs->ctm);
426
fz_concat(&inverse, ctm, &inverse);
427
memcpy(&gs->ctm, ctm, sizeof(*ctm));
428
fz_buffer_printf(ctx, gs->buf, "%f %f %f %f %f %f cm\n", inverse.a, inverse.b, inverse.c, inverse.d, inverse.e, inverse.f);
429
}
430
431
static void
432
pdf_dev_color(fz_context *ctx, pdf_device *pdev, fz_colorspace *colorspace, float *color, int stroke)
433
{
434
int diff = 0;
435
int i;
436
int cspace = 0;
437
float rgb[FZ_MAX_COLORS];
438
gstate *gs = CURRENT_GSTATE(pdev);
439
440
if (colorspace == fz_device_gray(ctx))
441
cspace = 1;
442
else if (colorspace == fz_device_rgb(ctx))
443
cspace = 3;
444
else if (colorspace == fz_device_cmyk(ctx))
445
cspace = 4;
446
447
if (cspace == 0)
448
{
449
/* If it's an unknown colorspace, fallback to rgb */
450
colorspace->to_rgb(ctx, colorspace, color, rgb);
451
color = rgb;
452
colorspace = fz_device_rgb(ctx);
453
}
454
455
if (gs->colorspace[stroke] != colorspace)
456
{
457
gs->colorspace[stroke] = colorspace;
458
diff = 1;
459
}
460
461
for (i=0; i < colorspace->n; i++)
462
if (gs->color[stroke][i] != color[i])
463
{
464
gs->color[stroke][i] = color[i];
465
diff = 1;
466
}
467
468
if (diff == 0)
469
return;
470
471
switch (cspace + stroke*8)
472
{
473
case 1:
474
fz_buffer_printf(ctx, gs->buf, "%f g\n", color[0]);
475
break;
476
case 3:
477
fz_buffer_printf(ctx, gs->buf, "%f %f %f rg\n", color[0], color[1], color[2]);
478
break;
479
case 4:
480
fz_buffer_printf(ctx, gs->buf, "%f %f %f %f k\n", color[0], color[1], color[2], color[3]);
481
break;
482
case 1+8:
483
fz_buffer_printf(ctx, gs->buf, "%f G\n", color[0]);
484
break;
485
case 3+8:
486
fz_buffer_printf(ctx, gs->buf, "%f %f %f RG\n", color[0], color[1], color[2]);
487
break;
488
case 4+8:
489
fz_buffer_printf(ctx, gs->buf, "%f %f %f %f K\n", color[0], color[1], color[2], color[3]);
490
break;
491
}
492
}
493
494
static void
495
pdf_dev_alpha(fz_context *ctx, pdf_device *pdev, float alpha, int stroke)
496
{
497
int i;
498
pdf_document *doc = pdev->doc;
499
gstate *gs = CURRENT_GSTATE(pdev);
500
501
/* If the alpha is unchanged, nothing to do */
502
if (gs->alpha[stroke] == alpha)
503
return;
504
505
/* Have we sent such an alpha before? */
506
for (i = 0; i < pdev->num_alphas; i++)
507
if (pdev->alphas[i].alpha == alpha && pdev->alphas[i].stroke == stroke)
508
break;
509
510
if (i == pdev->num_alphas)
511
{
512
pdf_obj *o;
513
pdf_obj *ref = NULL;
514
515
fz_var(ref);
516
517
/* No. Need to make a new one */
518
if (pdev->num_alphas == pdev->max_alphas)
519
{
520
int newmax = pdev->max_alphas * 2;
521
if (newmax == 0)
522
newmax = 4;
523
pdev->alphas = fz_resize_array(ctx, pdev->alphas, newmax, sizeof(*pdev->alphas));
524
pdev->max_alphas = newmax;
525
}
526
pdev->alphas[i].alpha = alpha;
527
pdev->alphas[i].stroke = stroke;
528
529
o = pdf_new_dict(ctx, doc, 1);
530
fz_try(ctx)
531
{
532
char text[32];
533
pdf_dict_put_drop(ctx, o, (stroke ? PDF_NAME_CA : PDF_NAME_ca), pdf_new_real(ctx, doc, alpha));
534
ref = pdf_new_ref(ctx, doc, o);
535
snprintf(text, sizeof(text), "ExtGState/Alp%d", i);
536
pdf_dict_putp(ctx, pdev->resources, text, ref);
537
}
538
fz_always(ctx)
539
{
540
pdf_drop_obj(ctx, o);
541
pdf_drop_obj(ctx, ref);
542
}
543
fz_catch(ctx)
544
{
545
fz_rethrow(ctx);
546
}
547
pdev->num_alphas++;
548
}
549
fz_buffer_printf(ctx, gs->buf, "/Alp%d gs\n", i);
550
}
551
552
static void
553
pdf_dev_font(fz_context *ctx, pdf_device *pdev, fz_font *font, float size)
554
{
555
int i;
556
pdf_document *doc = pdev->doc;
557
gstate *gs = CURRENT_GSTATE(pdev);
558
559
/* If the font is unchanged, nothing to do */
560
if (gs->font >= 0 && pdev->fonts[gs->font].font == font)
561
return;
562
563
if (font->ft_buffer != NULL || font->ft_substitute)
564
fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device supports only base 14 fonts currently");
565
566
/* Have we sent such a font before? */
567
for (i = 0; i < pdev->num_fonts; i++)
568
if (pdev->fonts[i].font == font)
569
break;
570
571
if (i == pdev->num_fonts)
572
{
573
pdf_obj *o;
574
pdf_obj *ref = NULL;
575
576
fz_var(ref);
577
578
/* No. Need to make a new one */
579
if (pdev->num_fonts == pdev->max_fonts)
580
{
581
int newmax = pdev->max_fonts * 2;
582
if (newmax == 0)
583
newmax = 4;
584
pdev->fonts = fz_resize_array(ctx, pdev->fonts, newmax, sizeof(*pdev->fonts));
585
pdev->max_fonts = newmax;
586
}
587
pdev->fonts[i].font = fz_keep_font(ctx, font);
588
589
o = pdf_new_dict(ctx, doc, 3);
590
fz_try(ctx)
591
{
592
char text[32];
593
pdf_dict_put_drop(ctx, o, PDF_NAME_Type, PDF_NAME_Font);
594
pdf_dict_put_drop(ctx, o, PDF_NAME_Subtype, PDF_NAME_Type1);
595
pdf_dict_put_drop(ctx, o, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, font->name));
596
pdf_dict_put_drop(ctx, o, PDF_NAME_Encoding, PDF_NAME_WinAnsiEncoding);
597
ref = pdf_new_ref(ctx, doc, o);
598
snprintf(text, sizeof(text), "Font/F%d", i);
599
pdf_dict_putp(ctx, pdev->resources, text, ref);
600
}
601
fz_always(ctx)
602
{
603
pdf_drop_obj(ctx, o);
604
pdf_drop_obj(ctx, ref);
605
}
606
fz_catch(ctx)
607
{
608
fz_rethrow(ctx);
609
}
610
pdev->num_fonts++;
611
}
612
fz_buffer_printf(ctx, gs->buf, "/F%d %f Tf\n", i, size);
613
}
614
615
static void
616
pdf_dev_tm(fz_context *ctx, pdf_device *pdev, const fz_matrix *tm)
617
{
618
gstate *gs = CURRENT_GSTATE(pdev);
619
620
if (memcmp(&gs->tm, tm, sizeof(*tm)) == 0)
621
return;
622
fz_buffer_printf(ctx, gs->buf, "%f %f %f %f %f %f Tm\n", tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
623
gs->tm = *tm;
624
}
625
626
static void
627
pdf_dev_push_new_buf(fz_context *ctx, pdf_device *pdev, fz_buffer *buf, void (*on_pop)(fz_context*,pdf_device*,void*), void *on_pop_arg)
628
{
629
if (pdev->num_gstates == pdev->max_gstates)
630
{
631
int newmax = pdev->max_gstates*2;
632
633
pdev->gstates = fz_resize_array(ctx, pdev->gstates, newmax, sizeof(*pdev->gstates));
634
pdev->max_gstates = newmax;
635
}
636
memcpy(&pdev->gstates[pdev->num_gstates], &pdev->gstates[pdev->num_gstates-1], sizeof(*pdev->gstates));
637
fz_keep_stroke_state(ctx, pdev->gstates[pdev->num_gstates].stroke_state);
638
if (buf)
639
pdev->gstates[pdev->num_gstates].buf = buf;
640
else
641
fz_keep_buffer(ctx, pdev->gstates[pdev->num_gstates].buf);
642
pdev->gstates[pdev->num_gstates].on_pop = on_pop;
643
pdev->gstates[pdev->num_gstates].on_pop_arg = on_pop_arg;
644
fz_buffer_printf(ctx, pdev->gstates[pdev->num_gstates].buf, "q\n");
645
pdev->num_gstates++;
646
}
647
648
static void
649
pdf_dev_push(fz_context *ctx, pdf_device *pdev)
650
{
651
pdf_dev_push_new_buf(ctx, pdev, NULL, NULL, NULL);
652
}
653
654
static void *
655
pdf_dev_pop(fz_context *ctx, pdf_device *pdev)
656
{
657
gstate *gs = CURRENT_GSTATE(pdev);
658
void *arg = gs->on_pop_arg;
659
660
fz_buffer_printf(ctx, gs->buf, "Q\n");
661
if (gs->on_pop)
662
gs->on_pop(ctx, pdev, arg);
663
pdev->num_gstates--;
664
fz_drop_stroke_state(ctx, pdev->gstates[pdev->num_gstates].stroke_state);
665
fz_drop_buffer(ctx, pdev->gstates[pdev->num_gstates].buf);
666
return arg;
667
}
668
669
static void
670
pdf_dev_text(fz_context *ctx, pdf_device *pdev, fz_text *text, float size)
671
{
672
int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
673
int i;
674
fz_matrix trm;
675
fz_matrix inverse;
676
gstate *gs = CURRENT_GSTATE(pdev);
677
fz_matrix trunc_trm;
678
679
trm = gs->tm;
680
trunc_trm.a = trm.a;
681
trunc_trm.b = trm.b;
682
trunc_trm.c = trm.c;
683
trunc_trm.d = trm.d;
684
trunc_trm.e = 0;
685
trunc_trm.f = 0;
686
fz_invert_matrix(&inverse, &trunc_trm);
687
688
i = 0;
689
while (i < text->len)
690
{
691
fz_text_item *it = &text->items[i];
692
fz_point delta;
693
float x;
694
int j;
695
696
delta.x = it->x - trm.e;
697
delta.y = it->y - trm.f;
698
fz_transform_point(&delta, &inverse);
699
if (delta.x != 0 || delta.y != 0)
700
{
701
fz_buffer_printf(ctx, gs->buf, "%f %f Td ", delta.x, delta.y);
702
trm.e = it->x;
703
trm.f = it->y;
704
}
705
706
j = i+1;
707
if (text->font->ft_face)
708
{
709
/* Find prefix of text for which the advance of each character accounts
710
* for the position offset */
711
x = it->x;
712
while (j < text->len)
713
{
714
FT_Fixed adv;
715
FT_Get_Advance(text->font->ft_face, text->items[j-1].gid, mask, &adv);
716
x += (float)adv * size /((FT_Face)text->font->ft_face)->units_per_EM;
717
if (fabs(x - text->items[j].x) > ALLOWED_TEXT_POS_ERROR || fabs(it->y - text->items[j].y) > ALLOWED_TEXT_POS_ERROR)
718
break;
719
j++;
720
}
721
}
722
723
fz_buffer_printf(ctx, gs->buf, "<");
724
for (/* i from its current value */; i < j; i++)
725
{
726
/* FIXME: should use it->gid, rather than it->ucs, and convert
727
* to the correct encoding */
728
fz_buffer_printf(ctx, gs->buf, "%02x", text->items[i].ucs);
729
}
730
fz_buffer_printf(ctx, gs->buf, "> Tj\n");
731
}
732
gs->tm.e = trm.e;
733
gs->tm.f = trm.f;
734
}
735
736
static void
737
pdf_dev_trm(fz_context *ctx, pdf_device *pdev, int trm)
738
{
739
gstate *gs = CURRENT_GSTATE(pdev);
740
741
if (gs->text_rendering_mode == trm)
742
return;
743
gs->text_rendering_mode = trm;
744
fz_buffer_printf(ctx, gs->buf, "%d Tr\n", trm);
745
}
746
747
static void
748
pdf_dev_begin_text(fz_context *ctx, pdf_device *pdev, const fz_matrix *tm, int trm)
749
{
750
pdf_dev_trm(ctx, pdev, trm);
751
if (!pdev->in_text)
752
{
753
gstate *gs = CURRENT_GSTATE(pdev);
754
fz_buffer_printf(ctx, gs->buf, "BT\n");
755
gs->tm.a = 1;
756
gs->tm.b = 0;
757
gs->tm.c = 0;
758
gs->tm.d = 1;
759
gs->tm.e = 0;
760
gs->tm.f = 0;
761
pdev->in_text = 1;
762
}
763
pdf_dev_tm(ctx, pdev, tm);
764
}
765
766
static void
767
pdf_dev_end_text(fz_context *ctx, pdf_device *pdev)
768
{
769
gstate *gs = CURRENT_GSTATE(pdev);
770
771
if (!pdev->in_text)
772
return;
773
pdev->in_text = 0;
774
fz_buffer_printf(ctx, gs->buf, "ET\n");
775
}
776
777
static int
778
pdf_dev_new_form(fz_context *ctx, pdf_obj **form_ref, pdf_device *pdev, const fz_rect *bbox, int isolated, int knockout, float alpha, fz_colorspace *colorspace)
779
{
780
pdf_document *doc = pdev->doc;
781
int num;
782
pdf_obj *group_ref;
783
pdf_obj *group;
784
pdf_obj *form;
785
786
*form_ref = NULL;
787
788
/* Find (or make) a new group with the required options. */
789
for(num = 0; num < pdev->num_groups; num++)
790
{
791
group_entry *g = &pdev->groups[num];
792
if (g->isolated == isolated && g->knockout == knockout && g->alpha == alpha && g->colorspace == colorspace)
793
{
794
group_ref = pdev->groups[num].ref;
795
break;
796
}
797
}
798
799
/* If we didn't find one, make one */
800
if (num == pdev->num_groups)
801
{
802
if (pdev->num_groups == pdev->max_groups)
803
{
804
int newmax = pdev->max_groups * 2;
805
if (newmax == 0)
806
newmax = 4;
807
pdev->groups = fz_resize_array(ctx, pdev->groups, newmax, sizeof(*pdev->groups));
808
pdev->max_groups = newmax;
809
}
810
pdev->num_groups++;
811
pdev->groups[num].isolated = isolated;
812
pdev->groups[num].knockout = knockout;
813
pdev->groups[num].alpha = alpha;
814
pdev->groups[num].colorspace = fz_keep_colorspace(ctx, colorspace);
815
pdev->groups[num].ref = NULL;
816
group = pdf_new_dict(ctx, doc, 5);
817
fz_try(ctx)
818
{
819
pdf_dict_put_drop(ctx, group, PDF_NAME_Type, PDF_NAME_Group);
820
pdf_dict_put_drop(ctx, group, PDF_NAME_S, PDF_NAME_Transparency);
821
pdf_dict_put_drop(ctx, group, PDF_NAME_K, pdf_new_bool(ctx, doc, knockout));
822
pdf_dict_put_drop(ctx, group, PDF_NAME_I, pdf_new_bool(ctx, doc, isolated));
823
if (!colorspace)
824
{}
825
else if (colorspace->n == 1)
826
pdf_dict_put_drop(ctx, group, PDF_NAME_CS, PDF_NAME_DeviceGray);
827
else if (colorspace->n == 4)
828
pdf_dict_put_drop(ctx, group, PDF_NAME_CS, PDF_NAME_DeviceCMYK);
829
else
830
pdf_dict_put_drop(ctx, group, PDF_NAME_CS, PDF_NAME_DeviceRGB);
831
group_ref = pdev->groups[num].ref = pdf_new_ref(ctx, doc, group);
832
}
833
fz_always(ctx)
834
{
835
pdf_drop_obj(ctx, group);
836
}
837
fz_catch(ctx)
838
{
839
fz_rethrow(ctx);
840
}
841
}
842
843
/* Make us a new Forms object that points to that group, and change
844
* to writing into the buffer for that Forms object. */
845
form = pdf_new_dict(ctx, doc, 4);
846
fz_try(ctx)
847
{
848
pdf_dict_put_drop(ctx, form, PDF_NAME_Subtype, PDF_NAME_Form);
849
pdf_dict_put(ctx, form, PDF_NAME_Group, group_ref);
850
pdf_dict_put_drop(ctx, form, PDF_NAME_FormType, pdf_new_int(ctx, doc, 1));
851
pdf_dict_put_drop(ctx, form, PDF_NAME_BBox, pdf_new_rect(ctx, doc, bbox));
852
*form_ref = pdf_new_ref(ctx, doc, form);
853
}
854
fz_catch(ctx)
855
{
856
pdf_drop_obj(ctx, form);
857
fz_rethrow(ctx);
858
}
859
860
/* Insert the new form object into the resources */
861
{
862
char text[32];
863
num = pdev->num_forms++;
864
snprintf(text, sizeof(text), "XObject/Fm%d", num);
865
pdf_dict_putp(ctx, pdev->resources, text, *form_ref);
866
}
867
868
return num;
869
}
870
871
/* Entry points */
872
873
static void
874
pdf_dev_fill_path(fz_context *ctx, fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm,
875
fz_colorspace *colorspace, float *color, float alpha)
876
{
877
pdf_device *pdev = (pdf_device*)dev;
878
gstate *gs = CURRENT_GSTATE(pdev);
879
880
pdf_dev_end_text(ctx, pdev);
881
pdf_dev_alpha(ctx, pdev, alpha, 0);
882
pdf_dev_color(ctx, pdev, colorspace, color, 0);
883
pdf_dev_ctm(ctx, pdev, ctm);
884
pdf_dev_path(ctx, pdev, path);
885
fz_buffer_printf(ctx, gs->buf, (even_odd ? "f*\n" : "f\n"));
886
}
887
888
static void
889
pdf_dev_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm,
890
fz_colorspace *colorspace, float *color, float alpha)
891
{
892
pdf_device *pdev = (pdf_device*)dev;
893
gstate *gs = CURRENT_GSTATE(pdev);
894
895
pdf_dev_end_text(ctx, pdev);
896
pdf_dev_alpha(ctx, pdev, alpha, 1);
897
pdf_dev_color(ctx, pdev, colorspace, color, 1);
898
pdf_dev_ctm(ctx, pdev, ctm);
899
pdf_dev_stroke_state(ctx, pdev, stroke);
900
pdf_dev_path(ctx, pdev, path);
901
fz_buffer_printf(ctx, gs->buf, "S\n");
902
}
903
904
static void
905
pdf_dev_clip_path(fz_context *ctx, fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm)
906
{
907
pdf_device *pdev = (pdf_device*)dev;
908
gstate *gs;
909
910
pdf_dev_end_text(ctx, pdev);
911
pdf_dev_push(ctx, pdev);
912
pdf_dev_ctm(ctx, pdev, ctm);
913
pdf_dev_path(ctx, pdev, path);
914
gs = CURRENT_GSTATE(pdev);
915
fz_buffer_printf(ctx, gs->buf, (even_odd ? "W* n\n" : "W n\n"));
916
}
917
918
static void
919
pdf_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm)
920
{
921
pdf_device *pdev = (pdf_device*)dev;
922
gstate *gs;
923
924
pdf_dev_end_text(ctx, pdev);
925
pdf_dev_push(ctx, pdev);
926
/* FIXME: Need to push a group, select a pattern (or shading) here,
927
* stroke with the pattern/shading. Then move to defining that pattern
928
* with the next calls to the device interface until the next pop
929
* when we pop the group. */
930
pdf_dev_ctm(ctx, pdev, ctm);
931
pdf_dev_path(ctx, pdev, path);
932
gs = CURRENT_GSTATE(pdev);
933
fz_buffer_printf(ctx, gs->buf, "W n\n");
934
}
935
936
static void
937
pdf_dev_fill_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm,
938
fz_colorspace *colorspace, float *color, float alpha)
939
{
940
pdf_device *pdev = (pdf_device*)dev;
941
fz_matrix trm = text->trm;
942
float size = fz_matrix_expansion(&trm);
943
944
fz_pre_scale(&trm, 1/size, 1/size);
945
946
pdf_dev_begin_text(ctx, pdev, &trm, 0);
947
pdf_dev_font(ctx, pdev, text->font, size);
948
pdf_dev_ctm(ctx, pdev, ctm);
949
pdf_dev_alpha(ctx, pdev, alpha, 0);
950
pdf_dev_color(ctx, pdev, colorspace, color, 0);
951
pdf_dev_text(ctx, pdev, text, size);
952
}
953
954
static void
955
pdf_dev_stroke_text(fz_context *ctx, fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm,
956
fz_colorspace *colorspace, float *color, float alpha)
957
{
958
pdf_device *pdev = (pdf_device*)dev;
959
fz_matrix trm = text->trm;
960
float size = fz_matrix_expansion(&trm);
961
962
fz_pre_scale(&trm, 1/size, 1/size);
963
964
pdf_dev_begin_text(ctx, pdev, &text->trm, 1);
965
pdf_dev_font(ctx, pdev, text->font, 1);
966
pdf_dev_ctm(ctx, pdev, ctm);
967
pdf_dev_alpha(ctx, pdev, alpha, 1);
968
pdf_dev_color(ctx, pdev, colorspace, color, 1);
969
pdf_dev_text(ctx, pdev, text, size);
970
}
971
972
static void
973
pdf_dev_clip_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate)
974
{
975
pdf_device *pdev = (pdf_device*)dev;
976
fz_matrix trm = text->trm;
977
float size = fz_matrix_expansion(&trm);
978
979
fz_pre_scale(&trm, 1/size, 1/size);
980
981
pdf_dev_begin_text(ctx, pdev, &text->trm, 0);
982
pdf_dev_ctm(ctx, pdev, ctm);
983
pdf_dev_font(ctx, pdev, text->font, 7);
984
pdf_dev_text(ctx, pdev, text, size);
985
}
986
987
static void
988
pdf_dev_clip_stroke_text(fz_context *ctx, fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm)
989
{
990
pdf_device *pdev = (pdf_device*)dev;
991
fz_matrix trm = text->trm;
992
float size = fz_matrix_expansion(&trm);
993
994
fz_pre_scale(&trm, 1/size, 1/size);
995
996
pdf_dev_begin_text(ctx, pdev, &text->trm, 0);
997
pdf_dev_font(ctx, pdev, text->font, 5);
998
pdf_dev_ctm(ctx, pdev, ctm);
999
pdf_dev_text(ctx, pdev, text, size);
1000
}
1001
1002
static void
1003
pdf_dev_ignore_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm)
1004
{
1005
pdf_device *pdev = (pdf_device*)dev;
1006
fz_matrix trm = text->trm;
1007
float size = fz_matrix_expansion(&trm);
1008
1009
fz_pre_scale(&trm, 1/size, 1/size);
1010
1011
pdf_dev_begin_text(ctx, pdev, &text->trm, 0);
1012
pdf_dev_ctm(ctx, pdev, ctm);
1013
pdf_dev_font(ctx, pdev, text->font, 3);
1014
pdf_dev_text(ctx, pdev, text, size);
1015
}
1016
1017
static void
1018
pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha)
1019
{
1020
pdf_device *pdev = (pdf_device*)dev;
1021
int num;
1022
gstate *gs = CURRENT_GSTATE(pdev);
1023
fz_matrix local_ctm = *ctm;
1024
1025
pdf_dev_end_text(ctx, pdev);
1026
num = send_image(ctx, pdev, image, 0, 0);
1027
pdf_dev_alpha(ctx, pdev, alpha, 0);
1028
/* PDF images are upside down, so fiddle the ctm */
1029
fz_pre_scale(&local_ctm, 1, -1);
1030
fz_pre_translate(&local_ctm, 0, -1);
1031
pdf_dev_ctm(ctx, pdev, &local_ctm);
1032
fz_buffer_printf(ctx, gs->buf, "/Img%d Do\n", num);
1033
}
1034
1035
static void
1036
pdf_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha)
1037
{
1038
pdf_device *pdev = (pdf_device*)dev;
1039
1040
/* FIXME */
1041
pdf_dev_end_text(ctx, pdev);
1042
}
1043
1044
static void
1045
pdf_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm,
1046
fz_colorspace *colorspace, float *color, float alpha)
1047
{
1048
pdf_device *pdev = (pdf_device*)dev;
1049
gstate *gs = CURRENT_GSTATE(pdev);
1050
int num;
1051
fz_matrix local_ctm = *ctm;
1052
1053
pdf_dev_end_text(ctx, pdev);
1054
num = send_image(ctx, pdev, image, 1, 0);
1055
fz_buffer_printf(ctx, gs->buf, "q\n");
1056
pdf_dev_alpha(ctx, pdev, alpha, 0);
1057
pdf_dev_color(ctx, pdev, colorspace, color, 0);
1058
/* PDF images are upside down, so fiddle the ctm */
1059
fz_pre_scale(&local_ctm, 1, -1);
1060
fz_pre_translate(&local_ctm, 0, -1);
1061
pdf_dev_ctm(ctx, pdev, &local_ctm);
1062
fz_buffer_printf(ctx, gs->buf, "/Img%d Do Q\n", num);
1063
}
1064
1065
static void
1066
pdf_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm)
1067
{
1068
pdf_device *pdev = (pdf_device*)dev;
1069
1070
/* FIXME */
1071
pdf_dev_end_text(ctx, pdev);
1072
pdf_dev_push(ctx, pdev);
1073
}
1074
1075
static void
1076
pdf_dev_pop_clip(fz_context *ctx, fz_device *dev)
1077
{
1078
pdf_device *pdev = (pdf_device*)dev;
1079
1080
/* FIXME */
1081
pdf_dev_end_text(ctx, pdev);
1082
pdf_dev_pop(ctx, pdev);
1083
}
1084
1085
static void
1086
pdf_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, float *color)
1087
{
1088
pdf_device *pdev = (pdf_device*)dev;
1089
pdf_document *doc = pdev->doc;
1090
gstate *gs;
1091
pdf_obj *smask = NULL;
1092
pdf_obj *egs = NULL;
1093
pdf_obj *egs_ref;
1094
pdf_obj *form_ref;
1095
pdf_obj *color_obj = NULL;
1096
int i;
1097
1098
fz_var(smask);
1099
fz_var(egs);
1100
fz_var(color_obj);
1101
1102
pdf_dev_end_text(ctx, pdev);
1103
1104
/* Make a new form to contain the contents of the softmask */
1105
pdf_dev_new_form(ctx, &form_ref, pdev, bbox, 0, 0, 1, colorspace);
1106
1107
fz_try(ctx)
1108
{
1109
smask = pdf_new_dict(ctx, doc, 4);
1110
pdf_dict_put_drop(ctx, smask, PDF_NAME_Type, PDF_NAME_Mask);
1111
pdf_dict_put_drop(ctx, smask, PDF_NAME_S, (luminosity ? PDF_NAME_Luminosity : PDF_NAME_Alpha));
1112
pdf_dict_put(ctx, smask, PDF_NAME_G, form_ref);
1113
color_obj = pdf_new_array(ctx, doc, colorspace->n);
1114
for (i = 0; i < colorspace->n; i++)
1115
pdf_array_push(ctx, color_obj, pdf_new_real(ctx, doc, color[i]));
1116
pdf_dict_put_drop(ctx, smask, PDF_NAME_BC, color_obj);
1117
color_obj = NULL;
1118
1119
egs = pdf_new_dict(ctx, doc, 5);
1120
pdf_dict_put_drop(ctx, egs, PDF_NAME_Type, PDF_NAME_ExtGState);
1121
pdf_dict_put_drop(ctx, egs, PDF_NAME_SMask, pdf_new_ref(ctx, doc, smask));
1122
egs_ref = pdf_new_ref(ctx, doc, egs);
1123
1124
{
1125
char text[32];
1126
snprintf(text, sizeof(text), "ExtGState/SM%d", pdev->num_smasks++);
1127
pdf_dict_putp(ctx, pdev->resources, text, egs_ref);
1128
pdf_drop_obj(ctx, egs_ref);
1129
}
1130
gs = CURRENT_GSTATE(pdev);
1131
fz_buffer_printf(ctx, gs->buf, "/SM%d gs\n", pdev->num_smasks-1);
1132
}
1133
fz_always(ctx)
1134
{
1135
pdf_drop_obj(ctx, smask);
1136
}
1137
fz_catch(ctx)
1138
{
1139
pdf_drop_obj(ctx, form_ref);
1140
pdf_drop_obj(ctx, color_obj);
1141
fz_rethrow(ctx);
1142
}
1143
1144
/* Now, everything we get until the end_mask needs to go into a
1145
* new buffer, which will be the stream contents for the form. */
1146
pdf_dev_push_new_buf(ctx, pdev, fz_new_buffer(ctx, 1024), NULL, form_ref);
1147
}
1148
1149
static void
1150
pdf_dev_end_mask(fz_context *ctx, fz_device *dev)
1151
{
1152
pdf_device *pdev = (pdf_device*)dev;
1153
pdf_document *doc = pdev->doc;
1154
gstate *gs = CURRENT_GSTATE(pdev);
1155
fz_buffer *buf = fz_keep_buffer(ctx, gs->buf);
1156
pdf_obj *form_ref = (pdf_obj *)gs->on_pop_arg;
1157
1158
/* Here we do part of the pop, but not all of it. */
1159
pdf_dev_end_text(ctx, pdev);
1160
fz_buffer_printf(ctx, buf, "Q\n");
1161
pdf_update_stream(ctx, doc, form_ref, buf, 0);
1162
fz_drop_buffer(ctx, buf);
1163
gs->buf = fz_keep_buffer(ctx, gs[-1].buf);
1164
gs->on_pop_arg = NULL;
1165
pdf_drop_obj(ctx, form_ref);
1166
fz_buffer_printf(ctx, gs->buf, "q\n");
1167
}
1168
1169
static void
1170
pdf_dev_begin_group(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int isolated, int knockout, int blendmode, float alpha)
1171
{
1172
pdf_device *pdev = (pdf_device*)dev;
1173
pdf_document *doc = pdev->doc;
1174
int num;
1175
pdf_obj *form_ref;
1176
gstate *gs;
1177
1178
pdf_dev_end_text(ctx, pdev);
1179
1180
num = pdf_dev_new_form(ctx, &form_ref, pdev, bbox, isolated, knockout, alpha, NULL);
1181
1182
/* Do we have an appropriate blending extgstate already? */
1183
{
1184
char text[32];
1185
pdf_obj *obj;
1186
snprintf(text, sizeof(text), "ExtGState/BlendMode%d", blendmode);
1187
obj = pdf_dict_getp(ctx, pdev->resources, text);
1188
if (obj == NULL)
1189
{
1190
/* No, better make one */
1191
obj = pdf_new_dict(ctx, doc, 2);
1192
pdf_dict_put_drop(ctx, obj, PDF_NAME_Type, PDF_NAME_ExtGState);
1193
pdf_dict_put_drop(ctx, obj, PDF_NAME_BM, pdf_new_name(ctx, doc, fz_blendmode_name(blendmode)));
1194
pdf_dict_putp_drop(ctx, pdev->resources, text, obj);
1195
}
1196
}
1197
1198
/* Add the call to this group */
1199
gs = CURRENT_GSTATE(pdev);
1200
fz_buffer_printf(ctx, gs->buf, "/BlendMode%d gs /Fm%d Do\n", blendmode, num);
1201
1202
/* Now, everything we get until the end of group needs to go into a
1203
* new buffer, which will be the stream contents for the form. */
1204
pdf_dev_push_new_buf(ctx, pdev, fz_new_buffer(ctx, 1024), NULL, form_ref);
1205
}
1206
1207
static void
1208
pdf_dev_end_group(fz_context *ctx, fz_device *dev)
1209
{
1210
pdf_device *pdev = (pdf_device*)dev;
1211
pdf_document *doc = pdev->doc;
1212
gstate *gs = CURRENT_GSTATE(pdev);
1213
fz_buffer *buf = fz_keep_buffer(ctx, gs->buf);
1214
pdf_obj *form_ref;
1215
1216
pdf_dev_end_text(ctx, pdev);
1217
form_ref = (pdf_obj *)pdf_dev_pop(ctx, pdev);
1218
pdf_update_stream(ctx, doc, form_ref, buf, 0);
1219
fz_drop_buffer(ctx, buf);
1220
pdf_drop_obj(ctx, form_ref);
1221
}
1222
1223
static int
1224
pdf_dev_begin_tile(fz_context *ctx, fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id)
1225
{
1226
pdf_device *pdev = (pdf_device*)dev;
1227
1228
/* FIXME */
1229
pdf_dev_end_text(ctx, pdev);
1230
return 0;
1231
}
1232
1233
static void
1234
pdf_dev_end_tile(fz_context *ctx, fz_device *dev)
1235
{
1236
pdf_device *pdev = (pdf_device*)dev;
1237
1238
/* FIXME */
1239
pdf_dev_end_text(ctx, pdev);
1240
}
1241
1242
static void
1243
pdf_dev_drop_imp(fz_context *ctx, fz_device *dev)
1244
{
1245
pdf_device *pdev = (pdf_device*)dev;
1246
pdf_document *doc = pdev->doc;
1247
int i;
1248
1249
pdf_dev_end_text(ctx, pdev);
1250
1251
for (i = pdev->num_gstates-1; i >= 0; i--)
1252
{
1253
fz_drop_stroke_state(ctx, pdev->gstates[i].stroke_state);
1254
}
1255
1256
for (i = pdev->num_fonts-1; i >= 0; i--)
1257
{
1258
fz_drop_font(ctx, pdev->fonts[i].font);
1259
}
1260
1261
for (i = pdev->num_imgs-1; i >= 0; i--)
1262
{
1263
pdf_drop_obj(ctx, pdev->images[i].ref);
1264
}
1265
1266
if (pdev->contents)
1267
{
1268
pdf_update_stream(ctx, doc, pdev->contents, pdev->gstates[0].buf, 0);
1269
pdf_drop_obj(ctx, pdev->contents);
1270
}
1271
1272
if (pdev->buffer != pdev->gstates[0].buf)
1273
{
1274
fz_drop_buffer(ctx, pdev->gstates[0].buf);
1275
}
1276
1277
pdf_drop_obj(ctx, pdev->resources);
1278
1279
fz_free(ctx, pdev->images);
1280
fz_free(ctx, pdev->alphas);
1281
fz_free(ctx, pdev->gstates);
1282
}
1283
1284
fz_device *pdf_new_pdf_device(fz_context *ctx, pdf_document *doc, pdf_obj *contents, pdf_obj *resources, const fz_matrix *ctm, fz_buffer *buf)
1285
{
1286
pdf_device *dev = fz_new_device(ctx, sizeof *dev);
1287
1288
dev->super.drop_imp = pdf_dev_drop_imp;
1289
1290
dev->super.fill_path = pdf_dev_fill_path;
1291
dev->super.stroke_path = pdf_dev_stroke_path;
1292
dev->super.clip_path = pdf_dev_clip_path;
1293
dev->super.clip_stroke_path = pdf_dev_clip_stroke_path;
1294
1295
dev->super.fill_text = pdf_dev_fill_text;
1296
dev->super.stroke_text = pdf_dev_stroke_text;
1297
dev->super.clip_text = pdf_dev_clip_text;
1298
dev->super.clip_stroke_text = pdf_dev_clip_stroke_text;
1299
dev->super.ignore_text = pdf_dev_ignore_text;
1300
1301
dev->super.fill_shade = pdf_dev_fill_shade;
1302
dev->super.fill_image = pdf_dev_fill_image;
1303
dev->super.fill_image_mask = pdf_dev_fill_image_mask;
1304
dev->super.clip_image_mask = pdf_dev_clip_image_mask;
1305
1306
dev->super.pop_clip = pdf_dev_pop_clip;
1307
1308
dev->super.begin_mask = pdf_dev_begin_mask;
1309
dev->super.end_mask = pdf_dev_end_mask;
1310
dev->super.begin_group = pdf_dev_begin_group;
1311
dev->super.end_group = pdf_dev_end_group;
1312
1313
dev->super.begin_tile = pdf_dev_begin_tile;
1314
dev->super.end_tile = pdf_dev_end_tile;
1315
1316
fz_try(ctx)
1317
{
1318
dev->buffer = fz_keep_buffer(ctx, buf);
1319
if (!buf)
1320
buf = fz_new_buffer(ctx, 256);
1321
dev->doc = doc;
1322
dev->contents = pdf_keep_obj(ctx, contents);
1323
dev->resources = pdf_keep_obj(ctx, resources);
1324
dev->gstates = fz_malloc_struct(ctx, gstate);
1325
dev->gstates[0].buf = buf;
1326
dev->gstates[0].ctm = *ctm;
1327
dev->gstates[0].colorspace[0] = fz_device_gray(ctx);
1328
dev->gstates[0].colorspace[1] = fz_device_gray(ctx);
1329
dev->gstates[0].color[0][0] = 1;
1330
dev->gstates[0].color[1][0] = 1;
1331
dev->gstates[0].alpha[0] = 1.0;
1332
dev->gstates[0].alpha[1] = 1.0;
1333
dev->gstates[0].font = -1;
1334
dev->gstates[0].horizontal_scaling = 100;
1335
dev->num_gstates = 1;
1336
dev->max_gstates = 1;
1337
}
1338
fz_catch(ctx)
1339
{
1340
if (dev->gstates && dev->buffer == NULL)
1341
fz_drop_buffer(ctx, dev->gstates[0].buf);
1342
fz_free(ctx, dev);
1343
fz_rethrow(ctx);
1344
}
1345
1346
return (fz_device*)dev;
1347
}
1348
1349
fz_device *pdf_page_write(fz_context *ctx, pdf_document *doc, pdf_page *page)
1350
{
1351
pdf_obj *resources = pdf_dict_get(ctx, page->me, PDF_NAME_Resources);
1352
fz_matrix ctm;
1353
pdf_obj *obj;
1354
1355
fz_pre_translate(fz_scale(&ctm, 1, -1), 0, page->mediabox.y0-page->mediabox.y1);
1356
1357
if (resources == NULL)
1358
{
1359
resources = pdf_new_dict(ctx, doc, 0);
1360
pdf_dict_put_drop(ctx, page->me, PDF_NAME_Resources, resources);
1361
}
1362
1363
/* We always make a new object for page->contents here, in case
1364
* the existing one is an array, or is shared. */
1365
obj = pdf_new_dict(ctx, doc, 0);
1366
fz_try(ctx)
1367
{
1368
pdf_obj *new_contents = pdf_new_ref(ctx, doc, obj);
1369
pdf_dict_put(ctx, page->me, PDF_NAME_Contents, new_contents);
1370
pdf_drop_obj(ctx, page->contents);
1371
page->contents = new_contents;
1372
}
1373
fz_always(ctx)
1374
{
1375
pdf_drop_obj(ctx, obj);
1376
}
1377
fz_catch(ctx)
1378
{
1379
fz_rethrow(ctx);
1380
}
1381
1382
return pdf_new_pdf_device(ctx, doc, page->contents, resources, &ctm, NULL);
1383
}
1384
1385