1 /*
2  *   PDF file output routines.
3  *
4  *   Copyright 2008 by Tobias Hoffmann.
5  *
6  *   This file is licensed as noted in "COPYING"
7  *   which should have been included with this file.
8  *
9  */
10 #include <stdio.h>
11 #include <assert.h>
12 #include <stdarg.h>
13 #include <memory.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include "pdfutils.h"
17 #include "fontembed/embed.h"
18 
pdfOut_printf(pdfOut * pdf,const char * fmt,...)19 void pdfOut_printf(pdfOut *pdf,const char *fmt,...) // {{{
20 {
21   assert(pdf);
22   int len;
23   va_list ap;
24 
25   va_start(ap,fmt);
26   len=vprintf(fmt,ap);
27   va_end(ap);
28   pdf->filepos+=len;
29 }
30 // }}}
31 
pdfOut_putString(pdfOut * pdf,const char * str,int len)32 void pdfOut_putString(pdfOut *pdf,const char *str,int len) // {{{ - >len==-1: strlen()
33 {
34   assert(pdf);
35   assert(str);
36   if (len==-1) {
37     len=strlen(str);
38   }
39   putc('(',stdout);
40   // escape special chars: \0 \\ \( \)  -- don't bother about balanced parens
41   int iA=0;
42   for (;len>0;iA++,len--) {
43     if ( (str[iA]<32)||(str[iA]>126) ) {
44       fwrite(str,1,iA,stdout);
45       fprintf(stdout,"\\%03o",(unsigned char)str[iA]);
46       pdf->filepos+=iA+4;
47       str+=iA+1;
48       iA=-1;
49     } else if ( (str[iA]=='(')||(str[iA]==')')||(str[iA]=='\\') ) {
50       fwrite(str,1,iA,stdout);
51       fprintf(stdout,"\\%c",str[iA]);
52       pdf->filepos+=iA+2;
53       str+=iA+1;
54       iA=-1;
55     }
56   }
57   pdf->filepos+=iA+2;
58   fwrite(str,1,iA,stdout);
59   putc(')',stdout);
60 }
61 // }}}
62 
pdfOut_putHexString(pdfOut * pdf,const char * str,int len)63 void pdfOut_putHexString(pdfOut *pdf,const char *str,int len) // {{{ - >len==-1: strlen()
64 {
65   assert(pdf);
66   assert(str);
67   if (len==-1) {
68     len=strlen(str);
69   }
70   pdf->filepos+=2*len+2;
71   putc('<',stdout);
72   for (;len>0;str++,len--) {
73     fprintf(stdout,"%02x",(unsigned char)*str);
74   }
75   putc('>',stdout);
76 }
77 // }}}
78 
pdfOut_new()79 pdfOut *pdfOut_new() // {{{ -  NULL on error
80 {
81   pdfOut *ret=malloc(sizeof(pdfOut));
82   if (ret) {
83     memset(ret,0,sizeof(pdfOut));
84   }
85 
86   return ret;
87 }
88 // }}}
89 
90 // NOTE: uses statically allocated buffer
pdfOut_to_pdfdate(struct tm * curtm)91 const char *pdfOut_to_pdfdate(struct tm *curtm) // {{{
92 {
93   static char curdate[250];
94   if (!curtm) {
95     time_t curtime;
96     curtime = time(NULL);
97     curtm   = localtime(&curtime);
98   }
99   strftime(curdate, sizeof(curdate), "D:%Y%m%d%H%M%S%z", curtm);
100   curdate[23]=0;
101   curdate[22]='\'';
102   curdate[21]=curdate[18];
103   curdate[20]=curdate[17];
104   curdate[19]='\'';
105   return curdate;
106 }
107 // }}}
108 
pdfOut_add_xref(pdfOut * pdf)109 int pdfOut_add_xref(pdfOut *pdf) // {{{  -  returns obj_no
110 {
111   assert(pdf);
112   assert(pdf->xrefsize<=pdf->xrefalloc);
113 
114   if (pdf->xrefsize==pdf->xrefalloc) {
115     long *tmp;
116     pdf->xrefalloc+=50;
117     tmp=realloc(pdf->xref,sizeof(long)*pdf->xrefalloc);
118     if (!tmp) {
119       pdf->xrefalloc=-1;
120       return -1;
121     }
122     pdf->xref=tmp;
123   }
124   pdf->xref[pdf->xrefsize++]=pdf->filepos;
125   return pdf->xrefsize; // xrefsize+1
126 }
127 // }}}
128 
pdfOut_add_page(pdfOut * pdf,int obj)129 int pdfOut_add_page(pdfOut *pdf,int obj) // {{{ -  returns false on error
130 {
131   assert(pdf);
132   assert(obj>0);
133   assert(pdf->pagessize<=pdf->pagesalloc);
134 
135   if (pdf->pagessize==pdf->pagesalloc) {
136     int *tmp;
137     pdf->pagesalloc+=10;
138     tmp=realloc(pdf->pages,sizeof(int)*pdf->pagesalloc);
139     if (!tmp) {
140       pdf->pagesalloc=-1;
141       return 0;
142     }
143     pdf->pages=tmp;
144   }
145   pdf->pages[pdf->pagessize++]=obj;
146   return 1;
147 }
148 // }}}
149 
pdfOut_add_kv(pdfOut * pdf,const char * key,const char * val)150 int pdfOut_add_kv(pdfOut *pdf,const char *key,const char *val) // {{{ -  returns false on error
151 {
152   assert(pdf);
153   assert(pdf->kvsize<=pdf->kvalloc);
154 
155   if (pdf->kvsize==pdf->kvalloc) {
156     struct keyval_t *tmp;
157     pdf->kvalloc+=10;
158     tmp=realloc(pdf->kv,sizeof(struct keyval_t)*pdf->kvalloc);
159     if (!tmp) {
160       pdf->kvalloc=-1;
161       return 0;
162     }
163     pdf->kv=tmp;
164   }
165   pdf->kv[pdf->kvsize].key=strdup(key);
166   pdf->kv[pdf->kvsize].value=strdup(val);
167   if ( (!pdf->kv[pdf->kvsize].key)||(!pdf->kv[pdf->kvsize].value) ) {
168     return 0;
169   }
170   pdf->kvsize++;
171   return 1;
172 }
173 // }}}
174 
pdfOut_begin_pdf(pdfOut * pdf)175 int pdfOut_begin_pdf(pdfOut *pdf) // ,...output_device?...) // {{{ - false on error
176 {
177   assert(pdf);
178   assert(pdf->kvsize==0); // otherwise: finish_pdf has not been called
179   int pages_obj;
180 
181   pdf->xrefsize=pdf->pagessize=0;
182   pdf->filepos=0;
183   pages_obj=pdfOut_add_xref(pdf); // fixed later
184   if (pages_obj!=1) {
185     return 0;
186   }
187   pdfOut_printf(pdf,"%%PDF-1.3\n");
188   return 1;
189 }
190 // }}}
191 
pdfOut_finish_pdf(pdfOut * pdf)192 void pdfOut_finish_pdf(pdfOut *pdf) // {{{
193 {
194   int iA;
195   int root_obj,info_obj=0,xref_start;
196   assert( (pdf)&&(pdf->filepos!=-1) );
197 
198   // pages
199   const int pages_obj=1;
200   pdf->xref[0]=pdf->filepos; // now fix it
201   pdfOut_printf(pdf,"%d 0 obj\n"
202                     "<</Type/Pages\n"
203                     "  /Count %d\n"
204                     "  /Kids [",
205                     pages_obj,pdf->pagessize);
206   for (iA=0;iA<pdf->pagessize;iA++) {
207     pdfOut_printf(pdf,"%d 0 R ",pdf->pages[iA]);
208   }
209   pdfOut_printf(pdf,"]\n"
210                     ">>\n"
211                     "endobj\n");
212 
213   // rootdict
214   root_obj=pdfOut_add_xref(pdf);
215   pdfOut_printf(pdf,"%d 0 obj\n"
216                     "<</Type/Catalog\n"
217                     "  /Pages %d 0 R\n"
218                     ">>\n"
219                     "endobj\n",
220                     root_obj,pages_obj);
221 
222   // info
223   if (pdf->kvsize) {
224     info_obj=pdfOut_add_xref(pdf);
225     pdfOut_printf(pdf,"%d 0 obj\n"
226                       "<<\n",
227                       info_obj);
228     for (iA=0;iA<pdf->kvsize;iA++) {
229       pdfOut_printf(pdf,"  /%s ",pdf->kv[iA].key);
230       pdfOut_putString(pdf,pdf->kv[iA].value,-1);
231       pdfOut_printf(pdf,"\n");
232     }
233     pdfOut_printf(pdf,">>\n"
234                       "endobj\n");
235   }
236   // TODO: some return-value checking (??)
237 
238   // write xref
239   xref_start=pdf->filepos;
240   pdfOut_printf(pdf,"xref\n"
241                     "%d %d\n"
242                     "%010d 65535 f \n",
243                     0,pdf->xrefsize+1,0);
244   for (iA=0;iA<pdf->xrefsize;iA++) {
245     pdfOut_printf(pdf,"%010ld 00000 n \n",
246                       pdf->xref[iA]);
247   }
248   pdfOut_printf(pdf,"trailer\n"
249                     "<<\n"
250                     "  /Size %d\n"
251                     "  /Root %d 0 R\n",
252                     pdf->xrefsize+1,
253                     root_obj);
254   if (info_obj) {
255     pdfOut_printf(pdf,"  /Info %d 0 R\n",info_obj);
256   }
257   pdfOut_printf(pdf,">>\n"
258                     "startxref\n"
259                     "%d\n"
260                     "%%%%EOF\n",
261                     xref_start);
262 
263   // set to done
264   pdf->filepos=-1;
265   for (iA=0;iA<pdf->kvsize;iA++) {
266     free(pdf->kv[iA].key);
267     free(pdf->kv[iA].value);
268   }
269   pdf->kvsize=0;
270 }
271 // }}}
272 
pdfOut_free(pdfOut * pdf)273 void pdfOut_free(pdfOut *pdf) // {{{
274 {
275   if (pdf) {
276     assert(pdf->kvsize==0); // otherwise: finish_pdf has not been called
277     free(pdf->kv);
278     free(pdf->pages);
279     free(pdf->xref);
280     free(pdf);
281   }
282 }
283 // }}}
284 
pdfOut_outfn(const char * buf,int len,void * context)285 static void pdfOut_outfn(const char *buf,int len,void *context) // {{{
286 {
287   pdfOut *pdf=(pdfOut *)context;
288 
289   if (fwrite(buf,1,len,stdout)!=len) {
290     perror("Short write");
291     assert(0);
292     return;
293   }
294   pdf->filepos+=len;
295 }
296 // }}}
297 
pdfOut_write_font(pdfOut * pdf,EMB_PARAMS * emb)298 int pdfOut_write_font(pdfOut *pdf,EMB_PARAMS *emb) // {{{
299 {
300   assert(pdf);
301   assert(emb);
302 
303   EMB_PDF_FONTDESCR *fdes=emb_pdf_fontdescr(emb);
304   if (!fdes) {
305     if (emb->outtype==EMB_FMT_STDFONT) { // std-14 font
306       const int f_obj=pdfOut_add_xref(pdf);
307       char *res=emb_pdf_simple_stdfont(emb);
308       if (!res) {
309         return 0;
310       }
311 
312       pdfOut_printf(pdf,"%d 0 obj\n"
313                         "%s"
314                         "endobj\n"
315                         ,f_obj,res);
316       free(res);
317       return f_obj;
318     }
319     return 0;
320   }
321 
322   const int ff_obj=pdfOut_add_xref(pdf);
323   pdfOut_printf(pdf,"%d 0 obj\n"
324                     "<</Length %d 0 R\n"
325                     ,ff_obj,ff_obj+1);
326   if (emb_pdf_get_fontfile_subtype(emb)) {
327     pdfOut_printf(pdf,"  /Subtype /%s\n",
328                       emb_pdf_get_fontfile_subtype(emb));
329   }
330   if (emb->outtype==EMB_FMT_TTF) {
331     pdfOut_printf(pdf,"  /Length1 %d 0 R\n"
332                       ,ff_obj+2);
333   } else if (emb->outtype==EMB_FMT_T1) { // TODO
334     pdfOut_printf(pdf,"  /Length1 ?\n"
335                       "  /Length2 ?\n"
336                       "  /Length3 ?\n"
337                       );
338   }
339   pdfOut_printf(pdf,">>\n"
340                     "stream\n");
341   long streamsize=-pdf->filepos;
342   const int outlen=emb_embed(emb,pdfOut_outfn,pdf);
343   streamsize+=pdf->filepos;
344   pdfOut_printf(pdf,"\nendstream\n"
345                     "endobj\n");
346 
347   const int l0_obj=pdfOut_add_xref(pdf);
348   assert(l0_obj==ff_obj+1);
349   pdfOut_printf(pdf,"%d 0 obj\n"
350                     "%ld\n"
351                     "endobj\n"
352                     ,l0_obj,streamsize);
353 
354   if (emb->outtype==EMB_FMT_TTF) {
355     const int l1_obj=pdfOut_add_xref(pdf);
356     assert(l1_obj==ff_obj+2);
357     pdfOut_printf(pdf,"%d 0 obj\n"
358                       "%d\n"
359                       "endobj\n"
360                       ,l1_obj,outlen);
361   }
362 
363   const int fd_obj=pdfOut_add_xref(pdf);
364   char *res=emb_pdf_simple_fontdescr(emb,fdes,ff_obj);
365   if (!res) {
366     free(fdes);
367     return 0;
368   }
369   pdfOut_printf(pdf,"%d 0 obj\n"
370                     "%s"
371                     "endobj\n"
372                     ,fd_obj,res);
373   free(res);
374 
375   EMB_PDF_FONTWIDTHS *fwid=emb_pdf_fontwidths(emb);
376   if (!fwid) {
377     free(fdes);
378     return 0;
379   }
380   const int f_obj=pdfOut_add_xref(pdf);
381   res=emb_pdf_simple_font(emb,fdes,fwid,fd_obj);
382   if (!res) {
383     free(fwid);
384     free(fdes);
385     return 0;
386   }
387   pdfOut_printf(pdf,"%d 0 obj\n"
388                     "%s"
389                     "endobj\n"
390                     ,f_obj,res);
391   free(res);
392   free(fwid);
393 
394   if (emb->plan&EMB_A_MULTIBYTE) {
395     res=emb_pdf_simple_cidfont(emb,fdes->fontname,f_obj);
396     if (!res) {
397       free(fdes);
398       return 0;
399     }
400     const int cf_obj=pdfOut_add_xref(pdf);
401     pdfOut_printf(pdf,"%d 0 obj\n"
402                       "%s"
403                       "endobj\n"
404                       ,cf_obj,res);
405     free(res);
406     free(fdes);
407     return cf_obj;
408   }
409 
410   free(fdes);
411   return f_obj;
412 }
413 // }}}
414 
415 #if 0
416 one_page(...parent,resources,mediabox,contents);
417 {
418 //                    "  /Resources %d 0 R\n"
419   pdfOut_printf(pdf,"%d 0 obj\n"
420                     "<</Type/Page\n"
421                     "  /Parent 1 0 R\n"
422                     "  /MediaBox [0 0 %d %d]\n"
423                     "  /Contents %d 0 R\n"
424                     ">>\n"
425                     "endobj\n"
426                     ,,,PageWidth,PageLength // TODO: into pdf->
427   ...
428 }
429 
430 ... pfb_embedder ... pfa?
431 #endif
432