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