improved polygon conversion in pdf2pdf
[swftools.git] / lib / devices / pdf.c
1 /* pdf.c
2
3    Part of the swftools package.
4
5    Copyright (c) 2007 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <assert.h>
26 #include <memory.h>
27 #include <pdflib.h>
28 #include <math.h>
29 #include "../os.h"
30 #include "../q.h"
31 #include "../jpeg.h"
32 #include "../types.h"
33 #include "../mem.h"
34 #include "../gfxdevice.h"
35 #include "../gfxtools.h"
36
37 typedef struct _internal {
38     PDF* p;
39     char*tempfile;
40     gfxfontlist_t*fontlist;
41 } internal_t;
42
43 int pdf_setparameter(gfxdevice_t*dev, const char*key, const char*value)
44 {
45     internal_t*i = (internal_t*)dev->internal;
46     return 0;
47 }
48
49 void pdf_startpage(gfxdevice_t*dev, int width, int height)
50 {
51     internal_t*i = (internal_t*)dev->internal;
52
53     if(!i->tempfile) {
54         i->tempfile = strdup(mktempname(0));
55         PDF_open_file(i->p, i->tempfile);
56         PDF_set_parameter(i->p, "usercoordinates", "true");
57         PDF_set_parameter(i->p, "topdown", "true");
58     }
59
60     PDF_begin_page(i->p, width, height);
61     PDF_set_parameter(i->p, "fillrule", "evenodd");
62 }
63
64 static char xy_equals(void*c1, void*c2)
65 {
66     return !memcmp(c1, c2, sizeof(double)*2);
67 }
68 static unsigned int xy_hash(void*c)
69 {
70     return string_hash3(c, sizeof(double)*2);
71 }
72 static void* xy_clone(void*c)
73 {
74     void*n = malloc(sizeof(double)*2);
75     memcpy(n, c, sizeof(double)*2);
76     return n;
77 }
78 static void xy_destroy(void*c)
79 {
80     free(c);
81 }
82 static type_t xy_type = {
83     hash: (hash_func)xy_hash,
84     equals: (equals_func)xy_equals,
85     dup: (dup_func)xy_clone, // all signatures are static
86     free: (free_func)xy_destroy,
87 };
88
89 static int mkline(gfxline_t*line, PDF*p, char fill)
90 {
91     int ret = 0;
92
93     dict_t*start = dict_new2(&xy_type);
94     gfxline_t*l = line;
95     while(l) {
96         if(l->type == gfx_moveTo) {
97             double xy[2] = {l->x, l->y};
98             dict_put(start, xy, l);
99         }
100         l = l->next;
101     }
102
103     assert(!line || line->type == gfx_moveTo);
104     
105     double x=0,y=0;
106     double pos[2] = {0,0};
107     char first = 1;
108
109     while(dict_count(start)) {
110         gfxline_t*l = dict_lookup(start, pos);
111         if(!l) {
112             DICT_ITERATE_DATA(start, gfxline_t*, l2) {
113                 l = l2;
114                 break;
115             }
116             assert(l);
117             double xy[2] = {l->x,l->y};
118             char d = dict_del2(start,xy,l);
119             assert(d);
120         } else {
121             char d = dict_del2(start,pos,l);
122             assert(d);
123         }
124         while(l) {
125             if(l->type == gfx_moveTo && (x!=l->x || y!=l->y || first)) {
126                 first = 0;
127                 PDF_moveto(p, l->x, l->y);
128             } else if(l->type == gfx_lineTo) {
129                 PDF_lineto(p, l->x, l->y);
130                 ret = 1;
131             } else {
132                 /* when converting a quadratic bezier to a cubic bezier, the
133                    two new control points are both 2/3 the way from the
134                    endpoints to the old control point */
135                 double c1x = (x + l->sx*2)/3;
136                 double c1y = (y + l->sy*2)/3;
137                 double c2x = (l->x + l->sx*2)/3;
138                 double c2y = (l->y + l->sy*2)/3;
139                 PDF_curveto(p, c1x, c1y, c2x, c2y, l->x, l->y);
140                 ret = 1;
141             }
142             x = l->x;
143             y = l->y;
144             pos[0] = x;
145             pos[1] = y;
146             l = l->next;
147             if(l && l->type == gfx_moveTo) 
148                 break;
149         }
150     }
151     dict_destroy(start);
152     return ret;
153 }
154
155 void pdf_startclip(gfxdevice_t*dev, gfxline_t*line)
156 {
157     internal_t*i = (internal_t*)dev->internal;
158     PDF_save(i->p);
159     if(mkline(line, i->p, 1))
160         PDF_clip(i->p);
161     else   
162         ; // TODO: strictly speaking, an empty clip clears everything
163
164 }
165 void pdf_endclip(gfxdevice_t*dev)
166 {
167     internal_t*i = (internal_t*)dev->internal;
168     PDF_restore(i->p);
169 }
170 void pdf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
171 {
172     internal_t*i = (internal_t*)dev->internal;
173     if(width<1e-6)
174         return;
175     PDF_setlinewidth(i->p, width);
176     PDF_setlinecap(i->p, cap_style==gfx_capButt?0:(cap_style==gfx_capRound?1:2));
177     PDF_setlinejoin(i->p, joint_style==gfx_joinMiter?0:(joint_style==gfx_joinRound?1:2));
178     PDF_setrgbcolor_stroke(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
179     if(joint_style==gfx_joinMiter)
180         PDF_setmiterlimit(i->p, miterLimit);
181     if(mkline(line, i->p, 0))
182         PDF_stroke(i->p);
183 }
184
185 void pdf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
186 {
187     internal_t*i = (internal_t*)dev->internal;
188     PDF_setrgbcolor_fill(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
189         
190     if(mkline(line, i->p, 1)) {
191         PDF_fill(i->p);
192     }
193 }
194
195 void pdf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
196 {
197     internal_t*i = (internal_t*)dev->internal;
198
199     double l1 = sqrt(matrix->m00*matrix->m00+matrix->m01*matrix->m01)*img->width;
200     double l2 = sqrt(matrix->m10*matrix->m10+matrix->m11*matrix->m11)*img->height;
201     double r = atan2(matrix->m01, matrix->m00);
202
203     /* fit_image needs the lower left corner of the image */
204     double x = matrix->tx + matrix->m10*img->height;
205     double y = matrix->ty + matrix->m11*img->height;
206
207     char*tempfile = mktempname(0);
208     char options[80];
209     sprintf(options, "boxsize {%f %f} fitmethod meet rotate %f", l1, l2, r*180/M_PI);
210     gfximage_save_jpeg(img, tempfile, 99);
211     int imgid = PDF_load_image(i->p, "jpeg", tempfile, 0, "");
212     PDF_fit_image(i->p, imgid, x, y, options);
213 }
214
215 void pdf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
216 {
217     internal_t*i = (internal_t*)dev->internal;
218 }
219
220 static char type3 = 1;
221
222 void pdf_addfont(gfxdevice_t*dev, gfxfont_t*font)
223 {
224     internal_t*i = (internal_t*)dev->internal;
225
226     if(type3) {
227         int fontid = 0;
228         if(!gfxfontlist_hasfont(i->fontlist, font)) {
229
230             static int fontnr = 1;
231             char fontname[32];
232             sprintf(fontname, "font%d", fontnr++);
233             int l = strlen(fontname);
234             char fontname2[64];
235             int t;
236             for(t=0;t<l+1;t++) {
237                 fontname2[t*2+0] = fontname[t];
238                 fontname2[t*2+1] = 0;
239             }
240
241             PDF_begin_font(i->p, fontname2, l*2, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, "");
242             int num = font->num_glyphs<256-32?font->num_glyphs:256-32;
243             for(t=0;t<num;t++) {
244                 gfxglyph_t*g = &font->glyphs[t];
245                 gfxbbox_t bbox = gfxline_getbbox(g->line);
246                 char name[32];
247                 sprintf(name, "chr%d", t+32);
248                 PDF_encoding_set_char(i->p, fontname, t+32, name, 0);
249                 PDF_begin_glyph(i->p, name, g->advance, bbox.xmin, bbox.ymin, bbox.xmax, bbox.ymax);
250                 if(mkline(g->line, i->p, 1))
251                     PDF_fill(i->p);
252                 PDF_end_glyph(i->p);
253             }
254             PDF_end_font(i->p);
255             fontid = PDF_load_font(i->p, fontname2, l*2, fontname, "");
256             
257             i->fontlist = gfxfontlist_addfont2(i->fontlist, font, (void*)(ptroff_t)fontid);
258         }
259     }
260 }
261
262 void pdf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
263 {
264     internal_t*i = (internal_t*)dev->internal;
265
266     if(!font)
267         return;
268
269     gfxglyph_t*glyph = &font->glyphs[glyphnr];
270     PDF_setrgbcolor_fill(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
271     char as_shape = 0;
272     if(!type3) as_shape=1;
273     if(glyphnr>256-32) as_shape=1;
274     if(fabs(matrix->m00 + matrix->m11) > 0.01) as_shape=1;
275     if(fabs(fabs(matrix->m01) + fabs(matrix->m10)) > 0.01) as_shape=1;
276     if(fabs(matrix->m00) < 0.01) as_shape=1;
277
278     if(as_shape) {
279         gfxline_t*line2 = gfxline_clone(glyph->line);
280         gfxline_transform(line2, matrix);
281         if(mkline(line2, i->p, 1)) {
282             PDF_fill(i->p);
283         }
284         gfxline_free(line2);
285     } else {
286         assert(gfxfontlist_hasfont(i->fontlist, font));
287         int fontid = (int)(ptroff_t)gfxfontlist_getuserdata(i->fontlist, font->id);
288         PDF_setfont(i->p, fontid, matrix->m00);
289         char name[32];
290         sprintf(name, "%c\0", glyphnr+32);
291         PDF_show_xy2(i->p, name, strlen(name), matrix->tx, matrix->ty);
292     }
293     return;
294 }
295
296 void pdf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
297 {
298     internal_t*i = (internal_t*)dev->internal;
299 }
300
301 void pdf_endpage(gfxdevice_t*dev)
302 {
303     internal_t*i = (internal_t*)dev->internal;
304     PDF_end_page(i->p);
305 }
306
307 typedef struct pdfresult_internal {
308     char*tempfile;
309 } pdfresult_internal_t;
310
311 void pdfresult_destroy(gfxresult_t*gfx)
312 {
313     pdfresult_internal_t*i = (pdfresult_internal_t*)gfx->internal;
314     free(i->tempfile);
315     free(gfx->internal);gfx->internal = 0;
316     free(gfx);
317 }
318
319 int pdfresult_save(gfxresult_t*gfx, const char*filename)
320 {
321     pdfresult_internal_t*i = (pdfresult_internal_t*)gfx->internal;
322     FILE*fi = fopen(i->tempfile, "rb");
323     FILE*fo = fopen(filename, "wb");
324     if(!fo) {
325         perror(filename);
326         return -1;
327     }
328     char buffer[4096];
329     int size = 0;
330     while((size = fread(buffer, 1, 4096, fi))) {
331         fwrite(buffer, 1, size, fo);
332     }
333     fclose(fi);
334     fclose(fo);
335     return 0;
336 }
337
338 void* pdfresult_get(gfxresult_t*gfx, const char*name)
339 {
340     return 0; 
341 }
342
343 gfxresult_t* pdf_finish(gfxdevice_t*dev)
344 {
345     internal_t*i = (internal_t*)dev->internal;
346     
347     PDF_close(i->p);
348     PDF_delete(i->p);
349
350     gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
351     memset(result, 0, sizeof(gfxresult_t));
352     result->save = pdfresult_save;
353     result->get = pdfresult_get;
354     result->destroy = pdfresult_destroy;
355     result->internal = 0;
356     result->internal = malloc(sizeof(pdfresult_internal_t));
357     pdfresult_internal_t*ri = (pdfresult_internal_t*)result->internal;
358     ri->tempfile = i->tempfile;i->tempfile=0;
359     free(dev->internal);dev->internal = 0;i=0;
360     return result;
361 }
362
363 void gfxdevice_pdf_init(gfxdevice_t*dev)
364 {
365     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
366     memset(dev, 0, sizeof(gfxdevice_t));
367
368     dev->name = "pdf";
369
370     dev->internal = i;
371
372     dev->setparameter = pdf_setparameter;
373     dev->startpage = pdf_startpage;
374     dev->startclip = pdf_startclip;
375     dev->endclip = pdf_endclip;
376     dev->stroke = pdf_stroke;
377     dev->fill = pdf_fill;
378     dev->fillbitmap = pdf_fillbitmap;
379     dev->fillgradient = pdf_fillgradient;
380     dev->addfont = pdf_addfont;
381     dev->drawchar = pdf_drawchar;
382     dev->drawlink = pdf_drawlink;
383     dev->endpage = pdf_endpage;
384     dev->finish = pdf_finish;
385
386     i->p = PDF_new();
387 }
388