fix for invisible characters appearing in pdf2swf documents
[swftools.git] / lib / pdf / CharOutputDev.cc
blobe1d19b2240fac56b00ed62eaa9bd94d0bf6404d7
1 /* CharOutputDev.cc
2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <math.h>
27 #include "../../config.h"
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_DIRENT_H
32 #include <dirent.h>
33 #endif
34 #ifdef HAVE_SYS_STAT_H
35 #include <sys/stat.h>
36 #endif
37 #ifdef HAVE_FONTCONFIG
38 #include <fontconfig.h>
39 #endif
41 // xpdf header files
42 #include "popplercompat.h"
43 #include "CharOutputDev.h"
45 // swftools header files
46 #include "../os.h"
47 #include "../log.h"
48 #include "../mem.h"
49 #include "../utf8.h"
50 #include "../gfxdevice.h"
51 #include "../gfxtools.h"
52 #include "../gfxfont.h"
53 #include "../gfxpoly.h"
54 #include "../devices/record.h"
55 #include "../devices/ops.h"
56 #include "../devices/polyops.h"
57 #include "../devices/render.h"
58 #include "../png.h"
60 // linked-in font data
61 #include "fonts.h"
63 typedef struct _fontfile
65 const char*filename;
66 int len; // basename length
67 int used;
68 struct _fontfile*next;
69 } fontfile_t;
71 // for pdfswf_addfont
72 static fontfile_t* global_fonts = 0;
73 static fontfile_t* global_fonts_next = 0;
75 static int fontnum = 0;
77 /* config */
78 struct fontentry {
79 const char*pdffont;
80 const char*filename;
81 char*afm;
82 int afmlen;
83 char*pfb;
84 int pfblen;
85 char*fullfilename;
86 DisplayFontParam *dfp;
87 } pdf2t1map[] ={
88 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
89 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
90 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
91 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
92 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
93 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
94 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
95 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
96 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
97 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
98 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
99 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
100 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
101 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
104 typedef struct _drawnchar
106 gfxcoord_t x,y;
107 int charid;
108 gfxcolor_t color;
109 } drawnchar_t;
111 class CharBuffer
113 drawnchar_t * chars;
114 int buf_size;
115 int num_chars;
117 public:
119 CharBuffer()
121 buf_size = 32;
122 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
123 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
124 num_chars = 0;
126 ~CharBuffer()
128 free(chars);chars = 0;
131 void grow(int size)
133 if(size>=buf_size) {
134 buf_size += 32;
135 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
139 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
141 grow(num_chars);
142 chars[num_chars].x = x;
143 chars[num_chars].y = y;
144 chars[num_chars].color = color;
145 chars[num_chars].charid = charid;
149 char* writeOutStdFont(fontentry* f)
151 FILE*fi;
152 char namebuf1[512];
153 char namebuf2[512];
154 char* tmpFileName = mktmpname(namebuf1);
156 sprintf(namebuf2, "%s.afm", tmpFileName);
157 fi = fopen(namebuf2, "wb");
158 if(!fi)
159 return 0;
160 int written = fwrite(f->afm, 1, f->afmlen, fi);
161 if(written<0)
162 return 0;
163 fclose(fi);
165 sprintf(namebuf2, "%s.pfb", tmpFileName);
166 fi = fopen(namebuf2, "wb");
167 if(!fi)
168 return 0;
169 written = fwrite(f->pfb, 1, f->pfblen, fi);
170 if(written<0)
171 return 0;
172 fclose(fi);
173 return strdup(namebuf2);
175 void unlinkfont(char* filename)
177 int l;
178 if(!filename)
179 return;
180 msg("<verbose> Removing temporary font file %s", filename);
181 l=strlen(filename);
182 unlink(filename);
183 if(!strncmp(&filename[l-4],".afm",4)) {
184 memcpy(&filename[l-4],".pfb",4); unlink(filename);
185 memcpy(&filename[l-4],".pfa",4); unlink(filename);
186 memcpy(&filename[l-4],".afm",4);
187 return;
188 } else
189 if(!strncmp(&filename[l-4],".pfa",4)) {
190 memcpy(&filename[l-4],".afm",4); unlink(filename);
191 memcpy(&filename[l-4],".pfa",4);
192 return;
193 } else
194 if(!strncmp(&filename[l-4],".pfb",4)) {
195 memcpy(&filename[l-4],".afm",4); unlink(filename);
196 memcpy(&filename[l-4],".pfb",4);
197 return;
201 static int config_use_fontconfig = 1;
202 static int fcinitcalled = 0;
204 GFXGlobalParams::GFXGlobalParams()
205 : GlobalParams((char*)"")
207 //setupBaseFonts(char *dir); //not tested yet
209 GFXGlobalParams::~GFXGlobalParams()
211 msg("<verbose> Performing cleanups");
212 int t;
213 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
214 if(pdf2t1map[t].fullfilename) {
215 unlinkfont(pdf2t1map[t].fullfilename);
218 #ifdef HAVE_FONTCONFIG
219 if(config_use_fontconfig && fcinitcalled)
220 FcFini();
221 #endif
223 #ifdef HAVE_FONTCONFIG
224 static char stralphacmp(const char*s1, const char*s2)
226 while(*s1 && *s2) {
227 /* skip over space, minus, comma etc. */
228 while(*s1>=32 && *s1<=63) s1++;
229 while(*s2>=32 && *s2<=63) s2++;
230 if(*s1!=*s2)
231 break;
232 s1++;s2++;
234 return *s1 - *s2;
237 static char fc_ismatch(FcPattern*match, char*family, char*style)
239 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
240 FcBool scalable=FcFalse, outline=FcFalse;
241 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
242 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
243 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
244 FcPatternGetBool(match, "outline", 0, &outline);
245 FcPatternGetBool(match, "scalable", 0, &scalable);
247 if(scalable!=FcTrue || outline!=FcTrue)
248 return 0;
250 if (!stralphacmp(fcfamily, family)) {
251 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
252 return 1;
253 } else {
254 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
255 return 0;
258 #endif
260 static inline char islowercase(char c)
262 return (c>='a' && c<='z');
265 char* fontconfig_searchForFont(char*name)
267 #ifdef HAVE_FONTCONFIG
268 if(!config_use_fontconfig)
269 return 0;
271 // call init ony once
272 if (!fcinitcalled) {
273 fcinitcalled = 1;
275 // check whether we have a config file
276 char* configfile = (char*)FcConfigFilename(0);
277 int configexists = 0;
278 FILE*fi = fopen(configfile, "rb");
279 if(fi) {
280 configexists = 1;fclose(fi);
281 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
282 } else {
283 msg("<debug> Initializing FontConfig (no configfile)");
286 if(!configexists) {
287 /* A fontconfig instance which didn't find a configfile is unbelievably
288 cranky, so let's just write out a small xml file and make fontconfig
289 happy */
290 FcConfig*c = FcConfigCreate();
291 char namebuf[512];
292 char* tmpFileName = mktmpname(namebuf);
293 FILE*fi = fopen(tmpFileName, "wb");
294 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
295 #ifdef WIN32
296 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
297 #endif
298 fprintf(fi, "<dir>~/.fonts</dir>\n");
299 #ifdef WIN32
300 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
301 #endif
302 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
303 fprintf(fi, "</fontconfig>\n");
304 fclose(fi);
305 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
306 FcConfigBuildFonts(c);
307 FcConfigSetCurrent(c);
310 if(!FcInit()) {
311 msg("<debug> FontConfig Initialization failed. Disabling.");
312 config_use_fontconfig = 0;
313 return 0;
315 FcConfig * config = FcConfigGetCurrent();
316 if(!config) {
317 msg("<debug> FontConfig Config Initialization failed. Disabling.");
318 config_use_fontconfig = 0;
319 return 0;
322 /* add external fonts to fontconfig's config, too. */
323 fontfile_t*fd = global_fonts;
324 while(fd) {
325 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
326 msg("<debug> Adding font %s to fontconfig", fd->filename);
327 fd = fd->next;
330 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
331 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
332 if(!set || !set->nfont) {
333 msg("<debug> FontConfig has zero fonts. Disabling.");
334 config_use_fontconfig = 0;
335 return 0;
338 if(getLogLevel() >= LOGLEVEL_TRACE) {
339 int t;
340 int p;
341 for(p=0;p<2;p++) {
342 if(set) {
343 for(t=0;t<set->nfont;t++) {
344 char*fcfamily=0,*fcstyle=0,*filename=0;
345 FcBool scalable=FcFalse, outline=FcFalse;
346 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
347 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
348 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
349 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
350 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
351 if(scalable && outline) {
352 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
356 set = FcConfigGetFonts(config, FcSetApplication);
361 char*family = strdup(name);
362 int len = strlen(family);
364 const char*styles[] = {"Medium", "Regular", "Bold", "Italic", "Black", "Narrow"};
365 const char*style = 0;
366 int t;
367 for(t=0;t<sizeof(styles)/sizeof(styles[0]);t++) {
368 int l = strlen(styles[t]);
369 if(len>l+1 && !strcmp(family+len-l, styles[t]) && islowercase(family[len-l-1])) {
370 style = styles[t];
371 family[len-l]=0;
372 break;
375 if(!style) {
376 char*dash = strchr(family, '-');
377 if(!dash) dash = strchr(family, ',');
378 if(dash) {
379 *dash = 0;
380 style = dash+1;
383 FcPattern*pattern = 0;
384 if(style) {
385 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
386 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
387 } else {
388 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
389 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
391 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
393 FcResult result;
394 FcConfigSubstitute(0, pattern, FcMatchPattern);
395 FcDefaultSubstitute(pattern);
397 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
398 if(set) {
399 int t;
400 for(t=0;t<set->nfont;t++) {
401 FcPattern*match = set->fonts[t];
402 //FcPattern*match = FcFontMatch(0, pattern, &result);
403 if(fc_ismatch(match, family, (char*)style)) {
404 char*filename=0;
405 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
406 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
407 filename=0;
409 //FcPatternDestroy(match);
410 msg("<debug> fontconfig: returning filename %s", filename);
411 free(family);
412 FcPatternDestroy(pattern);
413 FcFontSetDestroy(set);
414 return filename?strdup(filename):0;
418 free(family);
419 FcPatternDestroy(pattern);
420 FcFontSetDestroy(set);
421 return 0;
422 #else
423 return 0;
424 #endif
427 static DisplayFontParamKind detectFontType(const char*filename)
429 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
430 return displayFontTT;
431 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
432 return displayFontT1;
433 return displayFontTT;
436 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
438 msg("<verbose> looking for font %s", fontName->getCString());
440 char*name = fontName->getCString();
442 /* see if it is a pdf standard font */
443 int t;
444 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
445 if(!strcmp(name, pdf2t1map[t].pdffont)) {
446 if(!pdf2t1map[t].fullfilename) {
447 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
448 if(!pdf2t1map[t].fullfilename) {
449 msg("<error> Couldn't save default font- is the Temp Directory writable?");
450 } else {
451 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
453 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
454 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
455 pdf2t1map[t].dfp = dfp;
457 return pdf2t1map[t].dfp;
461 int bestlen = 0x7fffffff;
462 const char*bestfilename = 0;
464 #ifndef HAVE_FONTCONFIG
465 /* if we don't have fontconfig, try a simple filename-comparison approach */
466 fontfile_t*f = global_fonts;
467 while(f) {
468 if(strstr(f->filename, name)) {
469 if(f->len < bestlen) {
470 bestlen = f->len;
471 bestfilename = f->filename;
474 f = f->next;
476 #endif
478 /* if we didn't find anything up to now, try looking for the
479 font via fontconfig */
480 char*filename = 0;
481 if(!bestfilename) {
482 filename = fontconfig_searchForFont(name);
483 } else {
484 filename = strdup(bestfilename);
487 if(filename) {
488 msg("<verbose> Font %s maps to %s\n", name, filename);
489 DisplayFontParamKind kind = detectFontType(filename);
490 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
491 if(kind == displayFontTT) {
492 dfp->tt.fileName = new GString(filename);
493 } else {
494 dfp->t1.fileName = new GString(filename);
496 free(filename);
497 return dfp;
498 } else {
499 msg("<verbose> Font %s not found\n", name);
500 return GlobalParams::getDisplayFont(fontName);
504 DisplayFontParam *GFXGlobalParams::getDisplayCIDFont(GString *fontName, GString *collection)
506 DisplayFontParam*dfp = GlobalParams::getDisplayCIDFont(fontName, collection);
507 if(!dfp) {
508 dfp = this->getDisplayFont(fontName);
510 return dfp;
513 CharOutputDev::CharOutputDev(InfoOutputDev*info, PDFDoc*doc, int*page2page, int num_pages, int x, int y, int x1, int y1, int x2, int y2)
514 :CommonOutputDev(info, doc, page2page, num_pages, x, y, x1, y1, x2, y2)
516 this->type3active = 0;
517 this->xref = 0;
518 this->current_text_stroke = 0;
519 this->current_text_clip = 0;
520 this->config_bigchar=0;
521 this->config_extrafontdata = 0;
522 this->config_detectspaces = 1;
523 this->config_space_between_lines = 0;
524 this->config_linkdatafile = 0;
525 this->page2page = 0;
526 this->num_pages = 0;
527 this->links = 0;
528 this->last_link = 0;
531 CharOutputDev::~CharOutputDev()
535 void CharOutputDev::setParameter(const char*key, const char*value)
537 if(!strcmp(key,"detectspaces")) {
538 this->config_detectspaces = atoi(value);
539 } else if(!strcmp(key,"space_between_lines")) {
540 this->config_space_between_lines = atoi(value);
541 } else if(!strcmp(key,"extrafontdata")) {
542 this->config_extrafontdata = atoi(value);
543 } else if(!strcmp(key,"linkdatafile")) {
544 this->config_linkdatafile = strdup(value);
548 void CharOutputDev::setDevice(gfxdevice_t*dev)
550 this->device = dev;
553 static char*getFontName(GfxFont*font)
555 char*fontid;
556 GString*gstr = font->getName();
557 char* fname = gstr==0?0:gstr->getCString();
558 if(fname==0) {
559 char buf[32];
560 Ref*r=font->getID();
561 sprintf(buf, "UFONT%d", r->num);
562 fontid = strdup(buf);
563 } else {
564 fontid = strdup(fname);
567 char*fontname= 0;
568 char* plus = strchr(fontid, '+');
569 if(plus && plus < &fontid[strlen(fontid)-1]) {
570 fontname = strdup(plus+1);
571 } else {
572 fontname = strdup(fontid);
574 free(fontid);
575 return fontname;
578 static void dumpFontInfo(const char*loglevel, GfxFont*font);
579 static int lastdumps[1024];
580 static int lastdumppos = 0;
581 /* nr = 0 unknown
582 nr = 1 substituting
583 nr = 2 type 3
585 static void showFontError(GfxFont*font, int nr)
587 Ref*r=font->getID();
588 int t;
589 for(t=0;t<lastdumppos;t++)
590 if(lastdumps[t] == r->num)
591 break;
592 if(t < lastdumppos)
593 return;
594 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
595 lastdumps[lastdumppos++] = r->num;
596 if(nr == 0)
597 msg("<warning> The following font caused problems:");
598 else if(nr == 1)
599 msg("<warning> The following font caused problems (substituting):");
600 else if(nr == 2)
601 msg("<warning> The following Type 3 Font will be rendered as graphics:");
602 dumpFontInfo("<warning>", font);
605 static void dumpFontInfo(const char*loglevel, GfxFont*font)
607 char* id = getFontID(font);
608 char* name = getFontName(font);
609 Ref* r=font->getID();
610 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
612 GString*gstr = font->getTag();
614 msg("%s| Tag: %s", loglevel, id);
616 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
618 GfxFontType type=font->getType();
619 switch(type) {
620 case fontUnknownType:
621 msg("%s| Type: unknown",loglevel);
622 break;
623 case fontType1:
624 msg("%s| Type: 1",loglevel);
625 break;
626 case fontType1C:
627 msg("%s| Type: 1C",loglevel);
628 break;
629 case fontType3:
630 msg("%s| Type: 3",loglevel);
631 break;
632 case fontTrueType:
633 msg("%s| Type: TrueType",loglevel);
634 break;
635 case fontCIDType0:
636 msg("%s| Type: CIDType0",loglevel);
637 break;
638 case fontCIDType0C:
639 msg("%s| Type: CIDType0C",loglevel);
640 break;
641 case fontCIDType2:
642 msg("%s| Type: CIDType2",loglevel);
643 break;
646 Ref embRef;
647 GBool embedded = font->getEmbeddedFontID(&embRef);
648 char*embeddedName=0;
649 if(font->getEmbeddedFontName()) {
650 embeddedName = font->getEmbeddedFontName()->getCString();
652 if(embedded)
653 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
655 gstr = font->getExtFontFile();
656 if(gstr)
657 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
659 // Get font descriptor flags.
660 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
661 if(font->isSerif()) msg("%s| is serif", loglevel);
662 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
663 if(font->isItalic()) msg("%s| is italic", loglevel);
664 if(font->isBold()) msg("%s| is bold", loglevel);
666 free(id);
667 free(name);
670 GBool CharOutputDev::needNonText()
672 return gFalse;
675 void CharOutputDev::endPage()
677 msg("<verbose> endPage (GfxOutputDev)");
679 if(this->links) {
680 kdtree_destroy(this->links);
681 this->links = 0;
683 GFXLink*l = this->last_link;
684 while(l) {
685 GFXLink*last = l->last;
686 l->draw(this,device);
687 delete l;
688 l = last;
690 this->last_link = 0;
693 static inline double sqr(double x) {return x*x;}
695 GBool CharOutputDev::upsideDown()
697 return gTrue;
699 GBool CharOutputDev::useDrawChar()
701 return gTrue;
704 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
705 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
707 static char tmp_printstr[4096];
708 char* makeStringPrintable(char*str)
710 int len = strlen(str);
711 int dots = 0;
712 if(len>=80) {
713 len = 80;
714 dots = 1;
716 int t;
717 for(t=0;t<len;t++) {
718 char c = str[t];
719 if(c<32 || c>124) {
720 c = '.';
722 tmp_printstr[t] = c;
724 if(dots) {
725 tmp_printstr[len++] = '.';
726 tmp_printstr[len++] = '.';
727 tmp_printstr[len++] = '.';
729 tmp_printstr[len] = 0;
730 return tmp_printstr;
733 void CharOutputDev::updateTextMat(GfxState*state)
737 void CharOutputDev::beginString(GfxState *state, GString *s)
739 int render = state->getRender();
740 if(current_text_stroke) {
741 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
743 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
746 static gfxline_t* mkEmptyGfxShape(double x, double y)
748 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
749 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
750 return line;
753 void CharOutputDev::drawChar(GfxState *state, double x, double y,
754 double dx, double dy,
755 double originX, double originY,
756 CharCode charid, int nBytes, Unicode *_u, int uLen)
758 FontInfo*current_fontinfo = this->info->getFontInfo(state);
760 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
761 msg("<error> Invalid charid %d for font %p (%d characters)", charid, current_fontinfo, current_fontinfo?current_fontinfo->num_glyphs:0);
762 return;
765 gfxfont_t*current_gfxfont = current_fontinfo->getGfxFont();
766 if(!current_fontinfo->seen) {
767 dumpFontInfo("<verbose>", state->getFont());
768 device->addfont(device, current_gfxfont);
769 current_fontinfo->seen = 1;
772 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
774 int render = state->getRender();
775 gfxcolor_t col = gfxstate_getfillcolor(state);
777 GFXLink*link = 0;
778 if(links) {
779 kdarea_t*a = kdtree_find(this->links, x+dx/2,y+dy/2);
780 if(a) {
781 link = (GFXLink*)a->data;
782 #if 0
783 if(link) {
784 printf("area [%d %d %d %d] (link [%f %f %f %f]) contains (%f,%f)\n",
785 a->bbox.xmin, a->bbox.ymin, a->bbox.xmax, a->bbox.ymax,
786 link->x1, link->y1, link->x2, link->y2,
787 x+dx/2, y+dy/2
790 #endif
792 if(link != previous_link) {
793 previous_link = link;
794 device->setparameter(device, "link", link?link->action:"");
798 // check for invisible text -- this is used by Acrobat Capture
799 if (render == RENDER_INVISIBLE ||
800 render == RENDER_FILL && state->getFillColorSpace()->isNonMarking() ||
801 render == RENDER_STROKE && state->getStrokeColorSpace()->isNonMarking()) {
802 col.a = 0;
803 if(!config_extrafontdata)
804 return;
807 GfxFont*font = state->getFont();
809 if(font->getType() == fontType3) {
810 /* type 3 chars are passed as graphics */
811 msg("<debug> type3 char at %f/%f", x, y);
812 return;
815 Unicode u = uLen?(_u[0]):0;
817 gfxmatrix_t m = current_fontinfo->get_gfxmatrix(state);
818 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
820 gfxbbox_t bbox;
822 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d> '%c') CID=%d render=%d glyphid=%d font=%p size=%f",
823 m.tx, m.ty, (charid&127)>=32?charid:'?', charid, u, uLen, u,
824 font->isCIDFont(), render, glyphid, current_gfxfont,
825 m.m00);
827 gfxglyph_t* gfxglyph = &current_gfxfont->glyphs[glyphid];
829 int space = current_fontinfo->space_char;
830 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
831 /* space char detection */
832 //bool different_y = last_char_y - m.ty;
833 bool different_y = m.ty < last_char_y - last_ascent*last_char_y_fontsize
834 || m.ty > last_char_y + last_descent*last_char_y_fontsize;
835 double expected_x = last_char_x + last_char_advance*last_char_x_fontsize;
836 double rightx = m.tx + gfxglyph->advance * m.m00;
837 if(different_y) {
838 expected_x = m.tx - width/2;
841 if((!different_y || config_space_between_lines) &&
842 !last_char_was_space && !current_fontinfo->usesSpaces()) {
843 int space = current_fontinfo->space_char;
844 float width = fmax(m.m00*current_fontinfo->average_advance, last_char_x_fontsize*last_average_advance);
845 if(m.tx - expected_x >= width*4/10) {
846 msg("<debug> There's a %f pixel gap between char %d and char %d (expected no more than %f), I'm inserting a space here",
847 m.tx-expected_x,
848 last_char, glyphid,
849 width*4/10
851 #ifdef VISUALIZE_CHAR_GAPS
852 bbox = gfxline_getbbox(gfxglyph->line);
853 gfxline_t*rect = gfxline_makerectangle(last_char_x,m.ty,m.tx,m.ty+10);
854 gfxcolor_t red = {255,255,0,0};
855 device->fill(device, rect, &red);
856 gfxline_free(rect);
857 #endif
858 gfxmatrix_t m2 = m;
859 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
860 if(m2.tx < expected_x) m2.tx = expected_x;
861 device->drawchar(device, current_gfxfont, space, &col, &m2);
862 if(link) {
863 link->addchar(32);
867 last_average_advance = current_fontinfo->average_advance;
868 last_char_advance = gfxglyph->advance;
869 last_char_x_fontsize = m.m00;
870 last_char_y_fontsize = -m.m11;
871 last_char = glyphid;
872 last_char_x = m.tx;
873 last_char_y = m.ty;
874 last_ascent = current_gfxfont->ascent;
875 last_descent = fmax(current_gfxfont->descent, current_gfxfont->ascent/3);
876 last_char_was_space = GLYPH_IS_SPACE(gfxglyph);
878 if(m.tx < expected_x && rightx < expected_x + 1 && GLYPH_IS_SPACE(gfxglyph)) {
879 msg("<debug> Dropping dedented space char at %f-%f (before %f)",
880 m.tx, rightx, expected_x);
881 return;
885 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
887 if(link) {
888 link->addchar(current_gfxfont->glyphs[glyphid].unicode);
892 void CharOutputDev::endString(GfxState *state)
896 void CharOutputDev::endTextObject(GfxState *state)
900 /* the logic seems to be as following:
901 first, beginType3Char is called, with the charcode and the coordinates.
902 if this function returns true, it already knew about the char and has now drawn it.
903 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
904 called with some parameters.
905 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
906 at the position first passed to beginType3Char). the char ends with endType3Char.
908 The drawing operations between beginType3Char and endType3Char are somewhat different to
909 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
910 color determines the color of a font)
913 GBool CharOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
915 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
916 type3active = 1;
918 if(config_extrafontdata) {
920 FontInfo*current_fontinfo = info->getFontInfo(state);
921 if(!current_fontinfo) {
922 msg("<error> Couldn't find font info");
923 return gFalse;
925 gfxfont_t*current_gfxfont = current_fontinfo->getGfxFont();
927 /*m.m00*=INTERNAL_FONT_SIZE;
928 m.m01*=INTERNAL_FONT_SIZE;
929 m.m10*=INTERNAL_FONT_SIZE;
930 m.m11*=INTERNAL_FONT_SIZE;*/
932 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
933 msg("<error> Invalid type3 charid %d for font %p", charid, current_fontinfo);
934 return gFalse;
936 gfxcolor_t col={0,0,0,0};
937 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
938 gfxmatrix_t m = current_fontinfo->get_gfxmatrix(state);
939 this->transformXY(state, 0, 0, &m.tx, &m.ty);
940 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
944 /* the character itself is going to be passed using the draw functions */
945 return gFalse; /* gTrue= is_in_cache? */
948 void CharOutputDev::type3D0(GfxState *state, double wx, double wy) {
950 void CharOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
953 void CharOutputDev::endType3Char(GfxState *state)
955 type3active = 0;
956 msg("<debug> endType3Char");
959 void CharOutputDev::beginPage(GfxState *state, int pageNum)
961 this->currentpage = pageNum;
962 this->last_char_was_space = 1;
963 this->last_char_y = 0;
964 this->last_char_y_fontsize = 0;
965 this->last_ascent = 0;
966 this->last_descent = 0;
967 this->previous_link = 0;
970 void GFXLink::draw(CharOutputDev*out, gfxdevice_t*dev)
972 int x1,y1,x2,y2;
973 out->transformXY_stateless(this->x1, this->y1, &x1, &y1);
974 out->transformXY_stateless(this->x2, this->y2, &x2, &y2);
976 gfxline_t points[5];
977 points[0].type = gfx_moveTo;
978 points[0].x = x1;
979 points[0].y = y1;
980 points[0].next = &points[1];
981 points[1].type = gfx_lineTo;
982 points[1].x = x1;
983 points[1].y = y2;
984 points[1].next = &points[2];
985 points[2].type = gfx_lineTo;
986 points[2].x = x2;
987 points[2].y = y2;
988 points[2].next = &points[3];
989 points[3].type = gfx_lineTo;
990 points[3].x = x2;
991 points[3].y = y1;
992 points[3].next = &points[4];
993 points[4].type = gfx_lineTo;
994 points[4].x = x1;
995 points[4].y = y1;
996 points[4].next = 0;
997 msg("<trace> drawing link %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f to %s (\"%s\")",
998 points[0].x, points[0].y,
999 points[1].x, points[1].y,
1000 points[2].x, points[2].y,
1001 points[3].x, points[3].y, action, text);
1003 dev->drawlink(dev, points, action, text);
1006 void GFXLink::addchar(int unicode)
1008 msg("<trace> Adding '%c' (%d) to link %s", unicode, unicode, action);
1009 char buf[8];
1010 int l = writeUTF8(unicode, buf);
1011 while(size+l+1>=buf_size) {
1012 buf_size += 32;
1013 text = (char*)rfx_realloc(text, buf_size);
1015 strcpy(text+size, buf);
1016 size += l;
1019 GFXLink::GFXLink(GFXLink*last, const char*action, double x1, double y1, double x2, double y2)
1021 this->buf_size = 0;
1022 this->size = 0;
1023 this->text = 0;
1024 this->last = last;
1025 this->action = strdup(action);
1026 this->x1 = x1;
1027 this->y1 = y1;
1028 this->x2 = x2;
1029 this->y2 = y2;
1032 GFXLink::~GFXLink()
1034 free((void*)this->action);
1035 if(this->text)
1036 free(this->text);
1037 this->text = 0;
1038 this->action = 0;
1042 void CharOutputDev::processLink(Link *link, Catalog *catalog)
1044 double x1, y1, x2, y2;
1046 msg("<debug> drawlink");
1048 link->getRect(&x1, &y1, &x2, &y2);
1050 LinkAction*actionobj=link->getAction();
1051 char buf[128];
1052 char*s = 0;
1053 const char*type = "-?-";
1054 char*named = 0;
1055 int page = -1;
1056 msg("<trace> drawlink actionobj=%d", actionobj->getKind());
1057 switch(actionobj->getKind())
1059 case actionGoTo: {
1060 type = "GoTo";
1061 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1062 LinkDest *dest=NULL;
1063 if (ha->getDest()==NULL)
1064 dest=catalog->findDest(ha->getNamedDest());
1065 else
1066 dest=ha->getDest()->copy();
1067 if (dest){
1068 if (dest->isPageRef()){
1069 Ref pageref=dest->getPageRef();
1070 page=catalog->findPage(pageref.num,pageref.gen);
1072 else page=dest->getPageNum();
1073 sprintf(buf, "%d", page);
1074 s = strdup(buf);
1075 delete dest;
1078 break;
1079 case actionGoToR: {
1080 type = "GoToR";
1081 LinkGoToR*l = (LinkGoToR*)actionobj;
1082 GString*g = l->getFileName();
1083 if(g)
1084 s = strdup(g->getCString());
1085 if(!s) {
1086 /* if the GoToR link has no filename, then
1087 try to find a refernce in the *local*
1088 file */
1089 GString*g = l->getNamedDest();
1090 if(g)
1091 s = strdup(g->getCString());
1094 break;
1095 case actionNamed: {
1096 type = "Named";
1097 LinkNamed*l = (LinkNamed*)actionobj;
1098 GString*name = l->getName();
1099 if(name) {
1100 s = strdup(name->lowerCase()->getCString());
1101 named = name->getCString();
1102 if(!strchr(s,':'))
1104 if(strstr(s, "next") || strstr(s, "forward"))
1106 page = currentpage + 1;
1108 else if(strstr(s, "prev") || strstr(s, "back"))
1110 page = currentpage - 1;
1112 else if(strstr(s, "last") || strstr(s, "end"))
1114 if(this->page2page && this->num_pages) {
1115 page = this->page2page[this->num_pages-1];
1118 else if(strstr(s, "first") || strstr(s, "top"))
1120 page = 1;
1125 break;
1126 case actionLaunch: {
1127 type = "Launch";
1128 LinkLaunch*l = (LinkLaunch*)actionobj;
1129 GString * str = new GString(l->getFileName());
1130 GString * params = l->getParams();
1131 if(params)
1132 str->append(params);
1133 s = strdup(str->getCString());
1134 delete str;
1136 break;
1137 case actionURI: {
1138 char*url = 0;
1139 type = "URI";
1140 LinkURI*l = (LinkURI*)actionobj;
1141 GString*g = l->getURI();
1142 if(g) {
1143 url = g->getCString();
1144 s = strdup(url);
1147 break;
1148 case actionUnknown: {
1149 type = "Unknown";
1150 LinkUnknown*l = (LinkUnknown*)actionobj;
1151 s = strdup("");
1153 break;
1154 default: {
1155 msg("<error> Unknown link type!");
1156 break;
1160 if(!s) s = strdup("-?-");
1162 if(!getGfxGlobals()->linkinfo && (page || s))
1164 msg("<notice> File contains links");
1165 getGfxGlobals()->linkinfo = 1;
1168 char*action = 0;
1169 if(page>0) {
1170 int t;
1171 int lpage = -1;
1172 for(t=1;t<=this->num_pages;t++) {
1173 if(this->page2page[t]==page) {
1174 lpage = t;
1175 break;
1178 if(lpage<0) {
1179 lpage = page;
1182 char buf[80];
1183 sprintf(buf, "page%d", lpage);
1184 action = buf;
1186 else if(s)
1188 action = s;
1189 if(this->config_linkdatafile) {
1190 FILE*fi = fopen(config_linkdatafile, "ab+");
1191 fprintf(fi, "%s\n", s);
1192 fclose(fi);
1196 this->last_link = new GFXLink(this->last_link, action, x1, y1, x2, y2);
1197 if(!this->links) {
1198 this->links = kdtree_new();
1200 kdtree_add_box(this->links, x1,y1,x2,y2, this->last_link);
1201 #if 0
1202 printf("adding link %p at %f %f %f %f to tree\n", this->last_link, x1, y1, x2, y2);
1203 #endif
1205 msg("<verbose> storing \"%s\" link to \"%s\" (%f %f %f %f)", type, FIXNULL(action), x1, y1, x2, y2);
1206 free(s);s=0;
1209 void CharOutputDev::saveState(GfxState *state) {
1210 msg("<trace> saveState %p", state);
1211 updateAll(state);
1214 void CharOutputDev::restoreState(GfxState *state)
1216 updateAll(state);
1219 void CharOutputDev::updateFont(GfxState *state)
1221 GfxFont* gfxFont = state->getFont();
1222 if (!gfxFont) {
1223 return;
1226 char*id = getFontID(gfxFont);
1227 msg("<verbose> Updating font to %s", FIXNULL(id));
1228 free(id);id=0;
1230 if(gfxFont->getType() == fontType3) {
1231 infofeature("Type3 fonts");
1233 updateTextMat(state);
1236 static const char* dirseparator()
1238 #ifdef WIN32
1239 return "\\";
1240 #else
1241 return "/";
1242 #endif
1245 void addGlobalFont(const char*filename)
1247 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
1248 memset(f, 0, sizeof(fontfile_t));
1249 f->filename = filename;
1250 int len = strlen(filename);
1251 char*r1 = strrchr((char*)filename, '/');
1252 char*r2 = strrchr((char*)filename, '\\');
1253 if(r2>r1)
1254 r1 = r2;
1255 if(r1) {
1256 len = strlen(r1+1);
1258 f->len = len;
1260 msg("<verbose> Adding font \"%s\".", filename);
1261 if(global_fonts_next) {
1262 global_fonts_next->next = f;
1263 global_fonts_next = global_fonts_next->next;
1264 } else {
1265 global_fonts_next = global_fonts = f;
1269 void addGlobalLanguageDir(const char*dir)
1271 #ifdef HAVE_POPPLER
1272 msg("<notice> NOT adding %s to language pack directories (not implemented with poppler)", dir);
1273 #else
1274 msg("<notice> Adding %s to language pack directories", dir);
1276 FILE*fi = 0;
1277 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
1278 strcpy(config_file, dir);
1279 strcat(config_file, dirseparator());
1280 strcat(config_file, "add-to-xpdfrc");
1282 fi = fopen(config_file, "rb");
1283 if(!fi) {
1284 msg("<error> Could not open %s", config_file);
1285 return;
1287 globalParams->parseFile(new GString(config_file), fi);
1288 fclose(fi);
1289 #endif
1292 void addGlobalFontDir(const char*dirname)
1294 #ifdef HAVE_DIRENT_H
1295 DIR*dir = opendir(dirname);
1296 if(!dir) {
1297 msg("<warning> Couldn't open directory %s", dirname);
1298 return;
1300 struct dirent*ent;
1301 int fonts = 0;
1302 while(1) {
1303 ent = readdir (dir);
1304 if (!ent)
1305 break;
1306 int l;
1307 char*name = ent->d_name;
1308 char type = 0;
1309 if(!name) continue;
1310 l=strlen(name);
1311 if(l<4)
1312 continue;
1313 if(!strncasecmp(&name[l-4], ".pfa", 4))
1314 type=1;
1315 if(!strncasecmp(&name[l-4], ".pfb", 4))
1316 type=3;
1317 if(!strncasecmp(&name[l-4], ".ttf", 4))
1318 type=2;
1319 if(type) {
1320 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
1321 strcpy(fontname, dirname);
1322 strcat(fontname, dirseparator());
1323 strcat(fontname, name);
1324 addGlobalFont(fontname);
1325 fonts++;
1328 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
1329 closedir(dir);
1330 #else
1331 msg("<warning> No dirent.h");
1332 #endif