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);
1720 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1724 l->filename = strdup(filename);
1726 current_gfxfont = l->font;
1732 device->addfont(device, current_gfxfont);
1736 void GFXOutputDev::updateFont(GfxState *state)
1738 GfxFont*gfxFont = state->getFont();
1744 char * fontid = getFontID(gfxFont);
1745 char * fontname = getFontName(gfxFont);
1747 double maxSize = 1.0;
1750 maxSize = this->info->getMaximumFontSize(fontid);
1754 /* first, look if we substituted this font before-
1755 this way, we don't initialize the T1 Fonts
1757 for(t=0;t<substitutepos;t++) {
1758 if(!strcmp(fontid, substitutesource[t])) {
1759 free(fontid);fontid=0;
1760 fontid = strdup(substitutetarget[t]);
1765 /* second, see if this is a font which was used before-
1766 if so, we are done */
1767 if(setGfxFont(fontid, fontname, 0, 0)) {
1772 /* if(swfoutput_queryfont(&device, fontid))
1773 swfoutput_setfont(&device, fontid, 0);
1775 msg("<debug> updateFont(%s) [cached]", fontid);
1779 // look for Type 3 font
1780 if (gfxFont->getType() == fontType3) {
1782 type3Warning = gTrue;
1783 showFontError(gfxFont, 2);
1790 /* now either load the font, or find a substitution */
1793 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1798 (gfxFont->getType() == fontType1 ||
1799 gfxFont->getType() == fontType1C ||
1800 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1801 gfxFont->getType() == fontTrueType ||
1802 gfxFont->getType() == fontCIDType2
1805 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1806 if(!fileName) showFontError(gfxFont,0);
1809 fileName = searchFont(fontname);
1810 if(!fileName) showFontError(gfxFont,0);
1813 char * fontname = getFontName(gfxFont);
1814 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1817 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1819 msg("<warning> Try specifying one or more font directories");
1821 fileName = substituteFont(gfxFont, fontid);
1824 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1825 msg("<notice> Font is now %s (%s)", fontid, fileName);
1829 msg("<error> Couldn't set font %s\n", fontid);
1835 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1836 dumpFontInfo("<verbose>", gfxFont);
1838 //swfoutput_setfont(&device, fontid, fileName);
1840 if(!setGfxFont(fontid, fontname, 0, 0)) {
1841 setGfxFont(fontid, fontname, fileName, maxSize);
1845 unlinkfont(fileName);
1855 #define SQR(x) ((x)*(x))
1857 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1859 if((newwidth<2 || newheight<2) ||
1860 (width<=newwidth || height<=newheight))
1862 unsigned char*newdata;
1864 newdata= (unsigned char*)malloc(newwidth*newheight);
1866 double fx = (double)(width)/newwidth;
1867 double fy = (double)(height)/newheight;
1869 int blocksize = (int)(8192/(fx*fy));
1870 int r = 8192*256/palettesize;
1871 for(x=0;x<newwidth;x++) {
1872 double ex = px + fx;
1873 int fromx = (int)px;
1875 int xweight1 = (int)(((fromx+1)-px)*256);
1876 int xweight2 = (int)((ex-tox)*256);
1878 for(y=0;y<newheight;y++) {
1879 double ey = py + fy;
1880 int fromy = (int)py;
1882 int yweight1 = (int)(((fromy+1)-py)*256);
1883 int yweight2 = (int)((ey-toy)*256);
1886 for(xx=fromx;xx<=tox;xx++)
1887 for(yy=fromy;yy<=toy;yy++) {
1888 int b = 1-data[width*yy+xx];
1890 if(xx==fromx) weight = (weight*xweight1)/256;
1891 if(xx==tox) weight = (weight*xweight2)/256;
1892 if(yy==fromy) weight = (weight*yweight1)/256;
1893 if(yy==toy) weight = (weight*yweight2)/256;
1896 //if(a) a=(palettesize-1)*r/blocksize;
1897 newdata[y*newwidth+x] = (a*blocksize)/r;
1905 #define IMAGE_TYPE_JPEG 0
1906 #define IMAGE_TYPE_LOSSLESS 1
1908 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1909 double x1,double y1,
1910 double x2,double y2,
1911 double x3,double y3,
1912 double x4,double y4, int type)
1914 gfxcolor_t*newpic=0;
1916 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1917 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1919 gfxline_t p1,p2,p3,p4,p5;
1920 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1921 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1922 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1923 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1924 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1926 {p1.x = (int)(p1.x*20)/20.0;
1927 p1.y = (int)(p1.y*20)/20.0;
1928 p2.x = (int)(p2.x*20)/20.0;
1929 p2.y = (int)(p2.y*20)/20.0;
1930 p3.x = (int)(p3.x*20)/20.0;
1931 p3.y = (int)(p3.y*20)/20.0;
1932 p4.x = (int)(p4.x*20)/20.0;
1933 p4.y = (int)(p4.y*20)/20.0;
1934 p5.x = (int)(p5.x*20)/20.0;
1935 p5.y = (int)(p5.y*20)/20.0;
1942 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1943 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1948 img.data = (gfxcolor_t*)data;
1952 if(type == IMAGE_TYPE_JPEG)
1953 /* TODO: pass image_dpi to device instead */
1954 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1956 dev->fillbitmap(dev, &p1, &img, &m, 0);
1959 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1960 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1962 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1965 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1966 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1968 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1972 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1973 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1974 GBool inlineImg, int mask, int*maskColors,
1975 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1977 double x1,y1,x2,y2,x3,y3,x4,y4;
1978 ImageStream *imgStr;
1983 unsigned char* maskbitmap = 0;
1986 ncomps = colorMap->getNumPixelComps();
1987 bits = colorMap->getBits();
1992 unsigned char buf[8];
1993 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1995 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1996 imgMaskStr->reset();
1997 unsigned char pal[256];
1998 int n = 1 << colorMap->getBits();
2003 maskColorMap->getGray(pixBuf, &gray);
2004 pal[t] = colToByte(gray);
2006 for (y = 0; y < maskHeight; y++) {
2007 for (x = 0; x < maskWidth; x++) {
2008 imgMaskStr->getPixel(buf);
2009 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2014 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2015 imgMaskStr->reset();
2016 for (y = 0; y < maskHeight; y++) {
2017 for (x = 0; x < maskWidth; x++) {
2018 imgMaskStr->getPixel(buf);
2020 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2028 imgStr = new ImageStream(str, width, ncomps,bits);
2031 if(!width || !height || (height<=1 && width<=1))
2033 msg("<verbose> Ignoring %d by %d image", width, height);
2034 unsigned char buf[8];
2036 for (y = 0; y < height; ++y)
2037 for (x = 0; x < width; ++x) {
2038 imgStr->getPixel(buf);
2046 state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
2047 state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
2048 state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
2049 state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
2052 if(!pbminfo && !(str->getKind()==strDCT)) {
2054 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2058 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2060 if(!jpeginfo && (str->getKind()==strDCT)) {
2061 msg("<notice> file contains jpeg pictures");
2067 unsigned char buf[8];
2069 unsigned char*pic = new unsigned char[width*height];
2070 gfxcolor_t pal[256];
2072 state->getFillRGB(&rgb);
2074 memset(pal,255,sizeof(pal));
2075 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2076 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2077 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2078 pal[0].a = 255; pal[1].a = 0;
2081 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2082 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2083 for (y = 0; y < height; ++y)
2084 for (x = 0; x < width; ++x)
2086 imgStr->getPixel(buf);
2089 pic[width*y+x] = buf[0];
2092 /* the size of the drawn image is added to the identifier
2093 as the same image may require different bitmaps if displayed
2094 at different sizes (due to antialiasing): */
2097 unsigned char*pic2 = 0;
2100 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2109 height = realheight;
2113 /* make a black/white palette */
2115 float r = 255/(numpalette-1);
2117 for(t=0;t<numpalette;t++) {
2118 pal[t].r = colToByte(rgb.r);
2119 pal[t].g = colToByte(rgb.g);
2120 pal[t].b = colToByte(rgb.b);
2121 pal[t].a = (unsigned char)(t*r);
2125 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2126 for (y = 0; y < height; ++y) {
2127 for (x = 0; x < width; ++x) {
2128 pic2[width*y+x] = pal[pic[y*width+x]];
2131 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2135 if(maskbitmap) free(maskbitmap);
2141 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2142 gfxcolor_t*pic=new gfxcolor_t[width*height];
2143 for (y = 0; y < height; ++y) {
2144 for (x = 0; x < width; ++x) {
2145 imgStr->getPixel(pixBuf);
2146 colorMap->getRGB(pixBuf, &rgb);
2147 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2148 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2149 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2150 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2152 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2156 if(str->getKind()==strDCT)
2157 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2159 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2162 if(maskbitmap) free(maskbitmap);
2165 gfxcolor_t*pic=new gfxcolor_t[width*height];
2166 gfxcolor_t pal[256];
2167 int n = 1 << colorMap->getBits();
2169 for(t=0;t<256;t++) {
2171 colorMap->getRGB(pixBuf, &rgb);
2173 {/*if(maskColors && *maskColors==t) {
2174 msg("<notice> Color %d is transparent", t);
2175 if (imgData->maskColors) {
2177 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2178 if (pix[i] < imgData->maskColors[2*i] ||
2179 pix[i] > imgData->maskColors[2*i+1]) {
2194 pal[t].r = (unsigned char)(colToByte(rgb.r));
2195 pal[t].g = (unsigned char)(colToByte(rgb.g));
2196 pal[t].b = (unsigned char)(colToByte(rgb.b));
2197 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2200 for (y = 0; y < height; ++y) {
2201 for (x = 0; x < width; ++x) {
2202 imgStr->getPixel(pixBuf);
2203 pic[width*y+x] = pal[pixBuf[0]];
2205 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2209 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2213 if(maskbitmap) free(maskbitmap);
2218 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2219 int width, int height, GBool invert,
2222 if(states[statepos].textRender & 4) //clipped
2224 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2225 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2228 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2229 int width, int height, GfxImageColorMap *colorMap,
2230 int *maskColors, GBool inlineImg)
2232 if(states[statepos].textRender & 4) //clipped
2235 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2236 colorMap?"colorMap":"no colorMap",
2237 maskColors?"maskColors":"no maskColors",
2240 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2241 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2242 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2245 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2246 int width, int height,
2247 GfxImageColorMap *colorMap,
2248 Stream *maskStr, int maskWidth, int maskHeight,
2251 if(states[statepos].textRender & 4) //clipped
2254 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2255 colorMap?"colorMap":"no colorMap",
2256 maskWidth, maskHeight);
2258 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2259 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2260 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2263 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2264 int width, int height,
2265 GfxImageColorMap *colorMap,
2267 int maskWidth, int maskHeight,
2268 GfxImageColorMap *maskColorMap)
2270 if(states[statepos].textRender & 4) //clipped
2273 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2274 colorMap?"colorMap":"no colorMap",
2275 maskWidth, maskHeight);
2277 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2278 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2279 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2282 static char* dirseparator()
2291 void addGlobalFont(char*filename)
2294 memset(&f, 0, sizeof(fontfile_t));
2295 f.filename = filename;
2296 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2297 msg("<verbose> Adding font \"%s\".", filename);
2298 fonts[fontnum++] = f;
2300 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2304 void addGlobalLanguageDir(char*dir)
2307 globalParams = new GlobalParams("");
2309 msg("<notice> Adding %s to language pack directories", dir);
2313 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2314 strcpy(config_file, dir);
2315 strcat(config_file, dirseparator());
2316 strcat(config_file, "add-to-xpdfrc");
2318 fi = fopen(config_file, "rb");
2320 msg("<error> Could not open %s", config_file);
2323 globalParams->parseFile(new GString(config_file), fi);
2327 void addGlobalFontDir(char*dirname)
2329 #ifdef HAVE_DIRENT_H
2330 msg("<notice> Adding %s to font directories", dirname);
2331 lastfontdir = strdup(dirname);
2332 DIR*dir = opendir(dirname);
2334 msg("<warning> Couldn't open directory %s\n", dirname);
2339 ent = readdir (dir);
2343 char*name = ent->d_name;
2349 if(!strncasecmp(&name[l-4], ".pfa", 4))
2351 if(!strncasecmp(&name[l-4], ".pfb", 4))
2353 if(!strncasecmp(&name[l-4], ".ttf", 4))
2357 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2358 strcpy(fontname, dirname);
2359 strcat(fontname, dirseparator());
2360 strcat(fontname, name);
2361 addGlobalFont(fontname);
2366 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2370 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2376 this->pagebuflen = 1024;
2377 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2378 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2380 while(pdfpage >= this->pagebuflen)
2382 int oldlen = this->pagebuflen;
2383 this->pagebuflen+=1024;
2384 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2385 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2388 this->pages[pdfpage] = outputpage;
2389 if(pdfpage>this->pagepos)
2390 this->pagepos = pdfpage;
2397 delete globalParams;globalParams=0;
2398 Object::memCheck(stderr);