bugfixes
[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 <memory.h>
26 #include <pdflib.h>
27 #include <math.h>
28 #include "../os.h"
29 #include "../jpeg.h"
30 #include "../types.h"
31 #include "../mem.h"
32 #include "../gfxdevice.h"
33 #include "../gfxtools.h"
34
35 typedef struct _internal {
36     PDF* p;
37     char*tempfile;
38 } internal_t;
39
40 int pdf_setparameter(gfxdevice_t*dev, const char*key, const char*value)
41 {
42     internal_t*i = (internal_t*)dev->internal;
43     return 0;
44 }
45
46 void pdf_startpage(gfxdevice_t*dev, int width, int height)
47 {
48     internal_t*i = (internal_t*)dev->internal;
49
50     if(!i->tempfile) {
51         i->tempfile = strdup(mktempname(0));
52         PDF_open_file(i->p, i->tempfile);
53         PDF_set_parameter(i->p, "usercoordinates", "true");
54         PDF_set_parameter(i->p, "topdown", "true");
55     }
56
57     PDF_begin_page(i->p, width, height);
58     PDF_set_parameter(i->p, "fillrule", "evenodd");
59 }
60
61 static int mkline(gfxline_t*line, PDF*p)
62 {
63     int ret = 0;
64     double x=0,y=0;
65     while(line) {
66         if(line->type == gfx_moveTo) {
67             PDF_moveto(p, line->x, line->y);
68         } else if(line->type == gfx_lineTo) {
69             PDF_lineto(p, line->x, line->y);
70             ret = 1;
71         } else {
72             /* when converting a quadratic bezier to a cubic bezier, the
73                two new control points are both 2/3 the way from the
74                endpoints to the old control point */
75             double c1x = (x + line->sx*2)/3;
76             double c1y = (y + line->sy*2)/3;
77             double c2x = (line->x + line->sx*2)/3;
78             double c2y = (line->y + line->sy*2)/3;
79             PDF_curveto(p, c1x, c1y, c2x, c2y, line->x, line->y);
80             ret = 1;
81         }
82         x = line->x;
83         y = line->y;
84         line = line->next;
85     }
86     return ret;
87 }
88
89 void pdf_startclip(gfxdevice_t*dev, gfxline_t*line)
90 {
91     internal_t*i = (internal_t*)dev->internal;
92     PDF_save(i->p);
93     PDF_set_parameter(i->p, "fillrule", "evenodd");
94     if(mkline(line, i->p))
95         PDF_clip(i->p);
96     else   
97         ; // TODO: strictly speaking, an empty clip clears everything
98
99 }
100 void pdf_endclip(gfxdevice_t*dev)
101 {
102     internal_t*i = (internal_t*)dev->internal;
103     PDF_restore(i->p);
104 }
105 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)
106 {
107     internal_t*i = (internal_t*)dev->internal;
108     PDF_setlinewidth(i->p, width);
109     PDF_setlinecap(i->p, cap_style==gfx_capButt?0:(cap_style==gfx_capRound?1:2));
110     PDF_setlinejoin(i->p, joint_style==gfx_joinMiter?0:(joint_style==gfx_joinRound?1:2));
111     PDF_setrgbcolor_stroke(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
112     if(joint_style==gfx_joinMiter)
113         PDF_setmiterlimit(i->p, miterLimit);
114     if(mkline(line, i->p))
115         PDF_stroke(i->p);
116 }
117
118 void pdf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
119 {
120     internal_t*i = (internal_t*)dev->internal;
121     PDF_setrgbcolor_fill(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
122     PDF_set_parameter(i->p, "fillrule", "evenodd");
123         
124     if(mkline(line, i->p)) {
125         PDF_fill(i->p);
126     }
127 }
128
129 void pdf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
130 {
131     internal_t*i = (internal_t*)dev->internal;
132
133     double l1 = sqrt(matrix->m00*matrix->m00+matrix->m01*matrix->m01)*img->width;
134     double l2 = sqrt(matrix->m10*matrix->m10+matrix->m11*matrix->m11)*img->height;
135     double r = atan2(matrix->m01, matrix->m00);
136
137     /* fit_image needs the lower left corner of the image */
138     double x = matrix->tx + matrix->m10*img->height;
139     double y = matrix->ty + matrix->m11*img->height;
140
141     char*tempfile = mktempname(0);
142     char options[80];
143     sprintf(options, "boxsize {%f %f} fitmethod meet rotate %f", l1, l2, r*180/M_PI);
144     gfximage_save_jpeg(img, tempfile, 99);
145     int imgid = PDF_load_image(i->p, "jpeg", tempfile, 0, "");
146     PDF_fit_image(i->p, imgid, x, y, options);
147 }
148
149 void pdf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
150 {
151     internal_t*i = (internal_t*)dev->internal;
152 }
153
154 void pdf_addfont(gfxdevice_t*dev, gfxfont_t*font)
155 {
156     internal_t*i = (internal_t*)dev->internal;
157 }
158
159 void pdf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
160 {
161     internal_t*i = (internal_t*)dev->internal;
162     if(!font)
163         return;
164     /* align characters to whole pixels */
165     matrix->tx = (int)matrix->tx;
166     matrix->ty = (int)matrix->ty;
167
168     gfxglyph_t*glyph = &font->glyphs[glyphnr];
169     gfxline_t*line2 = gfxline_clone(glyph->line);
170     gfxline_transform(line2, matrix);
171     PDF_setrgbcolor_fill(i->p, color->r/255.0, color->g/255.0, color->b/255.0);
172     PDF_set_parameter(i->p, "fillrule", "evenodd");
173     
174     if(mkline(line2, i->p)) {
175         PDF_fill(i->p);
176     }
177     gfxline_free(line2);
178     return;
179 }
180
181 void pdf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
182 {
183     internal_t*i = (internal_t*)dev->internal;
184 }
185
186 void pdf_endpage(gfxdevice_t*dev)
187 {
188     internal_t*i = (internal_t*)dev->internal;
189     PDF_end_page(i->p);
190 }
191
192 typedef struct pdfresult_internal {
193     char*tempfile;
194 } pdfresult_internal_t;
195
196 void pdfresult_destroy(gfxresult_t*gfx)
197 {
198     pdfresult_internal_t*i = (pdfresult_internal_t*)gfx->internal;
199     free(i->tempfile);
200     free(gfx->internal);gfx->internal = 0;
201     free(gfx);
202 }
203
204 int pdfresult_save(gfxresult_t*gfx, const char*filename)
205 {
206     pdfresult_internal_t*i = (pdfresult_internal_t*)gfx->internal;
207     FILE*fi = fopen(i->tempfile, "rb");
208     FILE*fo = fopen(filename, "wb");
209     if(!fo) {
210         perror(filename);
211         return -1;
212     }
213     char buffer[4096];
214     int size = 0;
215     while((size = fread(buffer, 1, 4096, fi))) {
216         fwrite(buffer, 1, size, fo);
217     }
218     fclose(fi);
219     fclose(fo);
220     return 0;
221 }
222
223 void* pdfresult_get(gfxresult_t*gfx, const char*name)
224 {
225     return 0; 
226 }
227
228 gfxresult_t* pdf_finish(gfxdevice_t*dev)
229 {
230     internal_t*i = (internal_t*)dev->internal;
231     
232     PDF_close(i->p);
233     PDF_delete(i->p);
234
235     gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
236     memset(result, 0, sizeof(gfxresult_t));
237     result->save = pdfresult_save;
238     result->get = pdfresult_get;
239     result->destroy = pdfresult_destroy;
240     result->internal = 0;
241     result->internal = malloc(sizeof(pdfresult_internal_t));
242     pdfresult_internal_t*ri = (pdfresult_internal_t*)result->internal;
243     ri->tempfile = i->tempfile;i->tempfile=0;
244     free(dev->internal);dev->internal = 0;i=0;
245     return result;
246 }
247
248 void gfxdevice_pdf_init(gfxdevice_t*dev)
249 {
250     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
251     memset(dev, 0, sizeof(gfxdevice_t));
252
253     dev->name = "pdf";
254
255     dev->internal = i;
256
257     dev->setparameter = pdf_setparameter;
258     dev->startpage = pdf_startpage;
259     dev->startclip = pdf_startclip;
260     dev->endclip = pdf_endclip;
261     dev->stroke = pdf_stroke;
262     dev->fill = pdf_fill;
263     dev->fillbitmap = pdf_fillbitmap;
264     dev->fillgradient = pdf_fillgradient;
265     dev->addfont = pdf_addfont;
266     dev->drawchar = pdf_drawchar;
267     dev->drawlink = pdf_drawlink;
268     dev->endpage = pdf_endpage;
269     dev->finish = pdf_finish;
270
271     i->p = PDF_new();
272 }
273