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 */
25 #include "../../config.h"
29 #ifdef HAVE_SYS_STAT_H
32 #ifdef HAVE_FONTCONFIG
33 #include <fontconfig.h>
50 #include "OutputDev.h"
53 #include "CharCodeToUnicode.h"
54 #include "NameToUnicodeTable.h"
55 #include "GlobalParams.h"
56 #include "FoFiType1C.h"
57 #include "FoFiTrueType.h"
59 #include "GFXOutputDev.h"
61 //swftools header files
63 #include "../gfxdevice.h"
64 #include "../gfxtools.h"
65 #include "../gfxfont.h"
69 typedef struct _fontfile
76 static fontfile_t fonts[2048];
77 static int fontnum = 0;
81 static char* lastfontdir = 0;
87 {"Times-Roman", "n021003l"},
88 {"Times-Italic", "n021023l"},
89 {"Times-Bold", "n021004l"},
90 {"Times-BoldItalic", "n021024l"},
91 {"Helvetica", "n019003l"},
92 {"Helvetica-Oblique", "n019023l"},
93 {"Helvetica-Bold", "n019004l"},
94 {"Helvetica-BoldOblique", "n019024l"},
95 {"Courier", "n022003l"},
96 {"Courier-Oblique", "n022023l"},
97 {"Courier-Bold", "n022004l"},
98 {"Courier-BoldOblique", "n022024l"},
99 {"Symbol", "s050000l"},
100 {"ZapfDingbats", "d050000l"}};
102 GFXOutputState::GFXOutputState() {
104 this->textRender = 0;
107 GBool GFXOutputDev::interpretType3Chars()
112 typedef struct _drawnchar
130 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
131 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
136 free(chars);chars = 0;
143 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
147 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
150 chars[num_chars].x = x;
151 chars[num_chars].y = y;
152 chars[num_chars].color = color;
153 chars[num_chars].charid = charid;
157 static char*getFontID(GfxFont*font);
159 GFXOutputDev::GFXOutputDev(parameter_t*p)
162 this->textmodeinfo = 0;
166 this->type3active = 0;
169 this->substitutepos = 0;
170 this->type3Warning = 0;
171 this->user_movex = 0;
172 this->user_movey = 0;
175 this->user_clipx1 = 0;
176 this->user_clipy1 = 0;
177 this->user_clipx2 = 0;
178 this->user_clipy2 = 0;
179 this->current_text_stroke = 0;
180 this->current_text_clip = 0;
182 this->outer_clip_box = 0;
184 this->pagebuflen = 0;
187 this->forceType0Fonts=1;
188 this->config_use_fontconfig=1;
190 this->parameters = p;
192 /* configure device */
194 if(!strcmp(p->name,"forceType0Fonts")) {
195 this->forceType0Fonts = atoi(p->value);
196 } else if(!strcmp(p->name,"fontconfig")) {
197 this->config_use_fontconfig = atoi(p->value);
203 void GFXOutputDev::setDevice(gfxdevice_t*dev)
205 parameter_t*p = this->parameters;
207 /* TODO: get rid of this */
211 this->device->setparameter(this->device, p->name, p->value);
217 void GFXOutputDev::setMove(int x,int y)
219 this->user_movex = x;
220 this->user_movey = y;
223 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
225 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
226 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
228 this->user_clipx1 = x1;
229 this->user_clipy1 = y1;
230 this->user_clipx2 = x2;
231 this->user_clipy2 = y2;
234 static char*getFontID(GfxFont*font)
236 Ref*ref = font->getID();
237 GString*gstr = font->getName();
238 char* fname = gstr==0?0:gstr->getCString();
241 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
243 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
248 static char*getFontName(GfxFont*font)
251 GString*gstr = font->getName();
252 char* fname = gstr==0?0:gstr->getCString();
256 sprintf(buf, "UFONT%d", r->num);
257 fontid = strdup(buf);
259 fontid = strdup(fname);
263 char* plus = strchr(fontid, '+');
264 if(plus && plus < &fontid[strlen(fontid)-1]) {
265 fontname = strdup(plus+1);
267 fontname = strdup(fontid);
273 static char mybuf[1024];
274 static char* gfxstate2str(GfxState *state)
278 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
285 if(state->getX1()!=0.0)
286 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
287 if(state->getY1()!=0.0)
288 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
289 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
290 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
291 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
292 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
293 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
294 state->getFillColor()->c[0], state->getFillColor()->c[1]);
295 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
296 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
297 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
298 state->getFillColor()->c[0], state->getFillColor()->c[1],
299 state->getFillColor()->c[2], state->getFillColor()->c[3],
300 state->getFillColor()->c[4], state->getFillColor()->c[5],
301 state->getFillColor()->c[6], state->getFillColor()->c[7]);
302 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
303 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
304 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
305 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
306 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
307 state->getFillRGB(&rgb);
308 if(rgb.r || rgb.g || rgb.b)
309 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
310 state->getStrokeRGB(&rgb);
311 if(rgb.r || rgb.g || rgb.b)
312 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
313 if(state->getFillColorSpace()->getNComps()>1)
314 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
315 if(state->getStrokeColorSpace()->getNComps()>1)
316 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
317 if(state->getFillPattern())
318 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
319 if(state->getStrokePattern())
320 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
322 if(state->getFillOpacity()!=1.0)
323 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
324 if(state->getStrokeOpacity()!=1.0)
325 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
327 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
332 state->getLineDash(&dash, &length, &start);
336 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
337 for(t=0;t<length;t++) {
338 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
340 bufpos+=sprintf(bufpos,"]");
343 if(state->getFlatness()!=1)
344 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
345 if(state->getLineJoin()!=0)
346 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
347 if(state->getLineJoin()!=0)
348 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
349 if(state->getLineJoin()!=0)
350 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
352 if(state->getFont() && getFontID(state->getFont()))
353 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
354 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
355 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
356 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
357 if(state->getCharSpace())
358 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
359 if(state->getWordSpace())
360 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
361 if(state->getHorizScaling()!=1.0)
362 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
363 if(state->getLeading())
364 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
366 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
367 if(state->getRender())
368 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
369 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
370 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
371 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
372 if(state->getLineX())
373 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
374 if(state->getLineY())
375 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
376 bufpos+=sprintf(bufpos," ");
380 static void dumpFontInfo(char*loglevel, GfxFont*font);
381 static int lastdumps[1024];
382 static int lastdumppos = 0;
387 static void showFontError(GfxFont*font, int nr)
391 for(t=0;t<lastdumppos;t++)
392 if(lastdumps[t] == r->num)
396 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
397 lastdumps[lastdumppos++] = r->num;
399 msg("<warning> The following font caused problems:");
401 msg("<warning> The following font caused problems (substituting):");
403 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
404 dumpFontInfo("<warning>", font);
407 static void dumpFontInfo(char*loglevel, GfxFont*font)
409 char* id = getFontID(font);
410 char* name = getFontName(font);
411 Ref* r=font->getID();
412 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
414 GString*gstr = font->getTag();
416 msg("%s| Tag: %s\n", loglevel, id);
418 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
420 GfxFontType type=font->getType();
422 case fontUnknownType:
423 msg("%s| Type: unknown\n",loglevel);
426 msg("%s| Type: 1\n",loglevel);
429 msg("%s| Type: 1C\n",loglevel);
432 msg("%s| Type: 3\n",loglevel);
435 msg("%s| Type: TrueType\n",loglevel);
438 msg("%s| Type: CIDType0\n",loglevel);
441 msg("%s| Type: CIDType0C\n",loglevel);
444 msg("%s| Type: CIDType2\n",loglevel);
449 GBool embedded = font->getEmbeddedFontID(&embRef);
451 if(font->getEmbeddedFontName()) {
452 embeddedName = font->getEmbeddedFontName()->getCString();
455 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
457 gstr = font->getExtFontFile();
459 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
461 // Get font descriptor flags.
462 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
463 if(font->isSerif()) msg("%s| is serif\n", loglevel);
464 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
465 if(font->isItalic()) msg("%s| is italic\n", loglevel);
466 if(font->isBold()) msg("%s| is bold\n", loglevel);
472 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
473 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
476 void dump_outline(gfxline_t*line)
479 if(line->type == gfx_moveTo) {
480 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
481 } else if(line->type == gfx_lineTo) {
482 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
483 } else if(line->type == gfx_splineTo) {
484 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
490 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
492 int num = path->getNumSubpaths();
495 double lastx=0,lasty=0,posx=0,posy=0;
498 msg("<warning> empty path");
502 gfxdrawer_target_gfxline(&draw);
504 for(t = 0; t < num; t++) {
505 GfxSubpath *subpath = path->getSubpath(t);
506 int subnum = subpath->getNumPoints();
507 double bx=0,by=0,cx=0,cy=0;
509 for(s=0;s<subnum;s++) {
512 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
517 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
518 draw.lineTo(&draw, lastx, lasty);
520 draw.moveTo(&draw, x,y);
525 } else if(subpath->getCurve(s) && cpos==0) {
529 } else if(subpath->getCurve(s) && cpos==1) {
537 draw.lineTo(&draw, x,y);
539 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
546 /* fix non-closed lines */
547 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
548 draw.lineTo(&draw, lastx, lasty);
550 gfxline_t*result = (gfxline_t*)draw.result(&draw);
554 /*----------------------------------------------------------------------------
555 * Primitive Graphic routines
556 *----------------------------------------------------------------------------*/
558 void GFXOutputDev::stroke(GfxState *state)
560 GfxPath * path = state->getPath();
561 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
562 strokeGfxline(state, line);
566 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
568 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
569 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
570 double miterLimit = state->getMiterLimit();
571 double width = state->getTransformedLineWidth();
574 double opaq = state->getStrokeOpacity();
576 state->getFillRGB(&rgb);
578 state->getStrokeRGB(&rgb);
580 col.r = colToByte(rgb.r);
581 col.g = colToByte(rgb.g);
582 col.b = colToByte(rgb.b);
583 col.a = (unsigned char)(opaq*255);
585 gfx_capType capType = gfx_capRound;
586 if(lineCap == 0) capType = gfx_capButt;
587 else if(lineCap == 1) capType = gfx_capRound;
588 else if(lineCap == 2) capType = gfx_capSquare;
590 gfx_joinType joinType = gfx_joinRound;
591 if(lineJoin == 0) joinType = gfx_joinMiter;
592 else if(lineJoin == 1) joinType = gfx_joinRound;
593 else if(lineJoin == 2) joinType = gfx_joinBevel;
596 double dashphase = 0;
598 state->getLineDash(&ldash, &dashnum, &dashphase);
602 if(dashnum && ldash) {
603 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
607 msg("<trace> %d dashes", dashnum);
608 msg("<trace> | phase: %f", dashphase);
609 for(t=0;t<dashnum;t++) {
611 msg("<trace> | d%-3d: %f", t, ldash[t]);
614 if(getLogLevel() >= LOGLEVEL_TRACE) {
618 line2 = gfxtool_dash_line(line, dash, dashphase);
621 msg("<trace> After dashing:");
624 if(getLogLevel() >= LOGLEVEL_TRACE) {
625 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
627 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
628 lineCap==0?"butt": (lineJoin==1?"round":"square"),
630 col.r,col.g,col.b,col.a
635 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
636 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
646 gfxcolor_t getFillColor(GfxState * state)
649 double opaq = state->getFillOpacity();
650 state->getFillRGB(&rgb);
652 col.r = colToByte(rgb.r);
653 col.g = colToByte(rgb.g);
654 col.b = colToByte(rgb.b);
655 col.a = (unsigned char)(opaq*255);
659 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
661 gfxcolor_t col = getFillColor(state);
663 if(getLogLevel() >= LOGLEVEL_TRACE) {
664 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
668 device->fill(device, line, &col);
670 void GFXOutputDev::fill(GfxState *state)
672 GfxPath * path = state->getPath();
673 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
674 fillGfxLine(state, line);
677 void GFXOutputDev::eoFill(GfxState *state)
679 GfxPath * path = state->getPath();
680 gfxcolor_t col = getFillColor(state);
682 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
684 if(getLogLevel() >= LOGLEVEL_TRACE) {
685 msg("<trace> eofill\n");
689 device->fill(device, line, &col);
693 void GFXOutputDev::clip(GfxState *state)
695 GfxPath * path = state->getPath();
696 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
697 clipToGfxLine(state, line);
701 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
703 if(getLogLevel() >= LOGLEVEL_TRACE) {
704 msg("<trace> clip\n");
708 device->startclip(device, line);
709 states[statepos].clipping++;
711 void GFXOutputDev::eoClip(GfxState *state)
713 GfxPath * path = state->getPath();
714 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
716 if(getLogLevel() >= LOGLEVEL_TRACE) {
717 msg("<trace> eoclip\n");
721 device->startclip(device, line);
722 states[statepos].clipping++;
726 void GFXOutputDev::endframe()
729 device->endclip(device);
733 device->endpage(device);
736 void GFXOutputDev::finish()
740 device->endclip(device);
746 GFXOutputDev::~GFXOutputDev()
751 free(this->pages); this->pages = 0;
754 fontlist_t*l = this->fontlist;
756 fontlist_t*next = l->next;
758 gfxfont_free(l->font);
759 free(l->filename);l->filename=0;
765 GBool GFXOutputDev::upsideDown()
769 GBool GFXOutputDev::useDrawChar()
774 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
775 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
777 #define RENDER_FILL 0
778 #define RENDER_STROKE 1
779 #define RENDER_FILLSTROKE 2
780 #define RENDER_INVISIBLE 3
781 #define RENDER_CLIP 4
783 static char tmp_printstr[4096];
784 char* makeStringPrintable(char*str)
786 int len = strlen(str);
801 tmp_printstr[len++] = '.';
802 tmp_printstr[len++] = '.';
803 tmp_printstr[len++] = '.';
805 tmp_printstr[len] = 0;
810 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
815 /* find out char name from unicode index
816 TODO: should be precomputed
818 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
819 if(nameToUnicodeTab[t].u == u) {
820 uniname = nameToUnicodeTab[t].name;
828 for(t=0;t<font->num_glyphs;t++) {
829 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
830 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
834 /* if we didn't find the character, maybe
835 we can find the capitalized version */
836 for(t=0;t<font->num_glyphs;t++) {
837 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
838 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
846 for(t=0;t<font->num_glyphs;t++) {
847 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
848 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
852 /* if we didn't find the character, maybe
853 we can find the capitalized version */
854 for(t=0;t<font->num_glyphs;t++) {
855 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
856 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
862 /* try to use the unicode id */
863 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
864 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
865 return font->unicode2glyph[u];
868 if(charnr>=0 && charnr<font->num_glyphs) {
869 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
877 void GFXOutputDev::beginString(GfxState *state, GString *s)
879 int render = state->getRender();
880 if(current_text_stroke) {
881 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
884 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
885 double m11,m21,m12,m22;
886 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
887 state->getFontTransMat(&m11, &m12, &m21, &m22);
888 m11 *= state->getHorizScaling();
889 m21 *= state->getHorizScaling();
891 this->current_font_matrix.m00 = m11 / 1024.0;
892 this->current_font_matrix.m01 = m12 / 1024.0;
893 this->current_font_matrix.m10 = -m21 / 1024.0;
894 this->current_font_matrix.m11 = -m22 / 1024.0;
895 this->current_font_matrix.tx = 0;
896 this->current_font_matrix.ty = 0;
898 gfxmatrix_t m = this->current_font_matrix;
900 /*if(render != 3 && render != 0)
901 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
902 states[statepos].textRender = render;
905 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
906 double dx, double dy,
907 double originX, double originY,
908 CharCode c, int nBytes, Unicode *_u, int uLen)
910 int render = state->getRender();
911 // check for invisible text -- this is used by Acrobat Capture
913 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
917 if(states[statepos].textRender != render)
918 msg("<error> Internal error: drawChar.render!=beginString.render");
920 gfxcolor_t col = getFillColor(state);
922 Gushort *CIDToGIDMap = 0;
923 GfxFont*font = state->getFont();
925 if(font->getType() == fontType3) {
926 /* type 3 chars are passed as graphics */
927 msg("<debug> type3 char at %f/%f", x, y);
937 if(font->isCIDFont()) {
938 GfxCIDFont*cfont = (GfxCIDFont*)font;
940 if(font->getType() == fontCIDType2)
941 CIDToGIDMap = cfont->getCIDToGID();
944 font8 = (Gfx8BitFont*)font;
945 char**enc=font8->getEncoding();
949 msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
952 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
958 charid = getGfxCharID(current_gfxfont, c, name, u);
960 charid = getGfxCharID(current_gfxfont, c, name, -1);
963 /* multiple unicodes- should usually map to a ligature.
964 if the ligature doesn't exist, we need to draw
965 the characters one-by-one. */
967 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
968 for(t=0;t<uLen;t++) {
969 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
975 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
976 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
980 gfxmatrix_t m = this->current_font_matrix;
981 state->transform(x, y, &m.tx, &m.ty);
982 m.tx += user_movex + clipmovex;
983 m.ty += user_movey + clipmovey;
985 if(render == RENDER_FILL) {
986 device->drawchar(device, current_gfxfont, charid, &col, &m);
988 msg("<debug> Drawing glyph %d as shape", charid);
990 msg("<notice> Some texts will be rendered as shape");
993 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
994 gfxline_t*tglyph = gfxline_clone(glyph);
995 gfxline_transform(tglyph, &m);
996 if((render&3) != RENDER_INVISIBLE) {
997 gfxline_t*add = gfxline_clone(tglyph);
998 current_text_stroke = gfxline_append(current_text_stroke, add);
1000 if(render&RENDER_CLIP) {
1001 gfxline_t*add = gfxline_clone(tglyph);
1002 current_text_clip = gfxline_append(current_text_clip, add);
1004 gfxline_free(tglyph);
1008 void GFXOutputDev::endString(GfxState *state)
1010 int render = state->getRender();
1011 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1012 if(states[statepos].textRender != render)
1013 msg("<error> Internal error: drawChar.render!=beginString.render");
1015 if(current_text_stroke) {
1016 /* fillstroke and stroke text rendering objects we can process right
1017 now (as there may be texts of other rendering modes in this
1018 text object)- clipping objects have to wait until endTextObject,
1020 device->setparameter(device, "mark","TXT");
1021 if((render&3) == RENDER_FILL) {
1022 fillGfxLine(state, current_text_stroke);
1023 gfxline_free(current_text_stroke);
1024 current_text_stroke = 0;
1025 } else if((render&3) == RENDER_FILLSTROKE) {
1026 fillGfxLine(state, current_text_stroke);
1027 strokeGfxline(state, current_text_stroke);
1028 gfxline_free(current_text_stroke);
1029 current_text_stroke = 0;
1030 } else if((render&3) == RENDER_STROKE) {
1031 strokeGfxline(state, current_text_stroke);
1032 gfxline_free(current_text_stroke);
1033 current_text_stroke = 0;
1035 device->setparameter(device, "mark","");
1039 void GFXOutputDev::endTextObject(GfxState *state)
1041 int render = state->getRender();
1042 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1043 if(states[statepos].textRender != render)
1044 msg("<error> Internal error: drawChar.render!=beginString.render");
1046 if(current_text_clip) {
1047 device->setparameter(device, "mark","TXT");
1048 clipToGfxLine(state, current_text_clip);
1049 device->setparameter(device, "mark","");
1050 gfxline_free(current_text_clip);
1051 current_text_clip = 0;
1055 /* the logic seems to be as following:
1056 first, beginType3Char is called, with the charcode and the coordinates.
1057 if this function returns true, it already knew about the char and has now drawn it.
1058 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1059 the all draw operations until endType3Char are part of the char (which in this moment is
1060 at the position first passed to beginType3Char). the char ends with endType3Char.
1062 The drawing operations between beginType3Char and endType3Char are somewhat different to
1063 the normal ones. For example, the fillcolor equals the stroke color.
1066 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1068 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1070 /* the character itself is going to be passed using the draw functions */
1071 return gFalse; /* gTrue= is_in_cache? */
1074 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1075 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1077 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1078 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1082 void GFXOutputDev::endType3Char(GfxState *state)
1085 msg("<debug> endType3Char");
1088 void GFXOutputDev::startFrame(int width, int height)
1090 if(outer_clip_box) {
1091 device->endclip(device);
1095 device->startpage(device, width, height);
1098 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1100 this->currentpage = pageNum;
1102 int rot = doc->getPageRotate(1);
1105 gfxline_t clippath[5];
1107 white.r = white.g = white.b = white.a = 255;
1109 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1110 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1111 Use CropBox, not MediaBox, as page size
1118 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1119 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1121 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1122 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1124 this->clipmovex = -(int)x1;
1125 this->clipmovey = -(int)y1;
1127 /* apply user clip box */
1128 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1129 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1130 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1131 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1132 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1133 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1136 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1138 msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex + clipmovex, user_movey + clipmovey);
1140 msg("<verbose> page is rotated %d degrees\n", rot);
1142 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1143 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1144 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1145 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1146 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1147 device->startclip(device, clippath); outer_clip_box = 1;
1148 device->fill(device, clippath, &white);
1151 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1153 double x1, y1, x2, y2, w;
1154 gfxline_t points[5];
1157 msg("<debug> drawlink\n");
1159 link->getRect(&x1, &y1, &x2, &y2);
1160 cvtUserToDev(x1, y1, &x, &y);
1161 points[0].type = gfx_moveTo;
1162 points[0].x = points[4].x = x + user_movex + clipmovex;
1163 points[0].y = points[4].y = y + user_movey + clipmovey;
1164 points[0].next = &points[1];
1165 cvtUserToDev(x2, y1, &x, &y);
1166 points[1].type = gfx_lineTo;
1167 points[1].x = x + user_movex + clipmovex;
1168 points[1].y = y + user_movey + clipmovey;
1169 points[1].next = &points[2];
1170 cvtUserToDev(x2, y2, &x, &y);
1171 points[2].type = gfx_lineTo;
1172 points[2].x = x + user_movex + clipmovex;
1173 points[2].y = y + user_movey + clipmovey;
1174 points[2].next = &points[3];
1175 cvtUserToDev(x1, y2, &x, &y);
1176 points[3].type = gfx_lineTo;
1177 points[3].x = x + user_movex + clipmovex;
1178 points[3].y = y + user_movey + clipmovey;
1179 points[3].next = &points[4];
1180 cvtUserToDev(x1, y1, &x, &y);
1181 points[4].type = gfx_lineTo;
1182 points[4].x = x + user_movex + clipmovex;
1183 points[4].y = y + user_movey + clipmovey;
1186 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1187 points[0].x, points[0].y,
1188 points[1].x, points[1].y,
1189 points[2].x, points[2].y,
1190 points[3].x, points[3].y);
1192 LinkAction*action=link->getAction();
1198 msg("<trace> drawlink action=%d\n", action->getKind());
1199 switch(action->getKind())
1203 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1204 LinkDest *dest=NULL;
1205 if (ha->getDest()==NULL)
1206 dest=catalog->findDest(ha->getNamedDest());
1207 else dest=ha->getDest();
1209 if (dest->isPageRef()){
1210 Ref pageref=dest->getPageRef();
1211 page=catalog->findPage(pageref.num,pageref.gen);
1213 else page=dest->getPageNum();
1214 sprintf(buf, "%d", page);
1221 LinkGoToR*l = (LinkGoToR*)action;
1222 GString*g = l->getFileName();
1224 s = strdup(g->getCString());
1226 /* if the GoToR link has no filename, then
1227 try to find a refernce in the *local*
1229 GString*g = l->getNamedDest();
1231 s = strdup(g->getCString());
1237 LinkNamed*l = (LinkNamed*)action;
1238 GString*name = l->getName();
1240 s = strdup(name->lowerCase()->getCString());
1241 named = name->getCString();
1244 if(strstr(s, "next") || strstr(s, "forward"))
1246 page = currentpage + 1;
1248 else if(strstr(s, "prev") || strstr(s, "back"))
1250 page = currentpage - 1;
1252 else if(strstr(s, "last") || strstr(s, "end"))
1254 if(pages && pagepos>0)
1255 page = pages[pagepos-1];
1257 else if(strstr(s, "first") || strstr(s, "top"))
1265 case actionLaunch: {
1267 LinkLaunch*l = (LinkLaunch*)action;
1268 GString * str = new GString(l->getFileName());
1269 GString * params = l->getParams();
1271 str->append(params);
1272 s = strdup(str->getCString());
1279 LinkURI*l = (LinkURI*)action;
1280 GString*g = l->getURI();
1282 url = g->getCString();
1287 case actionUnknown: {
1289 LinkUnknown*l = (LinkUnknown*)action;
1294 msg("<error> Unknown link type!\n");
1299 if(!s) s = strdup("-?-");
1301 msg("<trace> drawlink s=%s\n", s);
1303 if(!linkinfo && (page || s))
1305 msg("<notice> File contains links");
1313 for(t=1;t<=pagepos;t++) {
1314 if(pages[t]==page) {
1323 sprintf(buf, "page%d", lpage);
1324 device->drawlink(device, points, buf);
1328 device->drawlink(device, points, s);
1331 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1335 void GFXOutputDev::saveState(GfxState *state) {
1336 msg("<trace> saveState\n");
1339 msg("<error> Too many nested states in pdf.");
1343 states[statepos].clipping = 0; //? shouldn't this be the current value?
1344 states[statepos].textRender = states[statepos-1].textRender;
1347 void GFXOutputDev::restoreState(GfxState *state) {
1348 msg("<trace> restoreState\n");
1350 while(states[statepos].clipping) {
1351 device->endclip(device);
1352 states[statepos].clipping--;
1357 char* GFXOutputDev::searchFont(char*name)
1361 int is_standard_font = 0;
1363 msg("<verbose> SearchFont(%s)", name);
1365 /* see if it is a pdf standard font */
1366 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1368 if(!strcmp(name, pdf2t1map[i].pdffont))
1370 name = pdf2t1map[i].filename;
1371 is_standard_font = 1;
1375 /* look in all font files */
1376 for(i=0;i<fontnum;i++)
1378 if(strstr(fonts[i].filename, name))
1380 if(!fonts[i].used) {
1383 if(!is_standard_font)
1384 msg("<notice> Using %s for %s", fonts[i].filename, name);
1386 return strdup(fonts[i].filename);
1392 void GFXOutputDev::updateLineWidth(GfxState *state)
1394 double width = state->getTransformedLineWidth();
1395 //swfoutput_setlinewidth(&device, width);
1398 void GFXOutputDev::updateLineCap(GfxState *state)
1400 int c = state->getLineCap();
1403 void GFXOutputDev::updateLineJoin(GfxState *state)
1405 int j = state->getLineJoin();
1408 void GFXOutputDev::updateFillColor(GfxState *state)
1411 double opaq = state->getFillOpacity();
1412 state->getFillRGB(&rgb);
1414 //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1417 void GFXOutputDev::updateStrokeColor(GfxState *state)
1420 double opaq = state->getStrokeOpacity();
1421 state->getStrokeRGB(&rgb);
1422 //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1425 void FoFiWrite(void *stream, char *data, int len)
1427 int ret = fwrite(data, len, 1, (FILE*)stream);
1430 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1432 char*tmpFileName = NULL;
1438 Object refObj, strObj;
1440 tmpFileName = mktmpname(namebuf);
1443 ret = font->getEmbeddedFontID(&embRef);
1445 msg("<verbose> Didn't get embedded font id");
1446 /* not embedded- the caller should now search the font
1447 directories for this font */
1451 f = fopen(tmpFileName, "wb");
1453 msg("<error> Couldn't create temporary Type 1 font file");
1457 /*if(font->isCIDFont()) {
1458 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1459 GString c = cidFont->getCollection();
1460 msg("<notice> Collection: %s", c.getCString());
1463 //if (font->getType() == fontType1C) {
1464 if (0) { //font->getType() == fontType1C) {
1465 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1467 msg("<error> Couldn't read embedded font file");
1470 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1472 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1473 //cvt->convertToCIDType0("test", f);
1474 //cvt->convertToType0("test", f);
1477 } else if(font->getType() == fontTrueType) {
1478 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1479 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1481 msg("<error> Couldn't read embedded font file");
1484 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1485 cvt->writeTTF(FoFiWrite, f);
1489 font->getEmbeddedFontID(&embRef);
1490 refObj.initRef(embRef.num, embRef.gen);
1491 refObj.fetch(ref, &strObj);
1493 strObj.streamReset();
1498 f4[t] = strObj.streamGetChar();
1499 f4c[t] = (char)f4[t];
1504 if(!strncmp(f4c, "true", 4)) {
1505 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1506 Change this on the fly */
1507 f4[0] = f4[2] = f4[3] = 0;
1515 while ((c = strObj.streamGetChar()) != EOF) {
1519 strObj.streamClose();
1524 return strdup(tmpFileName);
1527 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1529 char*name = getFontName(gfxFont);
1533 if(!this->config_use_fontconfig)
1536 #ifdef HAVE_FONTCONFIG
1537 FcPattern *pattern, *match;
1541 static int fcinitcalled = false;
1543 msg("<debug> searchForSuitableFont(%s)", name);
1545 // call init ony once
1546 if (!fcinitcalled) {
1547 msg("<debug> Initializing FontConfig...");
1548 fcinitcalled = true;
1550 msg("<debug> FontConfig Initialization failed. Disabling.");
1551 config_use_fontconfig = 0;
1554 msg("<debug> ...initialized FontConfig");
1557 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1558 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1559 if (gfxFont->isItalic()) // check for italic
1560 msg("<debug> FontConfig: Adding Italic Slant");
1561 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1562 if (gfxFont->isBold()) // check for bold
1563 msg("<debug> FontConfig: Adding Bold Weight");
1564 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1566 msg("<debug> FontConfig: Try to match...");
1567 // configure and match using the original font name
1568 FcConfigSubstitute(0, pattern, FcMatchPattern);
1569 FcDefaultSubstitute(pattern);
1570 match = FcFontMatch(0, pattern, &result);
1572 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1573 msg("<debug> FontConfig: family=%s", (char*)v);
1574 // if we get an exact match
1575 if (strcmp((char *)v, name) == 0) {
1576 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1577 filename = strdup((char*)v); // mem leak
1578 char *nfn = strrchr(filename, '/');
1579 if(nfn) fontname = strdup(nfn+1);
1580 else fontname = filename;
1582 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1584 // initialize patterns
1585 FcPatternDestroy(pattern);
1586 FcPatternDestroy(match);
1588 // now match against serif etc.
1589 if (gfxFont->isSerif()) {
1590 msg("<debug> FontConfig: Create Serif Family Pattern");
1591 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1592 } else if (gfxFont->isFixedWidth()) {
1593 msg("<debug> FontConfig: Create Monospace Family Pattern");
1594 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1596 msg("<debug> FontConfig: Create Sans Family Pattern");
1597 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1601 if (gfxFont->isItalic()) {
1602 msg("<debug> FontConfig: Adding Italic Slant");
1603 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1606 if (gfxFont->isBold()) {
1607 msg("<debug> FontConfig: Adding Bold Weight");
1608 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1611 msg("<debug> FontConfig: Try to match... (2)");
1612 // configure and match using serif etc
1613 FcConfigSubstitute (0, pattern, FcMatchPattern);
1614 FcDefaultSubstitute (pattern);
1615 match = FcFontMatch (0, pattern, &result);
1617 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1618 filename = strdup((char*)v); // mem leak
1619 char *nfn = strrchr(filename, '/');
1620 if(nfn) fontname = strdup(nfn+1);
1621 else fontname = filename;
1623 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1627 //printf("FONTCONFIG: pattern");
1628 //FcPatternPrint(pattern);
1629 //printf("FONTCONFIG: match");
1630 //FcPatternPrint(match);
1632 FcPatternDestroy(pattern);
1633 FcPatternDestroy(match);
1635 pdfswf_addfont(filename);
1642 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1644 char*fontname = 0, *filename = 0;
1645 msg("<notice> substituteFont(%s)", oldname);
1647 if(!(fontname = searchForSuitableFont(gfxFont))) {
1648 fontname = "Times-Roman";
1650 filename = searchFont(fontname);
1652 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1656 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1657 msg("<fatal> Too many fonts in file.");
1661 substitutesource[substitutepos] = strdup(oldname); //mem leak
1662 substitutetarget[substitutepos] = fontname;
1663 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1666 return strdup(filename); //mem leak
1669 void unlinkfont(char* filename)
1676 if(!strncmp(&filename[l-4],".afm",4)) {
1677 memcpy(&filename[l-4],".pfb",4);
1679 memcpy(&filename[l-4],".pfa",4);
1681 memcpy(&filename[l-4],".afm",4);
1684 if(!strncmp(&filename[l-4],".pfa",4)) {
1685 memcpy(&filename[l-4],".afm",4);
1687 memcpy(&filename[l-4],".pfa",4);
1690 if(!strncmp(&filename[l-4],".pfb",4)) {
1691 memcpy(&filename[l-4],".afm",4);
1693 memcpy(&filename[l-4],".pfb",4);
1698 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1704 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1707 fontlist_t*last=0,*l = this->fontlist;
1710 msg("<error> Internal Error: FontID is null");
1712 /* TODO: should this be part of the state? */
1715 if(!strcmp(l->font->id, id)) {
1716 current_gfxfont = l->font;
1718 device->addfont(device, current_gfxfont);
1723 if(!filename) return 0;
1725 /* A font size of e.g. 9 means the font will be scaled down by
1726 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1727 we have to divide 0.05 by (fontsize/1024)
1729 double quality = (1024 * 0.05) / maxSize;
1731 msg("<verbose> Loading %s...", filename);
1732 font = gfxfont_load(id, filename, quality);
1734 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1737 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1741 l->filename = strdup(filename);
1743 current_gfxfont = l->font;
1749 device->addfont(device, current_gfxfont);
1753 void GFXOutputDev::updateFont(GfxState *state)
1755 GfxFont*gfxFont = state->getFont();
1761 char * fontid = getFontID(gfxFont);
1762 char * fontname = getFontName(gfxFont);
1764 double maxSize = 1.0;
1767 maxSize = this->info->getMaximumFontSize(fontid);
1771 /* first, look if we substituted this font before-
1772 this way, we don't initialize the T1 Fonts
1774 for(t=0;t<substitutepos;t++) {
1775 if(!strcmp(fontid, substitutesource[t])) {
1776 free(fontid);fontid=0;
1777 fontid = strdup(substitutetarget[t]);
1782 /* second, see if this is a font which was used before-
1783 if so, we are done */
1784 if(setGfxFont(fontid, fontname, 0, 0)) {
1789 /* if(swfoutput_queryfont(&device, fontid))
1790 swfoutput_setfont(&device, fontid, 0);
1792 msg("<debug> updateFont(%s) [cached]", fontid);
1796 // look for Type 3 font
1797 if (gfxFont->getType() == fontType3) {
1799 type3Warning = gTrue;
1800 showFontError(gfxFont, 2);
1807 /* now either load the font, or find a substitution */
1810 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1815 (gfxFont->getType() == fontType1 ||
1816 gfxFont->getType() == fontType1C ||
1817 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1818 gfxFont->getType() == fontTrueType ||
1819 gfxFont->getType() == fontCIDType2
1822 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1823 if(!fileName) showFontError(gfxFont,0);
1826 fileName = searchFont(fontname);
1827 if(!fileName) showFontError(gfxFont,0);
1830 char * fontname = getFontName(gfxFont);
1831 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1834 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1836 msg("<warning> Try specifying one or more font directories");
1838 fileName = substituteFont(gfxFont, fontid);
1841 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1842 msg("<notice> Font is now %s (%s)", fontid, fileName);
1846 msg("<error> Couldn't set font %s\n", fontid);
1852 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1853 dumpFontInfo("<verbose>", gfxFont);
1855 //swfoutput_setfont(&device, fontid, fileName);
1857 if(!setGfxFont(fontid, fontname, 0, 0)) {
1858 setGfxFont(fontid, fontname, fileName, maxSize);
1862 unlinkfont(fileName);
1872 #define SQR(x) ((x)*(x))
1874 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1876 if((newwidth<2 || newheight<2) ||
1877 (width<=newwidth || height<=newheight))
1879 unsigned char*newdata;
1881 newdata= (unsigned char*)malloc(newwidth*newheight);
1883 double fx = (double)(width)/newwidth;
1884 double fy = (double)(height)/newheight;
1886 int blocksize = (int)(8192/(fx*fy));
1887 int r = 8192*256/palettesize;
1888 for(x=0;x<newwidth;x++) {
1889 double ex = px + fx;
1890 int fromx = (int)px;
1892 int xweight1 = (int)(((fromx+1)-px)*256);
1893 int xweight2 = (int)((ex-tox)*256);
1895 for(y=0;y<newheight;y++) {
1896 double ey = py + fy;
1897 int fromy = (int)py;
1899 int yweight1 = (int)(((fromy+1)-py)*256);
1900 int yweight2 = (int)((ey-toy)*256);
1903 for(xx=fromx;xx<=tox;xx++)
1904 for(yy=fromy;yy<=toy;yy++) {
1905 int b = 1-data[width*yy+xx];
1907 if(xx==fromx) weight = (weight*xweight1)/256;
1908 if(xx==tox) weight = (weight*xweight2)/256;
1909 if(yy==fromy) weight = (weight*yweight1)/256;
1910 if(yy==toy) weight = (weight*yweight2)/256;
1913 //if(a) a=(palettesize-1)*r/blocksize;
1914 newdata[y*newwidth+x] = (a*blocksize)/r;
1922 #define IMAGE_TYPE_JPEG 0
1923 #define IMAGE_TYPE_LOSSLESS 1
1925 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1926 double x1,double y1,
1927 double x2,double y2,
1928 double x3,double y3,
1929 double x4,double y4, int type)
1931 gfxcolor_t*newpic=0;
1933 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1934 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1936 gfxline_t p1,p2,p3,p4,p5;
1937 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1938 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1939 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1940 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1941 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1943 {p1.x = (int)(p1.x*20)/20.0;
1944 p1.y = (int)(p1.y*20)/20.0;
1945 p2.x = (int)(p2.x*20)/20.0;
1946 p2.y = (int)(p2.y*20)/20.0;
1947 p3.x = (int)(p3.x*20)/20.0;
1948 p3.y = (int)(p3.y*20)/20.0;
1949 p4.x = (int)(p4.x*20)/20.0;
1950 p4.y = (int)(p4.y*20)/20.0;
1951 p5.x = (int)(p5.x*20)/20.0;
1952 p5.y = (int)(p5.y*20)/20.0;
1959 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1960 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1965 img.data = (gfxcolor_t*)data;
1969 if(type == IMAGE_TYPE_JPEG)
1970 /* TODO: pass image_dpi to device instead */
1971 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1973 dev->fillbitmap(dev, &p1, &img, &m, 0);
1976 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1977 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1979 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1982 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1983 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1985 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1989 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1990 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1991 GBool inlineImg, int mask, int*maskColors,
1992 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1994 double x1,y1,x2,y2,x3,y3,x4,y4;
1995 ImageStream *imgStr;
2000 unsigned char* maskbitmap = 0;
2003 ncomps = colorMap->getNumPixelComps();
2004 bits = colorMap->getBits();
2009 unsigned char buf[8];
2010 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2012 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2013 imgMaskStr->reset();
2014 unsigned char pal[256];
2015 int n = 1 << colorMap->getBits();
2020 maskColorMap->getGray(pixBuf, &gray);
2021 pal[t] = colToByte(gray);
2023 for (y = 0; y < maskHeight; y++) {
2024 for (x = 0; x < maskWidth; x++) {
2025 imgMaskStr->getPixel(buf);
2026 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2031 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2032 imgMaskStr->reset();
2033 for (y = 0; y < maskHeight; y++) {
2034 for (x = 0; x < maskWidth; x++) {
2035 imgMaskStr->getPixel(buf);
2037 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2045 imgStr = new ImageStream(str, width, ncomps,bits);
2048 if(!width || !height || (height<=1 && width<=1))
2050 msg("<verbose> Ignoring %d by %d image", width, height);
2051 unsigned char buf[8];
2053 for (y = 0; y < height; ++y)
2054 for (x = 0; x < width; ++x) {
2055 imgStr->getPixel(buf);
2063 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2064 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2065 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2066 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2069 if(!pbminfo && !(str->getKind()==strDCT)) {
2071 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2075 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2077 if(!jpeginfo && (str->getKind()==strDCT)) {
2078 msg("<notice> file contains jpeg pictures");
2084 unsigned char buf[8];
2086 unsigned char*pic = new unsigned char[width*height];
2087 gfxcolor_t pal[256];
2089 state->getFillRGB(&rgb);
2091 memset(pal,255,sizeof(pal));
2092 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2093 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2094 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2095 pal[0].a = 255; pal[1].a = 0;
2098 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2099 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2100 for (y = 0; y < height; ++y)
2101 for (x = 0; x < width; ++x)
2103 imgStr->getPixel(buf);
2106 pic[width*y+x] = buf[0];
2109 /* the size of the drawn image is added to the identifier
2110 as the same image may require different bitmaps if displayed
2111 at different sizes (due to antialiasing): */
2114 unsigned char*pic2 = 0;
2117 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2126 height = realheight;
2130 /* make a black/white palette */
2132 float r = 255/(numpalette-1);
2134 for(t=0;t<numpalette;t++) {
2135 pal[t].r = colToByte(rgb.r);
2136 pal[t].g = colToByte(rgb.g);
2137 pal[t].b = colToByte(rgb.b);
2138 pal[t].a = (unsigned char)(t*r);
2142 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2143 for (y = 0; y < height; ++y) {
2144 for (x = 0; x < width; ++x) {
2145 pic2[width*y+x] = pal[pic[y*width+x]];
2148 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2152 if(maskbitmap) free(maskbitmap);
2158 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2159 gfxcolor_t*pic=new gfxcolor_t[width*height];
2160 for (y = 0; y < height; ++y) {
2161 for (x = 0; x < width; ++x) {
2162 imgStr->getPixel(pixBuf);
2163 colorMap->getRGB(pixBuf, &rgb);
2164 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2165 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2166 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2167 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2169 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2173 if(str->getKind()==strDCT)
2174 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2176 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2179 if(maskbitmap) free(maskbitmap);
2182 gfxcolor_t*pic=new gfxcolor_t[width*height];
2183 gfxcolor_t pal[256];
2184 int n = 1 << colorMap->getBits();
2186 for(t=0;t<256;t++) {
2188 colorMap->getRGB(pixBuf, &rgb);
2190 {/*if(maskColors && *maskColors==t) {
2191 msg("<notice> Color %d is transparent", t);
2192 if (imgData->maskColors) {
2194 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2195 if (pix[i] < imgData->maskColors[2*i] ||
2196 pix[i] > imgData->maskColors[2*i+1]) {
2211 pal[t].r = (unsigned char)(colToByte(rgb.r));
2212 pal[t].g = (unsigned char)(colToByte(rgb.g));
2213 pal[t].b = (unsigned char)(colToByte(rgb.b));
2214 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2217 for (y = 0; y < height; ++y) {
2218 for (x = 0; x < width; ++x) {
2219 imgStr->getPixel(pixBuf);
2220 pic[width*y+x] = pal[pixBuf[0]];
2222 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2226 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2230 if(maskbitmap) free(maskbitmap);
2235 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2236 int width, int height, GBool invert,
2239 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2240 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2243 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2244 int width, int height, GfxImageColorMap *colorMap,
2245 int *maskColors, GBool inlineImg)
2247 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2248 colorMap?"colorMap":"no colorMap",
2249 maskColors?"maskColors":"no maskColors",
2252 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2253 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2254 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2257 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2258 int width, int height,
2259 GfxImageColorMap *colorMap,
2260 Stream *maskStr, int maskWidth, int maskHeight,
2263 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2264 colorMap?"colorMap":"no colorMap",
2265 maskWidth, maskHeight);
2267 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2268 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2269 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2272 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2273 int width, int height,
2274 GfxImageColorMap *colorMap,
2276 int maskWidth, int maskHeight,
2277 GfxImageColorMap *maskColorMap)
2279 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2280 colorMap?"colorMap":"no colorMap",
2281 maskWidth, maskHeight);
2283 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2284 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2285 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2288 static char* dirseparator()
2297 void addGlobalFont(char*filename)
2300 memset(&f, 0, sizeof(fontfile_t));
2301 f.filename = filename;
2302 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2303 msg("<verbose> Adding font \"%s\".", filename);
2304 fonts[fontnum++] = f;
2306 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2310 void addGlobalLanguageDir(char*dir)
2313 globalParams = new GlobalParams("");
2315 msg("<notice> Adding %s to language pack directories", dir);
2319 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2320 strcpy(config_file, dir);
2321 strcat(config_file, dirseparator());
2322 strcat(config_file, "add-to-xpdfrc");
2324 fi = fopen(config_file, "rb");
2326 msg("<error> Could not open %s", config_file);
2329 globalParams->parseFile(new GString(config_file), fi);
2333 void addGlobalFontDir(char*dirname)
2335 #ifdef HAVE_DIRENT_H
2336 msg("<notice> Adding %s to font directories", dirname);
2337 lastfontdir = strdup(dirname);
2338 DIR*dir = opendir(dirname);
2340 msg("<warning> Couldn't open directory %s\n", dirname);
2345 ent = readdir (dir);
2349 char*name = ent->d_name;
2355 if(!strncasecmp(&name[l-4], ".pfa", 4))
2357 if(!strncasecmp(&name[l-4], ".pfb", 4))
2359 if(!strncasecmp(&name[l-4], ".ttf", 4))
2363 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2364 strcpy(fontname, dirname);
2365 strcat(fontname, dirseparator());
2366 strcat(fontname, name);
2367 addGlobalFont(fontname);
2372 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2376 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2382 this->pagebuflen = 1024;
2383 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2384 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2386 while(pdfpage >= this->pagebuflen)
2388 int oldlen = this->pagebuflen;
2389 this->pagebuflen+=1024;
2390 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2391 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2394 this->pages[pdfpage] = outputpage;
2395 if(pdfpage>this->pagepos)
2396 this->pagepos = pdfpage;
2403 delete globalParams;globalParams=0;
2404 Object::memCheck(stderr);