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 */
27 #include "../../config.h"
34 #ifdef HAVE_SYS_STAT_H
37 #ifdef HAVE_FONTCONFIG
38 #include <fontconfig.h>
42 #include "popplercompat.h"
43 #include "CharOutputDev.h"
45 // swftools header files
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"
60 // linked-in font data
63 typedef struct _fontfile
66 int len
; // basename length
68 struct _fontfile
*next
;
72 static fontfile_t
* global_fonts
= 0;
73 static fontfile_t
* global_fonts_next
= 0;
75 static int fontnum
= 0;
86 DisplayFontParam
*dfp
;
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
122 chars
= (drawnchar_t
*)malloc(sizeof(drawnchar_t
)*buf_size
);
123 memset(chars
, 0, sizeof(drawnchar_t
)*buf_size
);
128 free(chars
);chars
= 0;
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
)
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
)
154 char* tmpFileName
= mktmpname(namebuf1
);
156 sprintf(namebuf2
, "%s.afm", tmpFileName
);
157 fi
= fopen(namebuf2
, "wb");
160 int written
= fwrite(f
->afm
, 1, f
->afmlen
, fi
);
165 sprintf(namebuf2
, "%s.pfb", tmpFileName
);
166 fi
= fopen(namebuf2
, "wb");
169 written
= fwrite(f
->pfb
, 1, f
->pfblen
, fi
);
173 return strdup(namebuf2
);
175 void unlinkfont(char* filename
)
180 msg("<verbose> Removing temporary font file %s", 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);
189 if(!strncmp(&filename
[l
-4],".pfa",4)) {
190 memcpy(&filename
[l
-4],".afm",4); unlink(filename
);
191 memcpy(&filename
[l
-4],".pfa",4);
194 if(!strncmp(&filename
[l
-4],".pfb",4)) {
195 memcpy(&filename
[l
-4],".afm",4); unlink(filename
);
196 memcpy(&filename
[l
-4],".pfb",4);
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");
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
)
223 #ifdef HAVE_FONTCONFIG
224 static char stralphacmp(const char*s1
, const char*s2
)
227 /* skip over space, minus, comma etc. */
228 while(*s1
>=32 && *s1
<=63) s1
++;
229 while(*s2
>=32 && *s2
<=63) 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
)
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
:"");
254 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
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
)
271 // call init ony once
275 // check whether we have a config file
276 char* configfile
= (char*)FcConfigFilename(0);
277 int configexists
= 0;
278 FILE*fi
= fopen(configfile
, "rb");
280 configexists
= 1;fclose(fi
);
281 msg("<debug> Initializing FontConfig (configfile=%s)", configfile
);
283 msg("<debug> Initializing FontConfig (no configfile)");
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
290 FcConfig
*c
= FcConfigCreate();
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">
296 fprintf(fi
, "<dir>WINDOWSFONTDIR</dir>\n");
298 fprintf(fi
, "<dir>~/.fonts</dir>\n");
300 fprintf(fi
, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
302 fprintf(fi
, "<cachedir>~/.fontconfig</cachedir>\n");
303 fprintf(fi
, "</fontconfig>\n");
305 FcConfigParseAndLoad(c
, (FcChar8
*)tmpFileName
, 1);
306 FcConfigBuildFonts(c
);
307 FcConfigSetCurrent(c
);
311 msg("<debug> FontConfig Initialization failed. Disabling.");
312 config_use_fontconfig
= 0;
315 FcConfig
* config
= FcConfigGetCurrent();
317 msg("<debug> FontConfig Config Initialization failed. Disabling.");
318 config_use_fontconfig
= 0;
322 /* add external fonts to fontconfig's config, too. */
323 fontfile_t
*fd
= global_fonts
;
325 FcConfigAppFontAddFile(config
, (FcChar8
*)fd
->filename
);
326 msg("<debug> Adding font %s to fontconfig", fd
->filename
);
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;
338 if(getLogLevel() >= LOGLEVEL_TRACE
) {
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;
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])) {
376 char*dash
= strchr(family
, '-');
377 if(!dash
) dash
= strchr(family
, ',');
383 FcPattern
*pattern
= 0;
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
);
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
);
394 FcConfigSubstitute(0, pattern
, FcMatchPattern
);
395 FcDefaultSubstitute(pattern
);
397 FcFontSet
*set
= FcFontSort(0, pattern
, 1, 0, &result
);
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
)) {
405 if(FcPatternGetString(match
, "file", 0, (FcChar8
**)&filename
) != FcResultMatch
) {
406 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name
);
409 //FcPatternDestroy(match);
410 msg("<debug> fontconfig: returning filename %s", filename
);
412 FcPatternDestroy(pattern
);
413 FcFontSetDestroy(set
);
414 return filename
?strdup(filename
):0;
419 FcPatternDestroy(pattern
);
420 FcFontSetDestroy(set
);
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 */
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?");
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
;
468 if(strstr(f
->filename
, name
)) {
469 if(f
->len
< bestlen
) {
471 bestfilename
= f
->filename
;
478 /* if we didn't find anything up to now, try looking for the
479 font via fontconfig */
482 filename
= fontconfig_searchForFont(name
);
484 filename
= strdup(bestfilename
);
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
);
494 dfp
->t1
.fileName
= new GString(filename
);
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
);
508 dfp
= this->getDisplayFont(fontName
);
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;
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;
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
)
553 static char*getFontName(GfxFont
*font
)
556 GString
*gstr
= font
->getName();
557 char* fname
= gstr
==0?0:gstr
->getCString();
561 sprintf(buf
, "UFONT%d", r
->num
);
562 fontid
= strdup(buf
);
564 fontid
= strdup(fname
);
568 char* plus
= strchr(fontid
, '+');
569 if(plus
&& plus
< &fontid
[strlen(fontid
)-1]) {
570 fontname
= strdup(plus
+1);
572 fontname
= strdup(fontid
);
578 static void dumpFontInfo(const char*loglevel
, GfxFont
*font
);
579 static int lastdumps
[1024];
580 static int lastdumppos
= 0;
585 static void showFontError(GfxFont
*font
, int nr
)
589 for(t
=0;t
<lastdumppos
;t
++)
590 if(lastdumps
[t
] == r
->num
)
594 if(lastdumppos
<sizeof(lastdumps
)/sizeof(int))
595 lastdumps
[lastdumppos
++] = r
->num
;
597 msg("<warning> The following font caused problems:");
599 msg("<warning> The following font caused problems (substituting):");
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();
620 case fontUnknownType
:
621 msg("%s| Type: unknown",loglevel
);
624 msg("%s| Type: 1",loglevel
);
627 msg("%s| Type: 1C",loglevel
);
630 msg("%s| Type: 3",loglevel
);
633 msg("%s| Type: TrueType",loglevel
);
636 msg("%s| Type: CIDType0",loglevel
);
639 msg("%s| Type: CIDType0C",loglevel
);
642 msg("%s| Type: CIDType2",loglevel
);
647 GBool embedded
= font
->getEmbeddedFontID(&embRef
);
649 if(font
->getEmbeddedFontName()) {
650 embeddedName
= font
->getEmbeddedFontName()->getCString();
653 msg("%s| Embedded id: %s id: %d",loglevel
, FIXNULL(embeddedName
), embRef
.num
);
655 gstr
= font
->getExtFontFile();
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
);
670 GBool
CharOutputDev::needNonText()
675 void CharOutputDev::endPage()
677 msg("<verbose> endPage (GfxOutputDev)");
680 kdtree_destroy(this->links
);
683 GFXLink
*l
= this->last_link
;
685 GFXLink
*last
= l
->last
;
686 l
->draw(this,device
);
693 static inline double sqr(double x
) {return x
*x
;}
695 GBool
CharOutputDev::upsideDown()
699 GBool
CharOutputDev::useDrawChar()
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
);
725 tmp_printstr
[len
++] = '.';
726 tmp_printstr
[len
++] = '.';
727 tmp_printstr
[len
++] = '.';
729 tmp_printstr
[len
] = 0;
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;
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);
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
);
779 kdarea_t
*a
= kdtree_find(this->links
, x
+dx
/2,y
+dy
/2);
781 link
= (GFXLink
*)a
->data
;
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
,
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()) {
803 if(!config_extrafontdata
)
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
);
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
);
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
,
827 gfxglyph_t
* gfxglyph
= ¤t_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
;
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",
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
);
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
);
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
;
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
);
885 device
->drawchar(device
, current_gfxfont
, glyphid
, &col
, &m
);
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);
918 if(config_extrafontdata
) {
920 FontInfo
*current_fontinfo
= info
->getFontInfo(state
);
921 if(!current_fontinfo
) {
922 msg("<error> Couldn't find font info");
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
);
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
)
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
)
973 out
->transformXY_stateless(this->x1
, this->y1
, &x1
, &y1
);
974 out
->transformXY_stateless(this->x2
, this->y2
, &x2
, &y2
);
977 points
[0].type
= gfx_moveTo
;
980 points
[0].next
= &points
[1];
981 points
[1].type
= gfx_lineTo
;
984 points
[1].next
= &points
[2];
985 points
[2].type
= gfx_lineTo
;
988 points
[2].next
= &points
[3];
989 points
[3].type
= gfx_lineTo
;
992 points
[3].next
= &points
[4];
993 points
[4].type
= gfx_lineTo
;
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
);
1010 int l
= writeUTF8(unicode
, buf
);
1011 while(size
+l
+1>=buf_size
) {
1013 text
= (char*)rfx_realloc(text
, buf_size
);
1015 strcpy(text
+size
, buf
);
1019 GFXLink::GFXLink(GFXLink
*last
, const char*action
, double x1
, double y1
, double x2
, double y2
)
1025 this->action
= strdup(action
);
1034 free((void*)this->action
);
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();
1053 const char*type
= "-?-";
1056 msg("<trace> drawlink actionobj=%d", actionobj
->getKind());
1057 switch(actionobj
->getKind())
1061 LinkGoTo
*ha
=(LinkGoTo
*)link
->getAction();
1062 LinkDest
*dest
=NULL
;
1063 if (ha
->getDest()==NULL
)
1064 dest
=catalog
->findDest(ha
->getNamedDest());
1066 dest
=ha
->getDest()->copy();
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
);
1081 LinkGoToR
*l
= (LinkGoToR
*)actionobj
;
1082 GString
*g
= l
->getFileName();
1084 s
= strdup(g
->getCString());
1086 /* if the GoToR link has no filename, then
1087 try to find a refernce in the *local*
1089 GString
*g
= l
->getNamedDest();
1091 s
= strdup(g
->getCString());
1097 LinkNamed
*l
= (LinkNamed
*)actionobj
;
1098 GString
*name
= l
->getName();
1100 s
= strdup(name
->lowerCase()->getCString());
1101 named
= name
->getCString();
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"))
1126 case actionLaunch
: {
1128 LinkLaunch
*l
= (LinkLaunch
*)actionobj
;
1129 GString
* str
= new GString(l
->getFileName());
1130 GString
* params
= l
->getParams();
1132 str
->append(params
);
1133 s
= strdup(str
->getCString());
1140 LinkURI
*l
= (LinkURI
*)actionobj
;
1141 GString
*g
= l
->getURI();
1143 url
= g
->getCString();
1148 case actionUnknown
: {
1150 LinkUnknown
*l
= (LinkUnknown
*)actionobj
;
1155 msg("<error> Unknown link type!");
1160 if(!s
) s
= strdup("-?-");
1162 if(!getGfxGlobals()->linkinfo
&& (page
|| s
))
1164 msg("<notice> File contains links");
1165 getGfxGlobals()->linkinfo
= 1;
1172 for(t
=1;t
<=this->num_pages
;t
++) {
1173 if(this->page2page
[t
]==page
) {
1183 sprintf(buf
, "page%d", lpage
);
1189 if(this->config_linkdatafile
) {
1190 FILE*fi
= fopen(config_linkdatafile
, "ab+");
1191 fprintf(fi
, "%s\n", s
);
1196 this->last_link
= new GFXLink(this->last_link
, action
, x1
, y1
, x2
, y2
);
1198 this->links
= kdtree_new();
1200 kdtree_add_box(this->links
, x1
,y1
,x2
,y2
, this->last_link
);
1202 printf("adding link %p at %f %f %f %f to tree\n", this->last_link
, x1
, y1
, x2
, y2
);
1205 msg("<verbose> storing \"%s\" link to \"%s\" (%f %f %f %f)", type
, FIXNULL(action
), x1
, y1
, x2
, y2
);
1209 void CharOutputDev::saveState(GfxState
*state
) {
1210 msg("<trace> saveState %p", state
);
1214 void CharOutputDev::restoreState(GfxState
*state
)
1219 void CharOutputDev::updateFont(GfxState
*state
)
1221 GfxFont
* gfxFont
= state
->getFont();
1226 char*id
= getFontID(gfxFont
);
1227 msg("<verbose> Updating font to %s", FIXNULL(id
));
1230 if(gfxFont
->getType() == fontType3
) {
1231 infofeature("Type3 fonts");
1233 updateTextMat(state
);
1236 static const char* dirseparator()
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
, '\\');
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
;
1265 global_fonts_next
= global_fonts
= f
;
1269 void addGlobalLanguageDir(const char*dir
)
1272 msg("<notice> NOT adding %s to language pack directories (not implemented with poppler)", dir
);
1274 msg("<notice> Adding %s to language pack directories", dir
);
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");
1284 msg("<error> Could not open %s", config_file
);
1287 globalParams
->parseFile(new GString(config_file
), fi
);
1292 void addGlobalFontDir(const char*dirname
)
1294 #ifdef HAVE_DIRENT_H
1295 DIR*dir
= opendir(dirname
);
1297 msg("<warning> Couldn't open directory %s", dirname
);
1303 ent
= readdir (dir
);
1307 char*name
= ent
->d_name
;
1313 if(!strncasecmp(&name
[l
-4], ".pfa", 4))
1315 if(!strncasecmp(&name
[l
-4], ".pfb", 4))
1317 if(!strncasecmp(&name
[l
-4], ".ttf", 4))
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
);
1328 msg("<notice> Added %s to font directories (%d fonts)", dirname
, fonts
);
1331 msg("<warning> No dirent.h");