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;
173 this->user_clipx1 = 0;
174 this->user_clipy1 = 0;
175 this->user_clipx2 = 0;
176 this->user_clipy2 = 0;
177 this->current_text_stroke = 0;
178 this->current_text_clip = 0;
180 this->outer_clip_box = 0;
182 this->pagebuflen = 0;
185 this->forceType0Fonts=1;
186 this->config_use_fontconfig=1;
188 this->parameters = p;
190 /* configure device */
192 if(!strcmp(p->name,"forceType0Fonts")) {
193 this->forceType0Fonts = atoi(p->value);
194 } else if(!strcmp(p->name,"fontconfig")) {
195 this->config_use_fontconfig = atoi(p->value);
201 void GFXOutputDev::setDevice(gfxdevice_t*dev)
203 parameter_t*p = this->parameters;
205 /* TODO: get rid of this */
209 this->device->setparameter(this->device, p->name, p->value);
215 void GFXOutputDev::setMove(int x,int y)
217 this->user_movex = x;
218 this->user_movey = y;
221 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
223 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
224 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
226 this->user_clipx1 = x1;
227 this->user_clipy1 = y1;
228 this->user_clipx2 = x2;
229 this->user_clipy2 = y2;
232 static char*getFontID(GfxFont*font)
234 Ref*ref = font->getID();
235 GString*gstr = font->getName();
236 char* fname = gstr==0?0:gstr->getCString();
239 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
241 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
246 static char*getFontName(GfxFont*font)
249 GString*gstr = font->getName();
250 char* fname = gstr==0?0:gstr->getCString();
254 sprintf(buf, "UFONT%d", r->num);
255 fontid = strdup(buf);
257 fontid = strdup(fname);
261 char* plus = strchr(fontid, '+');
262 if(plus && plus < &fontid[strlen(fontid)-1]) {
263 fontname = strdup(plus+1);
265 fontname = strdup(fontid);
271 static char mybuf[1024];
272 static char* gfxstate2str(GfxState *state)
276 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
283 if(state->getX1()!=0.0)
284 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
285 if(state->getY1()!=0.0)
286 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
287 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
288 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
289 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
290 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
291 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
292 state->getFillColor()->c[0], state->getFillColor()->c[1]);
293 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
294 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
295 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
296 state->getFillColor()->c[0], state->getFillColor()->c[1],
297 state->getFillColor()->c[2], state->getFillColor()->c[3],
298 state->getFillColor()->c[4], state->getFillColor()->c[5],
299 state->getFillColor()->c[6], state->getFillColor()->c[7]);
300 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
301 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
302 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
303 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
304 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
305 state->getFillRGB(&rgb);
306 if(rgb.r || rgb.g || rgb.b)
307 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
308 state->getStrokeRGB(&rgb);
309 if(rgb.r || rgb.g || rgb.b)
310 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
311 if(state->getFillColorSpace()->getNComps()>1)
312 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
313 if(state->getStrokeColorSpace()->getNComps()>1)
314 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
315 if(state->getFillPattern())
316 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
317 if(state->getStrokePattern())
318 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
320 if(state->getFillOpacity()!=1.0)
321 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
322 if(state->getStrokeOpacity()!=1.0)
323 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
325 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
330 state->getLineDash(&dash, &length, &start);
334 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
335 for(t=0;t<length;t++) {
336 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
338 bufpos+=sprintf(bufpos,"]");
341 if(state->getFlatness()!=1)
342 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
343 if(state->getLineJoin()!=0)
344 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
345 if(state->getLineJoin()!=0)
346 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
347 if(state->getLineJoin()!=0)
348 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
350 if(state->getFont() && getFontID(state->getFont()))
351 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
352 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
353 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
354 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
355 if(state->getCharSpace())
356 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
357 if(state->getWordSpace())
358 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
359 if(state->getHorizScaling()!=1.0)
360 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
361 if(state->getLeading())
362 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
364 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
365 if(state->getRender())
366 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
367 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
368 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
369 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
370 if(state->getLineX())
371 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
372 if(state->getLineY())
373 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
374 bufpos+=sprintf(bufpos," ");
378 static void dumpFontInfo(char*loglevel, GfxFont*font);
379 static int lastdumps[1024];
380 static int lastdumppos = 0;
385 static void showFontError(GfxFont*font, int nr)
389 for(t=0;t<lastdumppos;t++)
390 if(lastdumps[t] == r->num)
394 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
395 lastdumps[lastdumppos++] = r->num;
397 msg("<warning> The following font caused problems:");
399 msg("<warning> The following font caused problems (substituting):");
401 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
402 dumpFontInfo("<warning>", font);
405 static void dumpFontInfo(char*loglevel, GfxFont*font)
407 char* id = getFontID(font);
408 char* name = getFontName(font);
409 Ref* r=font->getID();
410 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
412 GString*gstr = font->getTag();
414 msg("%s| Tag: %s\n", loglevel, id);
416 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
418 GfxFontType type=font->getType();
420 case fontUnknownType:
421 msg("%s| Type: unknown\n",loglevel);
424 msg("%s| Type: 1\n",loglevel);
427 msg("%s| Type: 1C\n",loglevel);
430 msg("%s| Type: 3\n",loglevel);
433 msg("%s| Type: TrueType\n",loglevel);
436 msg("%s| Type: CIDType0\n",loglevel);
439 msg("%s| Type: CIDType0C\n",loglevel);
442 msg("%s| Type: CIDType2\n",loglevel);
447 GBool embedded = font->getEmbeddedFontID(&embRef);
449 if(font->getEmbeddedFontName()) {
450 embeddedName = font->getEmbeddedFontName()->getCString();
453 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
455 gstr = font->getExtFontFile();
457 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
459 // Get font descriptor flags.
460 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
461 if(font->isSerif()) msg("%s| is serif\n", loglevel);
462 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
463 if(font->isItalic()) msg("%s| is italic\n", loglevel);
464 if(font->isBold()) msg("%s| is bold\n", loglevel);
470 //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");}
471 //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");}
474 void dump_outline(gfxline_t*line)
477 if(line->type == gfx_moveTo) {
478 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
479 } else if(line->type == gfx_lineTo) {
480 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
481 } else if(line->type == gfx_splineTo) {
482 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
488 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
490 int num = path->getNumSubpaths();
493 double lastx=0,lasty=0,posx=0,posy=0;
496 msg("<warning> empty path");
500 gfxdrawer_target_gfxline(&draw);
502 for(t = 0; t < num; t++) {
503 GfxSubpath *subpath = path->getSubpath(t);
504 int subnum = subpath->getNumPoints();
505 double bx=0,by=0,cx=0,cy=0;
507 for(s=0;s<subnum;s++) {
510 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
515 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
516 draw.lineTo(&draw, lastx, lasty);
518 draw.moveTo(&draw, x,y);
523 } else if(subpath->getCurve(s) && cpos==0) {
527 } else if(subpath->getCurve(s) && cpos==1) {
535 draw.lineTo(&draw, x,y);
537 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
544 /* fix non-closed lines */
545 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
546 draw.lineTo(&draw, lastx, lasty);
548 gfxline_t*result = (gfxline_t*)draw.result(&draw);
552 /*----------------------------------------------------------------------------
553 * Primitive Graphic routines
554 *----------------------------------------------------------------------------*/
556 void GFXOutputDev::stroke(GfxState *state)
558 GfxPath * path = state->getPath();
559 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
560 strokeGfxline(state, line);
564 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
566 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
567 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
568 double miterLimit = state->getMiterLimit();
569 double width = state->getTransformedLineWidth();
572 double opaq = state->getStrokeOpacity();
574 state->getFillRGB(&rgb);
576 state->getStrokeRGB(&rgb);
578 col.r = colToByte(rgb.r);
579 col.g = colToByte(rgb.g);
580 col.b = colToByte(rgb.b);
581 col.a = (unsigned char)(opaq*255);
583 gfx_capType capType = gfx_capRound;
584 if(lineCap == 0) capType = gfx_capButt;
585 else if(lineCap == 1) capType = gfx_capRound;
586 else if(lineCap == 2) capType = gfx_capSquare;
588 gfx_joinType joinType = gfx_joinRound;
589 if(lineJoin == 0) joinType = gfx_joinMiter;
590 else if(lineJoin == 1) joinType = gfx_joinRound;
591 else if(lineJoin == 2) joinType = gfx_joinBevel;
594 double dashphase = 0;
596 state->getLineDash(&ldash, &dashnum, &dashphase);
600 if(dashnum && ldash) {
601 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
605 msg("<trace> %d dashes", dashnum);
606 msg("<trace> | phase: %f", dashphase);
607 for(t=0;t<dashnum;t++) {
609 msg("<trace> | d%-3d: %f", t, ldash[t]);
612 if(getLogLevel() >= LOGLEVEL_TRACE) {
616 line2 = gfxtool_dash_line(line, dash, dashphase);
619 msg("<trace> After dashing:");
622 if(getLogLevel() >= LOGLEVEL_TRACE) {
623 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
625 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
626 lineCap==0?"butt": (lineJoin==1?"round":"square"),
628 col.r,col.g,col.b,col.a
633 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
634 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
644 gfxcolor_t getFillColor(GfxState * state)
647 double opaq = state->getFillOpacity();
648 state->getFillRGB(&rgb);
650 col.r = colToByte(rgb.r);
651 col.g = colToByte(rgb.g);
652 col.b = colToByte(rgb.b);
653 col.a = (unsigned char)(opaq*255);
657 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
659 gfxcolor_t col = getFillColor(state);
661 if(getLogLevel() >= LOGLEVEL_TRACE) {
662 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
666 device->fill(device, line, &col);
668 void GFXOutputDev::fill(GfxState *state)
670 GfxPath * path = state->getPath();
671 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
672 fillGfxLine(state, line);
675 void GFXOutputDev::eoFill(GfxState *state)
677 GfxPath * path = state->getPath();
678 gfxcolor_t col = getFillColor(state);
680 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
682 if(getLogLevel() >= LOGLEVEL_TRACE) {
683 msg("<trace> eofill\n");
687 device->fill(device, line, &col);
691 void GFXOutputDev::clip(GfxState *state)
693 GfxPath * path = state->getPath();
694 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
695 clipToGfxLine(state, line);
699 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
701 if(getLogLevel() >= LOGLEVEL_TRACE) {
702 msg("<trace> clip\n");
706 device->startclip(device, line);
707 states[statepos].clipping++;
709 void GFXOutputDev::eoClip(GfxState *state)
711 GfxPath * path = state->getPath();
712 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
714 if(getLogLevel() >= LOGLEVEL_TRACE) {
715 msg("<trace> eoclip\n");
719 device->startclip(device, line);
720 states[statepos].clipping++;
724 void GFXOutputDev::endframe()
727 device->endclip(device);
731 device->endpage(device);
734 void GFXOutputDev::finish()
738 device->endclip(device);
744 GFXOutputDev::~GFXOutputDev()
749 free(this->pages); this->pages = 0;
752 fontlist_t*l = this->fontlist;
754 fontlist_t*next = l->next;
756 gfxfont_free(l->font);
757 free(l->filename);l->filename=0;
763 GBool GFXOutputDev::upsideDown()
767 GBool GFXOutputDev::useDrawChar()
772 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
773 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
775 #define RENDER_FILL 0
776 #define RENDER_STROKE 1
777 #define RENDER_FILLSTROKE 2
778 #define RENDER_INVISIBLE 3
779 #define RENDER_CLIP 4
781 static char tmp_printstr[4096];
782 char* makeStringPrintable(char*str)
784 int len = strlen(str);
799 tmp_printstr[len++] = '.';
800 tmp_printstr[len++] = '.';
801 tmp_printstr[len++] = '.';
803 tmp_printstr[len] = 0;
808 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
813 /* find out char name from unicode index
814 TODO: should be precomputed
816 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
817 if(nameToUnicodeTab[t].u == u) {
818 uniname = nameToUnicodeTab[t].name;
826 for(t=0;t<font->num_glyphs;t++) {
827 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
828 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
832 /* if we didn't find the character, maybe
833 we can find the capitalized version */
834 for(t=0;t<font->num_glyphs;t++) {
835 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
836 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
844 for(t=0;t<font->num_glyphs;t++) {
845 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
846 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
850 /* if we didn't find the character, maybe
851 we can find the capitalized version */
852 for(t=0;t<font->num_glyphs;t++) {
853 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
854 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
860 /* try to use the unicode id */
861 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
862 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
863 return font->unicode2glyph[u];
866 if(charnr>=0 && charnr<font->num_glyphs) {
867 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
875 void GFXOutputDev::beginString(GfxState *state, GString *s)
877 int render = state->getRender();
878 if(current_text_stroke) {
879 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
882 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
883 double m11,m21,m12,m22;
884 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
885 state->getFontTransMat(&m11, &m12, &m21, &m22);
886 m11 *= state->getHorizScaling();
887 m21 *= state->getHorizScaling();
889 this->current_font_matrix.m00 = m11 / 1024.0;
890 this->current_font_matrix.m01 = m12 / 1024.0;
891 this->current_font_matrix.m10 = -m21 / 1024.0;
892 this->current_font_matrix.m11 = -m22 / 1024.0;
893 this->current_font_matrix.tx = 0;
894 this->current_font_matrix.ty = 0;
896 gfxmatrix_t m = this->current_font_matrix;
898 /*if(render != 3 && render != 0)
899 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
900 states[statepos].textRender = render;
903 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
904 double dx, double dy,
905 double originX, double originY,
906 CharCode c, int nBytes, Unicode *_u, int uLen)
908 int render = state->getRender();
909 // check for invisible text -- this is used by Acrobat Capture
911 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
915 if(states[statepos].textRender != render)
916 msg("<error> Internal error: drawChar.render!=beginString.render");
918 gfxcolor_t col = getFillColor(state);
920 Gushort *CIDToGIDMap = 0;
921 GfxFont*font = state->getFont();
923 if(font->getType() == fontType3) {
924 /* type 3 chars are passed as graphics */
925 msg("<debug> type3 char at %f/%f", x, y);
935 if(font->isCIDFont()) {
936 GfxCIDFont*cfont = (GfxCIDFont*)font;
938 if(font->getType() == fontCIDType2)
939 CIDToGIDMap = cfont->getCIDToGID();
942 font8 = (Gfx8BitFont*)font;
943 char**enc=font8->getEncoding();
947 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);
950 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);
956 charid = getGfxCharID(current_gfxfont, c, name, u);
958 charid = getGfxCharID(current_gfxfont, c, name, -1);
961 /* multiple unicodes- should usually map to a ligature.
962 if the ligature doesn't exist, we need to draw
963 the characters one-by-one. */
965 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
966 for(t=0;t<uLen;t++) {
967 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
973 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
974 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
978 gfxmatrix_t m = this->current_font_matrix;
979 state->transform(x, y, &m.tx, &m.ty);
983 if(render == RENDER_FILL) {
984 device->drawchar(device, current_gfxfont, charid, &col, &m);
986 msg("<debug> Drawing glyph %d as shape", charid);
988 msg("<notice> Some texts will be rendered as shape");
991 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
992 gfxline_t*tglyph = gfxline_clone(glyph);
993 gfxline_transform(tglyph, &m);
994 if((render&3) != RENDER_INVISIBLE) {
995 gfxline_t*add = gfxline_clone(tglyph);
996 current_text_stroke = gfxline_append(current_text_stroke, add);
998 if(render&RENDER_CLIP) {
999 gfxline_t*add = gfxline_clone(tglyph);
1000 current_text_clip = gfxline_append(current_text_clip, add);
1002 gfxline_free(tglyph);
1006 void GFXOutputDev::endString(GfxState *state)
1008 int render = state->getRender();
1009 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1010 if(states[statepos].textRender != render)
1011 msg("<error> Internal error: drawChar.render!=beginString.render");
1013 if(current_text_stroke) {
1014 /* fillstroke and stroke text rendering objects we can process right
1015 now (as there may be texts of other rendering modes in this
1016 text object)- clipping objects have to wait until endTextObject,
1018 device->setparameter(device, "mark","TXT");
1019 if((render&3) == RENDER_FILL) {
1020 fillGfxLine(state, current_text_stroke);
1021 gfxline_free(current_text_stroke);
1022 current_text_stroke = 0;
1023 } else if((render&3) == RENDER_FILLSTROKE) {
1024 fillGfxLine(state, current_text_stroke);
1025 strokeGfxline(state, current_text_stroke);
1026 gfxline_free(current_text_stroke);
1027 current_text_stroke = 0;
1028 } else if((render&3) == RENDER_STROKE) {
1029 strokeGfxline(state, current_text_stroke);
1030 gfxline_free(current_text_stroke);
1031 current_text_stroke = 0;
1033 device->setparameter(device, "mark","");
1037 void GFXOutputDev::endTextObject(GfxState *state)
1039 int render = state->getRender();
1040 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1041 if(states[statepos].textRender != render)
1042 msg("<error> Internal error: drawChar.render!=beginString.render");
1044 if(current_text_clip) {
1045 device->setparameter(device, "mark","TXT");
1046 clipToGfxLine(state, current_text_clip);
1047 device->setparameter(device, "mark","");
1048 gfxline_free(current_text_clip);
1049 current_text_clip = 0;
1053 /* the logic seems to be as following:
1054 first, beginType3Char is called, with the charcode and the coordinates.
1055 if this function returns true, it already knew about the char and has now drawn it.
1056 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1057 the all draw operations until endType3Char are part of the char (which in this moment is
1058 at the position first passed to beginType3Char). the char ends with endType3Char.
1060 The drawing operations between beginType3Char and endType3Char are somewhat different to
1061 the normal ones. For example, the fillcolor equals the stroke color.
1064 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1066 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1068 /* the character itself is going to be passed using the draw functions */
1069 return gFalse; /* gTrue= is_in_cache? */
1072 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1073 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1075 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1076 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1080 void GFXOutputDev::endType3Char(GfxState *state)
1083 msg("<debug> endType3Char");
1086 void GFXOutputDev::startFrame(int width, int height)
1088 device->startpage(device, width, height);
1091 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1093 this->currentpage = pageNum;
1095 int rot = doc->getPageRotate(1);
1098 gfxline_t clippath[5];
1100 white.r = white.g = white.b = white.a = 255;
1102 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1103 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1104 Use CropBox, not MediaBox, as page size
1111 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1112 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1114 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1115 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1118 /* apply user clip box */
1119 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1120 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1121 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1122 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1123 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1126 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1128 if(outer_clip_box) {
1129 device->endclip(device);
1133 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, user_movey);
1135 msg("<verbose> page is rotated %d degrees\n", rot);
1137 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1138 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1139 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1140 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1141 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1142 device->startclip(device, clippath); outer_clip_box = 1;
1143 device->fill(device, clippath, &white);
1146 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1148 double x1, y1, x2, y2, w;
1149 gfxline_t points[5];
1152 msg("<debug> drawlink\n");
1154 link->getRect(&x1, &y1, &x2, &y2);
1155 cvtUserToDev(x1, y1, &x, &y);
1156 points[0].type = gfx_moveTo;
1157 points[0].x = points[4].x = x + user_movex;
1158 points[0].y = points[4].y = y + user_movey;
1159 points[0].next = &points[1];
1160 cvtUserToDev(x2, y1, &x, &y);
1161 points[1].type = gfx_lineTo;
1162 points[1].x = x + user_movex;
1163 points[1].y = y + user_movey;
1164 points[1].next = &points[2];
1165 cvtUserToDev(x2, y2, &x, &y);
1166 points[2].type = gfx_lineTo;
1167 points[2].x = x + user_movex;
1168 points[2].y = y + user_movey;
1169 points[2].next = &points[3];
1170 cvtUserToDev(x1, y2, &x, &y);
1171 points[3].type = gfx_lineTo;
1172 points[3].x = x + user_movex;
1173 points[3].y = y + user_movey;
1174 points[3].next = &points[4];
1175 cvtUserToDev(x1, y1, &x, &y);
1176 points[4].type = gfx_lineTo;
1177 points[4].x = x + user_movex;
1178 points[4].y = y + user_movey;
1181 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1182 points[0].x, points[0].y,
1183 points[1].x, points[1].y,
1184 points[2].x, points[2].y,
1185 points[3].x, points[3].y);
1187 LinkAction*action=link->getAction();
1193 msg("<trace> drawlink action=%d\n", action->getKind());
1194 switch(action->getKind())
1198 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1199 LinkDest *dest=NULL;
1200 if (ha->getDest()==NULL)
1201 dest=catalog->findDest(ha->getNamedDest());
1202 else dest=ha->getDest();
1204 if (dest->isPageRef()){
1205 Ref pageref=dest->getPageRef();
1206 page=catalog->findPage(pageref.num,pageref.gen);
1208 else page=dest->getPageNum();
1209 sprintf(buf, "%d", page);
1216 LinkGoToR*l = (LinkGoToR*)action;
1217 GString*g = l->getNamedDest();
1219 s = strdup(g->getCString());
1224 LinkNamed*l = (LinkNamed*)action;
1225 GString*name = l->getName();
1227 s = strdup(name->lowerCase()->getCString());
1228 named = name->getCString();
1231 if(strstr(s, "next") || strstr(s, "forward"))
1233 page = currentpage + 1;
1235 else if(strstr(s, "prev") || strstr(s, "back"))
1237 page = currentpage - 1;
1239 else if(strstr(s, "last") || strstr(s, "end"))
1241 if(pages && pagepos>0)
1242 page = pages[pagepos-1];
1244 else if(strstr(s, "first") || strstr(s, "top"))
1252 case actionLaunch: {
1254 LinkLaunch*l = (LinkLaunch*)action;
1255 GString * str = new GString(l->getFileName());
1256 GString * params = l->getParams();
1258 str->append(params);
1259 s = strdup(str->getCString());
1266 LinkURI*l = (LinkURI*)action;
1267 GString*g = l->getURI();
1269 url = g->getCString();
1274 case actionUnknown: {
1276 LinkUnknown*l = (LinkUnknown*)action;
1281 msg("<error> Unknown link type!\n");
1286 if(!s) s = strdup("-?-");
1288 msg("<trace> drawlink s=%s\n", s);
1290 if(!linkinfo && (page || s))
1292 msg("<notice> File contains links");
1300 for(t=1;t<=pagepos;t++) {
1301 if(pages[t]==page) {
1310 sprintf(buf, "page%d", lpage);
1311 device->drawlink(device, points, buf);
1315 device->drawlink(device, points, s);
1318 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1322 void GFXOutputDev::saveState(GfxState *state) {
1323 msg("<trace> saveState\n");
1326 msg("<error> Too many nested states in pdf.");
1330 states[statepos].clipping = 0; //? shouldn't this be the current value?
1331 states[statepos].textRender = states[statepos-1].textRender;
1334 void GFXOutputDev::restoreState(GfxState *state) {
1335 msg("<trace> restoreState\n");
1337 while(states[statepos].clipping) {
1338 device->endclip(device);
1339 states[statepos].clipping--;
1344 char* GFXOutputDev::searchFont(char*name)
1348 int is_standard_font = 0;
1350 msg("<verbose> SearchFont(%s)", name);
1352 /* see if it is a pdf standard font */
1353 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1355 if(!strcmp(name, pdf2t1map[i].pdffont))
1357 name = pdf2t1map[i].filename;
1358 is_standard_font = 1;
1362 /* look in all font files */
1363 for(i=0;i<fontnum;i++)
1365 if(strstr(fonts[i].filename, name))
1367 if(!fonts[i].used) {
1370 if(!is_standard_font)
1371 msg("<notice> Using %s for %s", fonts[i].filename, name);
1373 return strdup(fonts[i].filename);
1379 void GFXOutputDev::updateLineWidth(GfxState *state)
1381 double width = state->getTransformedLineWidth();
1382 //swfoutput_setlinewidth(&device, width);
1385 void GFXOutputDev::updateLineCap(GfxState *state)
1387 int c = state->getLineCap();
1390 void GFXOutputDev::updateLineJoin(GfxState *state)
1392 int j = state->getLineJoin();
1395 void GFXOutputDev::updateFillColor(GfxState *state)
1398 double opaq = state->getFillOpacity();
1399 state->getFillRGB(&rgb);
1401 //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1404 void GFXOutputDev::updateStrokeColor(GfxState *state)
1407 double opaq = state->getStrokeOpacity();
1408 state->getStrokeRGB(&rgb);
1409 //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1412 void FoFiWrite(void *stream, char *data, int len)
1414 fwrite(data, len, 1, (FILE*)stream);
1417 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1419 char*tmpFileName = NULL;
1425 Object refObj, strObj;
1427 tmpFileName = mktmpname(namebuf);
1430 ret = font->getEmbeddedFontID(&embRef);
1432 msg("<verbose> Didn't get embedded font id");
1433 /* not embedded- the caller should now search the font
1434 directories for this font */
1438 f = fopen(tmpFileName, "wb");
1440 msg("<error> Couldn't create temporary Type 1 font file");
1444 /*if(font->isCIDFont()) {
1445 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1446 GString c = cidFont->getCollection();
1447 msg("<notice> Collection: %s", c.getCString());
1450 //if (font->getType() == fontType1C) {
1451 if (0) { //font->getType() == fontType1C) {
1452 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1454 msg("<error> Couldn't read embedded font file");
1457 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1459 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1460 //cvt->convertToCIDType0("test", f);
1461 //cvt->convertToType0("test", f);
1464 } else if(font->getType() == fontTrueType) {
1465 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1466 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1468 msg("<error> Couldn't read embedded font file");
1471 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1472 cvt->writeTTF(FoFiWrite, f);
1476 font->getEmbeddedFontID(&embRef);
1477 refObj.initRef(embRef.num, embRef.gen);
1478 refObj.fetch(ref, &strObj);
1480 strObj.streamReset();
1485 f4[t] = strObj.streamGetChar();
1486 f4c[t] = (char)f4[t];
1491 if(!strncmp(f4c, "true", 4)) {
1492 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1493 Change this on the fly */
1494 f4[0] = f4[2] = f4[3] = 0;
1502 while ((c = strObj.streamGetChar()) != EOF) {
1506 strObj.streamClose();
1511 return strdup(tmpFileName);
1514 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1516 char*name = getFontName(gfxFont);
1520 if(!this->config_use_fontconfig)
1523 #ifdef HAVE_FONTCONFIG
1524 FcPattern *pattern, *match;
1528 static int fcinitcalled = false;
1530 msg("<debug> searchForSuitableFont(%s)", name);
1532 // call init ony once
1533 if (!fcinitcalled) {
1534 msg("<debug> Initializing FontConfig...");
1535 fcinitcalled = true;
1537 msg("<debug> FontConfig Initialization failed. Disabling.");
1538 config_use_fontconfig = 0;
1541 msg("<debug> ...initialized FontConfig");
1544 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1545 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1546 if (gfxFont->isItalic()) // check for italic
1547 msg("<debug> FontConfig: Adding Italic Slant");
1548 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1549 if (gfxFont->isBold()) // check for bold
1550 msg("<debug> FontConfig: Adding Bold Weight");
1551 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1553 msg("<debug> FontConfig: Try to match...");
1554 // configure and match using the original font name
1555 FcConfigSubstitute(0, pattern, FcMatchPattern);
1556 FcDefaultSubstitute(pattern);
1557 match = FcFontMatch(0, pattern, &result);
1559 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1560 msg("<debug> FontConfig: family=%s", (char*)v);
1561 // if we get an exact match
1562 if (strcmp((char *)v, name) == 0) {
1563 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1564 filename = strdup((char*)v); // mem leak
1565 char *nfn = strrchr(filename, '/');
1566 if(nfn) fontname = strdup(nfn+1);
1567 else fontname = filename;
1569 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1571 // initialize patterns
1572 FcPatternDestroy(pattern);
1573 FcPatternDestroy(match);
1575 // now match against serif etc.
1576 if (gfxFont->isSerif()) {
1577 msg("<debug> FontConfig: Create Serif Family Pattern");
1578 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1579 } else if (gfxFont->isFixedWidth()) {
1580 msg("<debug> FontConfig: Create Monospace Family Pattern");
1581 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1583 msg("<debug> FontConfig: Create Sans Family Pattern");
1584 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1588 if (gfxFont->isItalic()) {
1589 msg("<debug> FontConfig: Adding Italic Slant");
1590 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1593 if (gfxFont->isBold()) {
1594 msg("<debug> FontConfig: Adding Bold Weight");
1595 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1598 msg("<debug> FontConfig: Try to match... (2)");
1599 // configure and match using serif etc
1600 FcConfigSubstitute (0, pattern, FcMatchPattern);
1601 FcDefaultSubstitute (pattern);
1602 match = FcFontMatch (0, pattern, &result);
1604 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1605 filename = strdup((char*)v); // mem leak
1606 char *nfn = strrchr(filename, '/');
1607 if(nfn) fontname = strdup(nfn+1);
1608 else fontname = filename;
1610 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1614 //printf("FONTCONFIG: pattern");
1615 //FcPatternPrint(pattern);
1616 //printf("FONTCONFIG: match");
1617 //FcPatternPrint(match);
1619 FcPatternDestroy(pattern);
1620 FcPatternDestroy(match);
1622 pdfswf_addfont(filename);
1629 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1631 char*fontname = 0, *filename = 0;
1632 msg("<notice> substituteFont(%s)", oldname);
1634 if(!(fontname = searchForSuitableFont(gfxFont))) {
1635 fontname = "Times-Roman";
1637 filename = searchFont(fontname);
1639 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1643 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1644 msg("<fatal> Too many fonts in file.");
1648 substitutesource[substitutepos] = strdup(oldname); //mem leak
1649 substitutetarget[substitutepos] = fontname;
1650 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1653 return strdup(filename); //mem leak
1656 void unlinkfont(char* filename)
1663 if(!strncmp(&filename[l-4],".afm",4)) {
1664 memcpy(&filename[l-4],".pfb",4);
1666 memcpy(&filename[l-4],".pfa",4);
1668 memcpy(&filename[l-4],".afm",4);
1671 if(!strncmp(&filename[l-4],".pfa",4)) {
1672 memcpy(&filename[l-4],".afm",4);
1674 memcpy(&filename[l-4],".pfa",4);
1677 if(!strncmp(&filename[l-4],".pfb",4)) {
1678 memcpy(&filename[l-4],".afm",4);
1680 memcpy(&filename[l-4],".pfb",4);
1685 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1691 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1694 fontlist_t*last=0,*l = this->fontlist;
1697 msg("<error> Internal Error: FontID is null");
1699 /* TODO: should this be part of the state? */
1702 if(!strcmp(l->font->id, id)) {
1703 current_gfxfont = l->font;
1705 device->addfont(device, current_gfxfont);
1710 if(!filename) return 0;
1712 /* A font size of e.g. 9 means the font will be scaled down by
1713 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1714 we have to divide 0.05 by (fontsize/1024)
1716 double quality = (1024 * 0.05) / maxSize;
1718 msg("<verbose> Loading %s...", filename);
1719 font = gfxfont_load(id, filename, quality);
1721 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1724 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1728 l->filename = strdup(filename);
1730 current_gfxfont = l->font;
1736 device->addfont(device, current_gfxfont);
1740 void GFXOutputDev::updateFont(GfxState *state)
1742 GfxFont*gfxFont = state->getFont();
1748 char * fontid = getFontID(gfxFont);
1749 char * fontname = getFontName(gfxFont);
1751 double maxSize = 1.0;
1754 maxSize = this->info->getMaximumFontSize(fontid);
1758 /* first, look if we substituted this font before-
1759 this way, we don't initialize the T1 Fonts
1761 for(t=0;t<substitutepos;t++) {
1762 if(!strcmp(fontid, substitutesource[t])) {
1763 free(fontid);fontid=0;
1764 fontid = strdup(substitutetarget[t]);
1769 /* second, see if this is a font which was used before-
1770 if so, we are done */
1771 if(setGfxFont(fontid, fontname, 0, 0)) {
1776 /* if(swfoutput_queryfont(&device, fontid))
1777 swfoutput_setfont(&device, fontid, 0);
1779 msg("<debug> updateFont(%s) [cached]", fontid);
1783 // look for Type 3 font
1784 if (gfxFont->getType() == fontType3) {
1786 type3Warning = gTrue;
1787 showFontError(gfxFont, 2);
1794 /* now either load the font, or find a substitution */
1797 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1802 (gfxFont->getType() == fontType1 ||
1803 gfxFont->getType() == fontType1C ||
1804 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1805 gfxFont->getType() == fontTrueType ||
1806 gfxFont->getType() == fontCIDType2
1809 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1810 if(!fileName) showFontError(gfxFont,0);
1813 fileName = searchFont(fontname);
1814 if(!fileName) showFontError(gfxFont,0);
1817 char * fontname = getFontName(gfxFont);
1818 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1821 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1823 msg("<warning> Try specifying one or more font directories");
1825 fileName = substituteFont(gfxFont, fontid);
1828 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1829 msg("<notice> Font is now %s (%s)", fontid, fileName);
1833 msg("<error> Couldn't set font %s\n", fontid);
1839 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1840 dumpFontInfo("<verbose>", gfxFont);
1842 //swfoutput_setfont(&device, fontid, fileName);
1844 if(!setGfxFont(fontid, fontname, 0, 0)) {
1845 setGfxFont(fontid, fontname, fileName, maxSize);
1849 unlinkfont(fileName);
1859 #define SQR(x) ((x)*(x))
1861 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1863 if((newwidth<2 || newheight<2) ||
1864 (width<=newwidth || height<=newheight))
1866 unsigned char*newdata;
1868 newdata= (unsigned char*)malloc(newwidth*newheight);
1870 double fx = (double)(width)/newwidth;
1871 double fy = (double)(height)/newheight;
1873 int blocksize = (int)(8192/(fx*fy));
1874 int r = 8192*256/palettesize;
1875 for(x=0;x<newwidth;x++) {
1876 double ex = px + fx;
1877 int fromx = (int)px;
1879 int xweight1 = (int)(((fromx+1)-px)*256);
1880 int xweight2 = (int)((ex-tox)*256);
1882 for(y=0;y<newheight;y++) {
1883 double ey = py + fy;
1884 int fromy = (int)py;
1886 int yweight1 = (int)(((fromy+1)-py)*256);
1887 int yweight2 = (int)((ey-toy)*256);
1890 for(xx=fromx;xx<=tox;xx++)
1891 for(yy=fromy;yy<=toy;yy++) {
1892 int b = 1-data[width*yy+xx];
1894 if(xx==fromx) weight = (weight*xweight1)/256;
1895 if(xx==tox) weight = (weight*xweight2)/256;
1896 if(yy==fromy) weight = (weight*yweight1)/256;
1897 if(yy==toy) weight = (weight*yweight2)/256;
1900 //if(a) a=(palettesize-1)*r/blocksize;
1901 newdata[y*newwidth+x] = (a*blocksize)/r;
1909 #define IMAGE_TYPE_JPEG 0
1910 #define IMAGE_TYPE_LOSSLESS 1
1912 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1913 double x1,double y1,
1914 double x2,double y2,
1915 double x3,double y3,
1916 double x4,double y4, int type)
1918 gfxcolor_t*newpic=0;
1920 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1921 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1923 gfxline_t p1,p2,p3,p4,p5;
1924 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1925 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1926 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1927 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1928 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1930 {p1.x = (int)(p1.x*20)/20.0;
1931 p1.y = (int)(p1.y*20)/20.0;
1932 p2.x = (int)(p2.x*20)/20.0;
1933 p2.y = (int)(p2.y*20)/20.0;
1934 p3.x = (int)(p3.x*20)/20.0;
1935 p3.y = (int)(p3.y*20)/20.0;
1936 p4.x = (int)(p4.x*20)/20.0;
1937 p4.y = (int)(p4.y*20)/20.0;
1938 p5.x = (int)(p5.x*20)/20.0;
1939 p5.y = (int)(p5.y*20)/20.0;
1946 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1947 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1952 img.data = (gfxcolor_t*)data;
1956 if(type == IMAGE_TYPE_JPEG)
1957 /* TODO: pass image_dpi to device instead */
1958 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1960 dev->fillbitmap(dev, &p1, &img, &m, 0);
1963 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1964 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1966 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1969 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1970 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1972 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1976 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1977 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1978 GBool inlineImg, int mask, int*maskColors,
1979 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1981 double x1,y1,x2,y2,x3,y3,x4,y4;
1982 ImageStream *imgStr;
1987 unsigned char* maskbitmap = 0;
1990 ncomps = colorMap->getNumPixelComps();
1991 bits = colorMap->getBits();
1996 unsigned char buf[8];
1997 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1999 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2000 imgMaskStr->reset();
2001 unsigned char pal[256];
2002 int n = 1 << colorMap->getBits();
2007 maskColorMap->getGray(pixBuf, &gray);
2008 pal[t] = colToByte(gray);
2010 for (y = 0; y < maskHeight; y++) {
2011 for (x = 0; x < maskWidth; x++) {
2012 imgMaskStr->getPixel(buf);
2013 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2018 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2019 imgMaskStr->reset();
2020 for (y = 0; y < maskHeight; y++) {
2021 for (x = 0; x < maskWidth; x++) {
2022 imgMaskStr->getPixel(buf);
2024 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2032 imgStr = new ImageStream(str, width, ncomps,bits);
2035 if(!width || !height || (height<=1 && width<=1))
2037 msg("<verbose> Ignoring %d by %d image", width, height);
2038 unsigned char buf[8];
2040 for (y = 0; y < height; ++y)
2041 for (x = 0; x < width; ++x) {
2042 imgStr->getPixel(buf);
2050 state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
2051 state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
2052 state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
2053 state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
2056 if(!pbminfo && !(str->getKind()==strDCT)) {
2058 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2062 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2064 if(!jpeginfo && (str->getKind()==strDCT)) {
2065 msg("<notice> file contains jpeg pictures");
2071 unsigned char buf[8];
2073 unsigned char*pic = new unsigned char[width*height];
2074 gfxcolor_t pal[256];
2076 state->getFillRGB(&rgb);
2078 memset(pal,255,sizeof(pal));
2079 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2080 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2081 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2082 pal[0].a = 255; pal[1].a = 0;
2085 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2086 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2087 for (y = 0; y < height; ++y)
2088 for (x = 0; x < width; ++x)
2090 imgStr->getPixel(buf);
2093 pic[width*y+x] = buf[0];
2096 /* the size of the drawn image is added to the identifier
2097 as the same image may require different bitmaps if displayed
2098 at different sizes (due to antialiasing): */
2101 unsigned char*pic2 = 0;
2104 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2113 height = realheight;
2117 /* make a black/white palette */
2119 float r = 255/(numpalette-1);
2121 for(t=0;t<numpalette;t++) {
2122 pal[t].r = colToByte(rgb.r);
2123 pal[t].g = colToByte(rgb.g);
2124 pal[t].b = colToByte(rgb.b);
2125 pal[t].a = (unsigned char)(t*r);
2129 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2130 for (y = 0; y < height; ++y) {
2131 for (x = 0; x < width; ++x) {
2132 pic2[width*y+x] = pal[pic[y*width+x]];
2135 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2139 if(maskbitmap) free(maskbitmap);
2145 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2146 gfxcolor_t*pic=new gfxcolor_t[width*height];
2147 for (y = 0; y < height; ++y) {
2148 for (x = 0; x < width; ++x) {
2149 imgStr->getPixel(pixBuf);
2150 colorMap->getRGB(pixBuf, &rgb);
2151 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2152 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2153 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2154 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2156 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2160 if(str->getKind()==strDCT)
2161 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2163 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2166 if(maskbitmap) free(maskbitmap);
2169 gfxcolor_t*pic=new gfxcolor_t[width*height];
2170 gfxcolor_t pal[256];
2171 int n = 1 << colorMap->getBits();
2173 for(t=0;t<256;t++) {
2175 colorMap->getRGB(pixBuf, &rgb);
2177 {/*if(maskColors && *maskColors==t) {
2178 msg("<notice> Color %d is transparent", t);
2179 if (imgData->maskColors) {
2181 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2182 if (pix[i] < imgData->maskColors[2*i] ||
2183 pix[i] > imgData->maskColors[2*i+1]) {
2198 pal[t].r = (unsigned char)(colToByte(rgb.r));
2199 pal[t].g = (unsigned char)(colToByte(rgb.g));
2200 pal[t].b = (unsigned char)(colToByte(rgb.b));
2201 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2204 for (y = 0; y < height; ++y) {
2205 for (x = 0; x < width; ++x) {
2206 imgStr->getPixel(pixBuf);
2207 pic[width*y+x] = pal[pixBuf[0]];
2209 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2213 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2217 if(maskbitmap) free(maskbitmap);
2222 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2223 int width, int height, GBool invert,
2226 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2227 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2230 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2231 int width, int height, GfxImageColorMap *colorMap,
2232 int *maskColors, GBool inlineImg)
2234 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2235 colorMap?"colorMap":"no colorMap",
2236 maskColors?"maskColors":"no maskColors",
2239 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2240 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2241 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2244 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2245 int width, int height,
2246 GfxImageColorMap *colorMap,
2247 Stream *maskStr, int maskWidth, int maskHeight,
2250 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2251 colorMap?"colorMap":"no colorMap",
2252 maskWidth, maskHeight);
2254 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2255 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2256 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2259 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2260 int width, int height,
2261 GfxImageColorMap *colorMap,
2263 int maskWidth, int maskHeight,
2264 GfxImageColorMap *maskColorMap)
2266 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2267 colorMap?"colorMap":"no colorMap",
2268 maskWidth, maskHeight);
2270 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2271 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2272 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2275 static char* dirseparator()
2284 void addGlobalFont(char*filename)
2287 memset(&f, 0, sizeof(fontfile_t));
2288 f.filename = filename;
2289 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2290 msg("<verbose> Adding font \"%s\".", filename);
2291 fonts[fontnum++] = f;
2293 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2297 void addGlobalLanguageDir(char*dir)
2300 globalParams = new GlobalParams("");
2302 msg("<notice> Adding %s to language pack directories", dir);
2306 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2307 strcpy(config_file, dir);
2308 strcat(config_file, dirseparator());
2309 strcat(config_file, "add-to-xpdfrc");
2311 fi = fopen(config_file, "rb");
2313 msg("<error> Could not open %s", config_file);
2316 globalParams->parseFile(new GString(config_file), fi);
2320 void addGlobalFontDir(char*dirname)
2322 #ifdef HAVE_DIRENT_H
2323 msg("<notice> Adding %s to font directories", dirname);
2324 lastfontdir = strdup(dirname);
2325 DIR*dir = opendir(dirname);
2327 msg("<warning> Couldn't open directory %s\n", dirname);
2332 ent = readdir (dir);
2336 char*name = ent->d_name;
2342 if(!strncasecmp(&name[l-4], ".pfa", 4))
2344 if(!strncasecmp(&name[l-4], ".pfb", 4))
2346 if(!strncasecmp(&name[l-4], ".ttf", 4))
2350 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2351 strcpy(fontname, dirname);
2352 strcat(fontname, dirseparator());
2353 strcat(fontname, name);
2354 addGlobalFont(fontname);
2359 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2363 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2369 this->pagebuflen = 1024;
2370 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2371 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2373 while(pdfpage >= this->pagebuflen)
2375 int oldlen = this->pagebuflen;
2376 this->pagebuflen+=1024;
2377 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2378 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2381 this->pages[pdfpage] = outputpage;
2382 if(pdfpage>this->pagepos)
2383 this->pagepos = pdfpage;
2390 delete globalParams;globalParams=0;
2391 Object::memCheck(stderr);