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);
260 char* plus = strchr(fontid, '+');
261 if(plus && plus < &fontid[strlen(fontid)-1]) {
262 fontname = strdup(plus+1);
264 fontname = strdup(fontid);
270 static char mybuf[1024];
271 static char* gfxstate2str(GfxState *state)
275 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
282 if(state->getX1()!=0.0)
283 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
284 if(state->getY1()!=0.0)
285 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
286 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
287 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
288 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
289 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
290 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
291 state->getFillColor()->c[0], state->getFillColor()->c[1]);
292 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
293 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
294 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
295 state->getFillColor()->c[0], state->getFillColor()->c[1],
296 state->getFillColor()->c[2], state->getFillColor()->c[3],
297 state->getFillColor()->c[4], state->getFillColor()->c[5],
298 state->getFillColor()->c[6], state->getFillColor()->c[7]);
299 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
300 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
301 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
302 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
303 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
304 state->getFillRGB(&rgb);
305 if(rgb.r || rgb.g || rgb.b)
306 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
307 state->getStrokeRGB(&rgb);
308 if(rgb.r || rgb.g || rgb.b)
309 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
310 if(state->getFillColorSpace()->getNComps()>1)
311 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
312 if(state->getStrokeColorSpace()->getNComps()>1)
313 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
314 if(state->getFillPattern())
315 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
316 if(state->getStrokePattern())
317 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
319 if(state->getFillOpacity()!=1.0)
320 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
321 if(state->getStrokeOpacity()!=1.0)
322 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
324 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
329 state->getLineDash(&dash, &length, &start);
333 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
334 for(t=0;t<length;t++) {
335 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
337 bufpos+=sprintf(bufpos,"]");
340 if(state->getFlatness()!=1)
341 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
342 if(state->getLineJoin()!=0)
343 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
344 if(state->getLineJoin()!=0)
345 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
346 if(state->getLineJoin()!=0)
347 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
349 if(state->getFont() && getFontID(state->getFont()))
350 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
351 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
352 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
353 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
354 if(state->getCharSpace())
355 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
356 if(state->getWordSpace())
357 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
358 if(state->getHorizScaling()!=1.0)
359 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
360 if(state->getLeading())
361 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
363 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
364 if(state->getRender())
365 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
366 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
367 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
368 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
369 if(state->getLineX())
370 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
371 if(state->getLineY())
372 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
373 bufpos+=sprintf(bufpos," ");
377 static void dumpFontInfo(char*loglevel, GfxFont*font);
378 static int lastdumps[1024];
379 static int lastdumppos = 0;
384 static void showFontError(GfxFont*font, int nr)
388 for(t=0;t<lastdumppos;t++)
389 if(lastdumps[t] == r->num)
393 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
394 lastdumps[lastdumppos++] = r->num;
396 msg("<warning> The following font caused problems:");
398 msg("<warning> The following font caused problems (substituting):");
400 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
401 dumpFontInfo("<warning>", font);
404 static void dumpFontInfo(char*loglevel, GfxFont*font)
406 char* id = getFontID(font);
407 char* name = getFontName(font);
408 Ref* r=font->getID();
409 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
411 GString*gstr = font->getTag();
413 msg("%s| Tag: %s\n", loglevel, id);
415 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
417 GfxFontType type=font->getType();
419 case fontUnknownType:
420 msg("%s| Type: unknown\n",loglevel);
423 msg("%s| Type: 1\n",loglevel);
426 msg("%s| Type: 1C\n",loglevel);
429 msg("%s| Type: 3\n",loglevel);
432 msg("%s| Type: TrueType\n",loglevel);
435 msg("%s| Type: CIDType0\n",loglevel);
438 msg("%s| Type: CIDType0C\n",loglevel);
441 msg("%s| Type: CIDType2\n",loglevel);
446 GBool embedded = font->getEmbeddedFontID(&embRef);
448 if(font->getEmbeddedFontName()) {
449 embeddedName = font->getEmbeddedFontName()->getCString();
452 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
454 gstr = font->getExtFontFile();
456 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
458 // Get font descriptor flags.
459 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
460 if(font->isSerif()) msg("%s| is serif\n", loglevel);
461 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
462 if(font->isItalic()) msg("%s| is italic\n", loglevel);
463 if(font->isBold()) msg("%s| is bold\n", loglevel);
469 //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");}
470 //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");}
473 void dump_outline(gfxline_t*line)
476 if(line->type == gfx_moveTo) {
477 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
478 } else if(line->type == gfx_lineTo) {
479 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
480 } else if(line->type == gfx_splineTo) {
481 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
487 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
489 int num = path->getNumSubpaths();
492 double lastx=0,lasty=0,posx=0,posy=0;
495 msg("<warning> empty path");
499 gfxdrawer_target_gfxline(&draw);
501 for(t = 0; t < num; t++) {
502 GfxSubpath *subpath = path->getSubpath(t);
503 int subnum = subpath->getNumPoints();
504 double bx=0,by=0,cx=0,cy=0;
506 for(s=0;s<subnum;s++) {
509 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
514 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
515 draw.lineTo(&draw, lastx, lasty);
517 draw.moveTo(&draw, x,y);
522 } else if(subpath->getCurve(s) && cpos==0) {
526 } else if(subpath->getCurve(s) && cpos==1) {
534 draw.lineTo(&draw, x,y);
536 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
543 /* fix non-closed lines */
544 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
545 draw.lineTo(&draw, lastx, lasty);
547 gfxline_t*result = (gfxline_t*)draw.result(&draw);
551 /*----------------------------------------------------------------------------
552 * Primitive Graphic routines
553 *----------------------------------------------------------------------------*/
555 void GFXOutputDev::stroke(GfxState *state)
557 GfxPath * path = state->getPath();
558 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
559 strokeGfxline(state, line);
563 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
565 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
566 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
567 double miterLimit = state->getMiterLimit();
568 double width = state->getTransformedLineWidth();
571 double opaq = state->getStrokeOpacity();
573 state->getFillRGB(&rgb);
575 state->getStrokeRGB(&rgb);
577 col.r = colToByte(rgb.r);
578 col.g = colToByte(rgb.g);
579 col.b = colToByte(rgb.b);
580 col.a = (unsigned char)(opaq*255);
582 gfx_capType capType = gfx_capRound;
583 if(lineCap == 0) capType = gfx_capButt;
584 else if(lineCap == 1) capType = gfx_capRound;
585 else if(lineCap == 2) capType = gfx_capSquare;
587 gfx_joinType joinType = gfx_joinRound;
588 if(lineJoin == 0) joinType = gfx_joinMiter;
589 else if(lineJoin == 1) joinType = gfx_joinRound;
590 else if(lineJoin == 2) joinType = gfx_joinBevel;
593 double dashphase = 0;
595 state->getLineDash(&ldash, &dashnum, &dashphase);
599 if(dashnum && ldash) {
600 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
604 msg("<trace> %d dashes", dashnum);
605 msg("<trace> | phase: %f", dashphase);
606 for(t=0;t<dashnum;t++) {
608 msg("<trace> | d%-3d: %f", t, ldash[t]);
611 if(getLogLevel() >= LOGLEVEL_TRACE) {
615 line2 = gfxtool_dash_line(line, dash, dashphase);
618 msg("<trace> After dashing:");
621 if(getLogLevel() >= LOGLEVEL_TRACE) {
622 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
624 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
625 lineCap==0?"butt": (lineJoin==1?"round":"square"),
627 col.r,col.g,col.b,col.a
632 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
633 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
643 gfxcolor_t getFillColor(GfxState * state)
646 double opaq = state->getFillOpacity();
647 state->getFillRGB(&rgb);
649 col.r = colToByte(rgb.r);
650 col.g = colToByte(rgb.g);
651 col.b = colToByte(rgb.b);
652 col.a = (unsigned char)(opaq*255);
656 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
658 gfxcolor_t col = getFillColor(state);
660 if(getLogLevel() >= LOGLEVEL_TRACE) {
661 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
665 device->fill(device, line, &col);
667 void GFXOutputDev::fill(GfxState *state)
669 GfxPath * path = state->getPath();
670 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
671 fillGfxLine(state, line);
674 void GFXOutputDev::eoFill(GfxState *state)
676 GfxPath * path = state->getPath();
677 gfxcolor_t col = getFillColor(state);
679 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
681 if(getLogLevel() >= LOGLEVEL_TRACE) {
682 msg("<trace> eofill\n");
686 device->fill(device, line, &col);
690 void GFXOutputDev::clip(GfxState *state)
692 GfxPath * path = state->getPath();
693 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
694 clipToGfxLine(state, line);
698 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
700 if(getLogLevel() >= LOGLEVEL_TRACE) {
701 msg("<trace> clip\n");
705 device->startclip(device, line);
706 states[statepos].clipping++;
708 void GFXOutputDev::eoClip(GfxState *state)
710 GfxPath * path = state->getPath();
711 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
713 if(getLogLevel() >= LOGLEVEL_TRACE) {
714 msg("<trace> eoclip\n");
718 device->startclip(device, line);
719 states[statepos].clipping++;
723 void GFXOutputDev::endframe()
726 device->endclip(device);
730 device->endpage(device);
733 void GFXOutputDev::finish()
737 device->endclip(device);
743 GFXOutputDev::~GFXOutputDev()
748 free(this->pages); this->pages = 0;
751 fontlist_t*l = this->fontlist;
753 fontlist_t*next = l->next;
755 gfxfont_free(l->font);
756 free(l->filename);l->filename=0;
762 GBool GFXOutputDev::upsideDown()
766 GBool GFXOutputDev::useDrawChar()
771 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
772 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
774 #define RENDER_FILL 0
775 #define RENDER_STROKE 1
776 #define RENDER_FILLSTROKE 2
777 #define RENDER_INVISIBLE 3
778 #define RENDER_CLIP 4
780 static char tmp_printstr[4096];
781 char* makeStringPrintable(char*str)
783 int len = strlen(str);
798 tmp_printstr[len++] = '.';
799 tmp_printstr[len++] = '.';
800 tmp_printstr[len++] = '.';
802 tmp_printstr[len] = 0;
807 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
812 /* find out char name from unicode index
813 TODO: should be precomputed
815 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
816 if(nameToUnicodeTab[t].u == u) {
817 uniname = nameToUnicodeTab[t].name;
825 for(t=0;t<font->num_glyphs;t++) {
826 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
827 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
831 /* if we didn't find the character, maybe
832 we can find the capitalized version */
833 for(t=0;t<font->num_glyphs;t++) {
834 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
835 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
843 for(t=0;t<font->num_glyphs;t++) {
844 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
845 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
849 /* if we didn't find the character, maybe
850 we can find the capitalized version */
851 for(t=0;t<font->num_glyphs;t++) {
852 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
853 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
859 /* try to use the unicode id */
860 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
861 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
862 return font->unicode2glyph[u];
865 if(charnr>=0 && charnr<font->num_glyphs) {
866 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
874 void GFXOutputDev::beginString(GfxState *state, GString *s)
876 int render = state->getRender();
877 if(current_text_stroke) {
878 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
881 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
882 double m11,m21,m12,m22;
883 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
884 state->getFontTransMat(&m11, &m12, &m21, &m22);
885 m11 *= state->getHorizScaling();
886 m21 *= state->getHorizScaling();
888 this->current_font_matrix.m00 = m11 / 1024.0;
889 this->current_font_matrix.m01 = m12 / 1024.0;
890 this->current_font_matrix.m10 = -m21 / 1024.0;
891 this->current_font_matrix.m11 = -m22 / 1024.0;
892 this->current_font_matrix.tx = 0;
893 this->current_font_matrix.ty = 0;
895 gfxmatrix_t m = this->current_font_matrix;
897 /*if(render != 3 && render != 0)
898 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
899 states[statepos].textRender = render;
902 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
903 double dx, double dy,
904 double originX, double originY,
905 CharCode c, int nBytes, Unicode *_u, int uLen)
907 int render = state->getRender();
908 // check for invisible text -- this is used by Acrobat Capture
910 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
914 if(states[statepos].textRender != render)
915 msg("<error> Internal error: drawChar.render!=beginString.render");
917 gfxcolor_t col = getFillColor(state);
919 Gushort *CIDToGIDMap = 0;
920 GfxFont*font = state->getFont();
922 if(font->getType() == fontType3) {
923 /* type 3 chars are passed as graphics */
924 msg("<debug> type3 char at %f/%f", x, y);
934 if(font->isCIDFont()) {
935 GfxCIDFont*cfont = (GfxCIDFont*)font;
937 if(font->getType() == fontCIDType2)
938 CIDToGIDMap = cfont->getCIDToGID();
941 font8 = (Gfx8BitFont*)font;
942 char**enc=font8->getEncoding();
946 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);
949 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);
955 charid = getGfxCharID(current_gfxfont, c, name, u);
957 charid = getGfxCharID(current_gfxfont, c, name, -1);
960 /* multiple unicodes- should usually map to a ligature.
961 if the ligature doesn't exist, we need to draw
962 the characters one-by-one. */
964 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
965 for(t=0;t<uLen;t++) {
966 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
972 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
973 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
977 gfxmatrix_t m = this->current_font_matrix;
978 state->transform(x, y, &m.tx, &m.ty);
982 if(render == RENDER_FILL) {
983 device->drawchar(device, current_gfxfont, charid, &col, &m);
985 msg("<debug> Drawing glyph %d as shape", charid);
987 msg("<notice> Some texts will be rendered as shape");
990 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
991 gfxline_t*tglyph = gfxline_clone(glyph);
992 gfxline_transform(tglyph, &m);
993 if((render&3) != RENDER_INVISIBLE) {
994 gfxline_t*add = gfxline_clone(tglyph);
995 current_text_stroke = gfxline_append(current_text_stroke, add);
997 if(render&RENDER_CLIP) {
998 gfxline_t*add = gfxline_clone(tglyph);
999 current_text_clip = gfxline_append(current_text_clip, add);
1001 gfxline_free(tglyph);
1005 void GFXOutputDev::endString(GfxState *state)
1007 int render = state->getRender();
1008 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1009 if(states[statepos].textRender != render)
1010 msg("<error> Internal error: drawChar.render!=beginString.render");
1012 if(current_text_stroke) {
1013 /* fillstroke and stroke text rendering objects we can process right
1014 now (as there may be texts of other rendering modes in this
1015 text object)- clipping objects have to wait until endTextObject,
1017 device->setparameter(device, "mark","TXT");
1018 if((render&3) == RENDER_FILL) {
1019 fillGfxLine(state, current_text_stroke);
1020 gfxline_free(current_text_stroke);
1021 current_text_stroke = 0;
1022 } else if((render&3) == RENDER_FILLSTROKE) {
1023 fillGfxLine(state, current_text_stroke);
1024 strokeGfxline(state, current_text_stroke);
1025 gfxline_free(current_text_stroke);
1026 current_text_stroke = 0;
1027 } else if((render&3) == RENDER_STROKE) {
1028 strokeGfxline(state, current_text_stroke);
1029 gfxline_free(current_text_stroke);
1030 current_text_stroke = 0;
1032 device->setparameter(device, "mark","");
1036 void GFXOutputDev::endTextObject(GfxState *state)
1038 int render = state->getRender();
1039 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1040 if(states[statepos].textRender != render)
1041 msg("<error> Internal error: drawChar.render!=beginString.render");
1043 if(current_text_clip) {
1044 device->setparameter(device, "mark","TXT");
1045 clipToGfxLine(state, current_text_clip);
1046 device->setparameter(device, "mark","");
1047 gfxline_free(current_text_clip);
1048 current_text_clip = 0;
1052 /* the logic seems to be as following:
1053 first, beginType3Char is called, with the charcode and the coordinates.
1054 if this function returns true, it already knew about the char and has now drawn it.
1055 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1056 the all draw operations until endType3Char are part of the char (which in this moment is
1057 at the position first passed to beginType3Char). the char ends with endType3Char.
1059 The drawing operations between beginType3Char and endType3Char are somewhat different to
1060 the normal ones. For example, the fillcolor equals the stroke color.
1063 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1065 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1067 /* the character itself is going to be passed using the draw functions */
1068 return gFalse; /* gTrue= is_in_cache? */
1071 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1072 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1074 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1075 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1079 void GFXOutputDev::endType3Char(GfxState *state)
1082 msg("<debug> endType3Char");
1085 void GFXOutputDev::startFrame(int width, int height)
1087 device->startpage(device, width, height);
1090 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1092 this->currentpage = pageNum;
1094 int rot = doc->getPageRotate(1);
1097 gfxline_t clippath[5];
1099 white.r = white.g = white.b = white.a = 255;
1101 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1102 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1103 Use CropBox, not MediaBox, as page size
1110 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1111 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1113 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1114 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1117 /* apply user clip box */
1118 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1119 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1120 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1121 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1122 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1125 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1127 if(outer_clip_box) {
1128 device->endclip(device);
1132 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);
1134 msg("<verbose> page is rotated %d degrees\n", rot);
1136 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1137 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1138 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1139 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1140 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1141 device->startclip(device, clippath); outer_clip_box = 1;
1142 device->fill(device, clippath, &white);
1145 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1147 double x1, y1, x2, y2, w;
1148 gfxline_t points[5];
1151 msg("<debug> drawlink\n");
1153 link->getRect(&x1, &y1, &x2, &y2);
1154 cvtUserToDev(x1, y1, &x, &y);
1155 points[0].type = gfx_moveTo;
1156 points[0].x = points[4].x = x + user_movex;
1157 points[0].y = points[4].y = y + user_movey;
1158 points[0].next = &points[1];
1159 cvtUserToDev(x2, y1, &x, &y);
1160 points[1].type = gfx_lineTo;
1161 points[1].x = x + user_movex;
1162 points[1].y = y + user_movey;
1163 points[1].next = &points[2];
1164 cvtUserToDev(x2, y2, &x, &y);
1165 points[2].type = gfx_lineTo;
1166 points[2].x = x + user_movex;
1167 points[2].y = y + user_movey;
1168 points[2].next = &points[3];
1169 cvtUserToDev(x1, y2, &x, &y);
1170 points[3].type = gfx_lineTo;
1171 points[3].x = x + user_movex;
1172 points[3].y = y + user_movey;
1173 points[3].next = &points[4];
1174 cvtUserToDev(x1, y1, &x, &y);
1175 points[4].type = gfx_lineTo;
1176 points[4].x = x + user_movex;
1177 points[4].y = y + user_movey;
1180 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1181 points[0].x, points[0].y,
1182 points[1].x, points[1].y,
1183 points[2].x, points[2].y,
1184 points[3].x, points[3].y);
1186 LinkAction*action=link->getAction();
1192 msg("<trace> drawlink action=%d\n", action->getKind());
1193 switch(action->getKind())
1197 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1198 LinkDest *dest=NULL;
1199 if (ha->getDest()==NULL)
1200 dest=catalog->findDest(ha->getNamedDest());
1201 else dest=ha->getDest();
1203 if (dest->isPageRef()){
1204 Ref pageref=dest->getPageRef();
1205 page=catalog->findPage(pageref.num,pageref.gen);
1207 else page=dest->getPageNum();
1208 sprintf(buf, "%d", page);
1215 LinkGoToR*l = (LinkGoToR*)action;
1216 GString*g = l->getNamedDest();
1218 s = strdup(g->getCString());
1223 LinkNamed*l = (LinkNamed*)action;
1224 GString*name = l->getName();
1226 s = strdup(name->lowerCase()->getCString());
1227 named = name->getCString();
1230 if(strstr(s, "next") || strstr(s, "forward"))
1232 page = currentpage + 1;
1234 else if(strstr(s, "prev") || strstr(s, "back"))
1236 page = currentpage - 1;
1238 else if(strstr(s, "last") || strstr(s, "end"))
1240 if(pages && pagepos>0)
1241 page = pages[pagepos-1];
1243 else if(strstr(s, "first") || strstr(s, "top"))
1251 case actionLaunch: {
1253 LinkLaunch*l = (LinkLaunch*)action;
1254 GString * str = new GString(l->getFileName());
1255 GString * params = l->getParams();
1257 str->append(params);
1258 s = strdup(str->getCString());
1265 LinkURI*l = (LinkURI*)action;
1266 GString*g = l->getURI();
1268 url = g->getCString();
1273 case actionUnknown: {
1275 LinkUnknown*l = (LinkUnknown*)action;
1280 msg("<error> Unknown link type!\n");
1285 if(!s) s = strdup("-?-");
1287 msg("<trace> drawlink s=%s\n", s);
1289 if(!linkinfo && (page || s))
1291 msg("<notice> File contains links");
1299 for(t=1;t<=pagepos;t++) {
1300 if(pages[t]==page) {
1309 sprintf(buf, "page%d", lpage);
1310 device->drawlink(device, points, buf);
1314 device->drawlink(device, points, s);
1317 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1321 void GFXOutputDev::saveState(GfxState *state) {
1322 msg("<trace> saveState\n");
1325 msg("<error> Too many nested states in pdf.");
1329 states[statepos].clipping = 0; //? shouldn't this be the current value?
1330 states[statepos].textRender = states[statepos-1].textRender;
1333 void GFXOutputDev::restoreState(GfxState *state) {
1334 msg("<trace> restoreState\n");
1336 while(states[statepos].clipping) {
1337 device->endclip(device);
1338 states[statepos].clipping--;
1343 char* GFXOutputDev::searchFont(char*name)
1347 int is_standard_font = 0;
1349 msg("<verbose> SearchFont(%s)", name);
1351 /* see if it is a pdf standard font */
1352 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1354 if(!strcmp(name, pdf2t1map[i].pdffont))
1356 name = pdf2t1map[i].filename;
1357 is_standard_font = 1;
1361 /* look in all font files */
1362 for(i=0;i<fontnum;i++)
1364 if(strstr(fonts[i].filename, name))
1366 if(!fonts[i].used) {
1369 if(!is_standard_font)
1370 msg("<notice> Using %s for %s", fonts[i].filename, name);
1372 return strdup(fonts[i].filename);
1378 void GFXOutputDev::updateLineWidth(GfxState *state)
1380 double width = state->getTransformedLineWidth();
1381 //swfoutput_setlinewidth(&device, width);
1384 void GFXOutputDev::updateLineCap(GfxState *state)
1386 int c = state->getLineCap();
1389 void GFXOutputDev::updateLineJoin(GfxState *state)
1391 int j = state->getLineJoin();
1394 void GFXOutputDev::updateFillColor(GfxState *state)
1397 double opaq = state->getFillOpacity();
1398 state->getFillRGB(&rgb);
1400 //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1403 void GFXOutputDev::updateStrokeColor(GfxState *state)
1406 double opaq = state->getStrokeOpacity();
1407 state->getStrokeRGB(&rgb);
1408 //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1411 void FoFiWrite(void *stream, char *data, int len)
1413 fwrite(data, len, 1, (FILE*)stream);
1416 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1418 char*tmpFileName = NULL;
1424 Object refObj, strObj;
1426 tmpFileName = mktmpname(namebuf);
1429 ret = font->getEmbeddedFontID(&embRef);
1431 msg("<verbose> Didn't get embedded font id");
1432 /* not embedded- the caller should now search the font
1433 directories for this font */
1437 f = fopen(tmpFileName, "wb");
1439 msg("<error> Couldn't create temporary Type 1 font file");
1443 /*if(font->isCIDFont()) {
1444 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1445 GString c = cidFont->getCollection();
1446 msg("<notice> Collection: %s", c.getCString());
1449 //if (font->getType() == fontType1C) {
1450 if (0) { //font->getType() == fontType1C) {
1451 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1453 msg("<error> Couldn't read embedded font file");
1456 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1458 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1459 //cvt->convertToCIDType0("test", f);
1460 //cvt->convertToType0("test", f);
1463 } else if(font->getType() == fontTrueType) {
1464 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1465 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1467 msg("<error> Couldn't read embedded font file");
1470 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1471 cvt->writeTTF(FoFiWrite, f);
1475 font->getEmbeddedFontID(&embRef);
1476 refObj.initRef(embRef.num, embRef.gen);
1477 refObj.fetch(ref, &strObj);
1479 strObj.streamReset();
1484 f4[t] = strObj.streamGetChar();
1485 f4c[t] = (char)f4[t];
1490 if(!strncmp(f4c, "true", 4)) {
1491 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1492 Change this on the fly */
1493 f4[0] = f4[2] = f4[3] = 0;
1501 while ((c = strObj.streamGetChar()) != EOF) {
1505 strObj.streamClose();
1510 return strdup(tmpFileName);
1513 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1515 char*name = getFontName(gfxFont);
1519 if(!this->config_use_fontconfig)
1522 #ifdef HAVE_FONTCONFIG
1523 FcPattern *pattern, *match;
1527 static int fcinitcalled = false;
1529 msg("<debug> searchForSuitableFont(%s)", name);
1531 // call init ony once
1532 if (!fcinitcalled) {
1533 msg("<debug> Initializing FontConfig...");
1534 fcinitcalled = true;
1536 msg("<debug> FontConfig Initialization failed. Disabling.");
1537 config_use_fontconfig = 0;
1540 msg("<debug> ...initialized FontConfig");
1543 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1544 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1545 if (gfxFont->isItalic()) // check for italic
1546 msg("<debug> FontConfig: Adding Italic Slant");
1547 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1548 if (gfxFont->isBold()) // check for bold
1549 msg("<debug> FontConfig: Adding Bold Weight");
1550 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1552 msg("<debug> FontConfig: Try to match...");
1553 // configure and match using the original font name
1554 FcConfigSubstitute(0, pattern, FcMatchPattern);
1555 FcDefaultSubstitute(pattern);
1556 match = FcFontMatch(0, pattern, &result);
1558 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1559 msg("<debug> FontConfig: family=%s", (char*)v);
1560 // if we get an exact match
1561 if (strcmp((char *)v, name) == 0) {
1562 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1563 filename = strdup((char*)v); // mem leak
1564 char *nfn = strrchr(filename, '/');
1565 if(nfn) fontname = strdup(nfn+1);
1566 else fontname = filename;
1568 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1570 // initialize patterns
1571 FcPatternDestroy(pattern);
1572 FcPatternDestroy(match);
1574 // now match against serif etc.
1575 if (gfxFont->isSerif()) {
1576 msg("<debug> FontConfig: Create Serif Family Pattern");
1577 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1578 } else if (gfxFont->isFixedWidth()) {
1579 msg("<debug> FontConfig: Create Monospace Family Pattern");
1580 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1582 msg("<debug> FontConfig: Create Sans Family Pattern");
1583 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1587 if (gfxFont->isItalic()) {
1588 msg("<debug> FontConfig: Adding Italic Slant");
1589 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1592 if (gfxFont->isBold()) {
1593 msg("<debug> FontConfig: Adding Bold Weight");
1594 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1597 msg("<debug> FontConfig: Try to match... (2)");
1598 // configure and match using serif etc
1599 FcConfigSubstitute (0, pattern, FcMatchPattern);
1600 FcDefaultSubstitute (pattern);
1601 match = FcFontMatch (0, pattern, &result);
1603 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1604 filename = strdup((char*)v); // mem leak
1605 char *nfn = strrchr(filename, '/');
1606 if(nfn) fontname = strdup(nfn+1);
1607 else fontname = filename;
1609 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1613 //printf("FONTCONFIG: pattern");
1614 //FcPatternPrint(pattern);
1615 //printf("FONTCONFIG: match");
1616 //FcPatternPrint(match);
1618 FcPatternDestroy(pattern);
1619 FcPatternDestroy(match);
1621 pdfswf_addfont(filename);
1628 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1630 char*fontname = 0, *filename = 0;
1631 msg("<notice> substituteFont(%s)", oldname);
1633 if(!(fontname = searchForSuitableFont(gfxFont))) {
1634 fontname = "Times-Roman";
1636 filename = searchFont(fontname);
1638 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1642 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1643 msg("<fatal> Too many fonts in file.");
1647 substitutesource[substitutepos] = strdup(oldname); //mem leak
1648 substitutetarget[substitutepos] = fontname;
1649 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1652 return strdup(filename); //mem leak
1655 void unlinkfont(char* filename)
1662 if(!strncmp(&filename[l-4],".afm",4)) {
1663 memcpy(&filename[l-4],".pfb",4);
1665 memcpy(&filename[l-4],".pfa",4);
1667 memcpy(&filename[l-4],".afm",4);
1670 if(!strncmp(&filename[l-4],".pfa",4)) {
1671 memcpy(&filename[l-4],".afm",4);
1673 memcpy(&filename[l-4],".pfa",4);
1676 if(!strncmp(&filename[l-4],".pfb",4)) {
1677 memcpy(&filename[l-4],".afm",4);
1679 memcpy(&filename[l-4],".pfb",4);
1684 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1690 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1693 fontlist_t*last=0,*l = this->fontlist;
1696 msg("<error> Internal Error: FontID is null");
1698 /* TODO: should this be part of the state? */
1701 if(!strcmp(l->font->id, id)) {
1702 current_gfxfont = l->font;
1704 device->addfont(device, current_gfxfont);
1709 if(!filename) return 0;
1711 /* A font size of e.g. 9 means the font will be scaled down by
1712 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1713 we have to divide 0.05 by (fontsize/1024)
1715 double quality = (1024 * 0.05) / maxSize;
1717 msg("<verbose> Loading %s...", filename);
1718 font = gfxfont_load(id, filename, quality);
1719 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1723 l->filename = strdup(filename);
1725 current_gfxfont = l->font;
1731 device->addfont(device, current_gfxfont);
1735 void GFXOutputDev::updateFont(GfxState *state)
1737 GfxFont*gfxFont = state->getFont();
1743 char * fontid = getFontID(gfxFont);
1744 char * fontname = getFontName(gfxFont);
1746 double maxSize = 1.0;
1749 maxSize = this->info->getMaximumFontSize(fontid);
1753 /* first, look if we substituted this font before-
1754 this way, we don't initialize the T1 Fonts
1756 for(t=0;t<substitutepos;t++) {
1757 if(!strcmp(fontid, substitutesource[t])) {
1758 free(fontid);fontid=0;
1759 fontid = strdup(substitutetarget[t]);
1764 /* second, see if this is a font which was used before-
1765 if so, we are done */
1766 if(setGfxFont(fontid, fontname, 0, 0)) {
1771 /* if(swfoutput_queryfont(&device, fontid))
1772 swfoutput_setfont(&device, fontid, 0);
1774 msg("<debug> updateFont(%s) [cached]", fontid);
1778 // look for Type 3 font
1779 if (gfxFont->getType() == fontType3) {
1781 type3Warning = gTrue;
1782 showFontError(gfxFont, 2);
1789 /* now either load the font, or find a substitution */
1792 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1797 (gfxFont->getType() == fontType1 ||
1798 gfxFont->getType() == fontType1C ||
1799 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1800 gfxFont->getType() == fontTrueType ||
1801 gfxFont->getType() == fontCIDType2
1804 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1805 if(!fileName) showFontError(gfxFont,0);
1808 fileName = searchFont(fontname);
1809 if(!fileName) showFontError(gfxFont,0);
1812 char * fontname = getFontName(gfxFont);
1813 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1816 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1818 msg("<warning> Try specifying one or more font directories");
1820 fileName = substituteFont(gfxFont, fontid);
1823 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1824 msg("<notice> Font is now %s (%s)", fontid, fileName);
1828 msg("<error> Couldn't set font %s\n", fontid);
1834 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1835 dumpFontInfo("<verbose>", gfxFont);
1837 //swfoutput_setfont(&device, fontid, fileName);
1839 if(!setGfxFont(fontid, fontname, 0, 0)) {
1840 setGfxFont(fontid, fontname, fileName, maxSize);
1844 unlinkfont(fileName);
1854 #define SQR(x) ((x)*(x))
1856 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1858 if((newwidth<2 || newheight<2) ||
1859 (width<=newwidth || height<=newheight))
1861 unsigned char*newdata;
1863 newdata= (unsigned char*)malloc(newwidth*newheight);
1865 double fx = (double)(width)/newwidth;
1866 double fy = (double)(height)/newheight;
1868 int blocksize = (int)(8192/(fx*fy));
1869 int r = 8192*256/palettesize;
1870 for(x=0;x<newwidth;x++) {
1871 double ex = px + fx;
1872 int fromx = (int)px;
1874 int xweight1 = (int)(((fromx+1)-px)*256);
1875 int xweight2 = (int)((ex-tox)*256);
1877 for(y=0;y<newheight;y++) {
1878 double ey = py + fy;
1879 int fromy = (int)py;
1881 int yweight1 = (int)(((fromy+1)-py)*256);
1882 int yweight2 = (int)((ey-toy)*256);
1885 for(xx=fromx;xx<=tox;xx++)
1886 for(yy=fromy;yy<=toy;yy++) {
1887 int b = 1-data[width*yy+xx];
1889 if(xx==fromx) weight = (weight*xweight1)/256;
1890 if(xx==tox) weight = (weight*xweight2)/256;
1891 if(yy==fromy) weight = (weight*yweight1)/256;
1892 if(yy==toy) weight = (weight*yweight2)/256;
1895 //if(a) a=(palettesize-1)*r/blocksize;
1896 newdata[y*newwidth+x] = (a*blocksize)/r;
1904 #define IMAGE_TYPE_JPEG 0
1905 #define IMAGE_TYPE_LOSSLESS 1
1907 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1908 double x1,double y1,
1909 double x2,double y2,
1910 double x3,double y3,
1911 double x4,double y4, int type)
1913 gfxcolor_t*newpic=0;
1915 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1916 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1918 gfxline_t p1,p2,p3,p4,p5;
1919 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1920 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1921 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1922 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1923 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1925 {p1.x = (int)(p1.x*20)/20.0;
1926 p1.y = (int)(p1.y*20)/20.0;
1927 p2.x = (int)(p2.x*20)/20.0;
1928 p2.y = (int)(p2.y*20)/20.0;
1929 p3.x = (int)(p3.x*20)/20.0;
1930 p3.y = (int)(p3.y*20)/20.0;
1931 p4.x = (int)(p4.x*20)/20.0;
1932 p4.y = (int)(p4.y*20)/20.0;
1933 p5.x = (int)(p5.x*20)/20.0;
1934 p5.y = (int)(p5.y*20)/20.0;
1941 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1942 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1947 img.data = (gfxcolor_t*)data;
1951 if(type == IMAGE_TYPE_JPEG)
1952 /* TODO: pass image_dpi to device instead */
1953 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1955 dev->fillbitmap(dev, &p1, &img, &m, 0);
1958 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1959 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1961 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1964 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1965 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1967 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1971 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1972 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1973 GBool inlineImg, int mask, int*maskColors,
1974 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1976 double x1,y1,x2,y2,x3,y3,x4,y4;
1977 ImageStream *imgStr;
1982 unsigned char* maskbitmap = 0;
1985 ncomps = colorMap->getNumPixelComps();
1986 bits = colorMap->getBits();
1991 unsigned char buf[8];
1992 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1994 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1995 imgMaskStr->reset();
1996 unsigned char pal[256];
1997 int n = 1 << colorMap->getBits();
2002 maskColorMap->getGray(pixBuf, &gray);
2003 pal[t] = colToByte(gray);
2005 for (y = 0; y < maskHeight; y++) {
2006 for (x = 0; x < maskWidth; x++) {
2007 imgMaskStr->getPixel(buf);
2008 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2013 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2014 imgMaskStr->reset();
2015 for (y = 0; y < maskHeight; y++) {
2016 for (x = 0; x < maskWidth; x++) {
2017 imgMaskStr->getPixel(buf);
2019 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2027 imgStr = new ImageStream(str, width, ncomps,bits);
2030 if(!width || !height || (height<=1 && width<=1))
2032 msg("<verbose> Ignoring %d by %d image", width, height);
2033 unsigned char buf[8];
2035 for (y = 0; y < height; ++y)
2036 for (x = 0; x < width; ++x) {
2037 imgStr->getPixel(buf);
2045 state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
2046 state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
2047 state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
2048 state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
2051 if(!pbminfo && !(str->getKind()==strDCT)) {
2053 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2057 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2059 if(!jpeginfo && (str->getKind()==strDCT)) {
2060 msg("<notice> file contains jpeg pictures");
2066 unsigned char buf[8];
2068 unsigned char*pic = new unsigned char[width*height];
2069 gfxcolor_t pal[256];
2071 state->getFillRGB(&rgb);
2073 memset(pal,255,sizeof(pal));
2074 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2075 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2076 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2077 pal[0].a = 255; pal[1].a = 0;
2080 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2081 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2082 for (y = 0; y < height; ++y)
2083 for (x = 0; x < width; ++x)
2085 imgStr->getPixel(buf);
2088 pic[width*y+x] = buf[0];
2091 /* the size of the drawn image is added to the identifier
2092 as the same image may require different bitmaps if displayed
2093 at different sizes (due to antialiasing): */
2096 unsigned char*pic2 = 0;
2099 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2108 height = realheight;
2112 /* make a black/white palette */
2114 float r = 255/(numpalette-1);
2116 for(t=0;t<numpalette;t++) {
2117 pal[t].r = colToByte(rgb.r);
2118 pal[t].g = colToByte(rgb.g);
2119 pal[t].b = colToByte(rgb.b);
2120 pal[t].a = (unsigned char)(t*r);
2124 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2125 for (y = 0; y < height; ++y) {
2126 for (x = 0; x < width; ++x) {
2127 pic2[width*y+x] = pal[pic[y*width+x]];
2130 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2134 if(maskbitmap) free(maskbitmap);
2140 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2141 gfxcolor_t*pic=new gfxcolor_t[width*height];
2142 for (y = 0; y < height; ++y) {
2143 for (x = 0; x < width; ++x) {
2144 imgStr->getPixel(pixBuf);
2145 colorMap->getRGB(pixBuf, &rgb);
2146 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2147 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2148 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2149 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2151 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2155 if(str->getKind()==strDCT)
2156 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2158 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2161 if(maskbitmap) free(maskbitmap);
2164 gfxcolor_t*pic=new gfxcolor_t[width*height];
2165 gfxcolor_t pal[256];
2166 int n = 1 << colorMap->getBits();
2168 for(t=0;t<256;t++) {
2170 colorMap->getRGB(pixBuf, &rgb);
2172 {/*if(maskColors && *maskColors==t) {
2173 msg("<notice> Color %d is transparent", t);
2174 if (imgData->maskColors) {
2176 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2177 if (pix[i] < imgData->maskColors[2*i] ||
2178 pix[i] > imgData->maskColors[2*i+1]) {
2193 pal[t].r = (unsigned char)(colToByte(rgb.r));
2194 pal[t].g = (unsigned char)(colToByte(rgb.g));
2195 pal[t].b = (unsigned char)(colToByte(rgb.b));
2196 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2199 for (y = 0; y < height; ++y) {
2200 for (x = 0; x < width; ++x) {
2201 imgStr->getPixel(pixBuf);
2202 pic[width*y+x] = pal[pixBuf[0]];
2204 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2208 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2212 if(maskbitmap) free(maskbitmap);
2217 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2218 int width, int height, GBool invert,
2221 if(states[statepos].textRender & 4) //clipped
2223 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2224 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2227 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2228 int width, int height, GfxImageColorMap *colorMap,
2229 int *maskColors, GBool inlineImg)
2231 if(states[statepos].textRender & 4) //clipped
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 if(states[statepos].textRender & 4) //clipped
2253 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2254 colorMap?"colorMap":"no colorMap",
2255 maskWidth, maskHeight);
2257 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2258 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2259 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2262 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2263 int width, int height,
2264 GfxImageColorMap *colorMap,
2266 int maskWidth, int maskHeight,
2267 GfxImageColorMap *maskColorMap)
2269 if(states[statepos].textRender & 4) //clipped
2272 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2273 colorMap?"colorMap":"no colorMap",
2274 maskWidth, maskHeight);
2276 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2277 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2278 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2281 static char* dirseparator()
2290 void addGlobalFont(char*filename)
2293 memset(&f, 0, sizeof(fontfile_t));
2294 f.filename = filename;
2295 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2296 msg("<verbose> Adding font \"%s\".", filename);
2297 fonts[fontnum++] = f;
2299 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2303 void addGlobalLanguageDir(char*dir)
2306 globalParams = new GlobalParams("");
2308 msg("<notice> Adding %s to language pack directories", dir);
2312 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2313 strcpy(config_file, dir);
2314 strcat(config_file, dirseparator());
2315 strcat(config_file, "add-to-xpdfrc");
2317 fi = fopen(config_file, "rb");
2319 msg("<error> Could not open %s", config_file);
2322 globalParams->parseFile(new GString(config_file), fi);
2326 void addGlobalFontDir(char*dirname)
2328 #ifdef HAVE_DIRENT_H
2329 msg("<notice> Adding %s to font directories", dirname);
2330 lastfontdir = strdup(dirname);
2331 DIR*dir = opendir(dirname);
2333 msg("<warning> Couldn't open directory %s\n", dirname);
2338 ent = readdir (dir);
2342 char*name = ent->d_name;
2348 if(!strncasecmp(&name[l-4], ".pfa", 4))
2350 if(!strncasecmp(&name[l-4], ".pfb", 4))
2352 if(!strncasecmp(&name[l-4], ".ttf", 4))
2356 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2357 strcpy(fontname, dirname);
2358 strcat(fontname, dirseparator());
2359 strcat(fontname, name);
2360 addGlobalFont(fontname);
2365 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2369 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2375 this->pagebuflen = 1024;
2376 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2377 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2379 while(pdfpage >= this->pagebuflen)
2381 int oldlen = this->pagebuflen;
2382 this->pagebuflen+=1024;
2383 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2384 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2387 this->pages[pdfpage] = outputpage;
2388 if(pdfpage>this->pagepos)
2389 this->pagepos = pdfpage;
2396 delete globalParams;globalParams=0;
2397 Object::memCheck(stderr);