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 */
26 #include "../../config.h"
31 #ifdef HAVE_SYS_STAT_H
34 #ifdef HAVE_FONTCONFIG
35 #include <fontconfig.h>
52 #include "OutputDev.h"
55 #include "CharCodeToUnicode.h"
56 #include "NameToUnicodeTable.h"
57 #include "GlobalParams.h"
58 #include "FoFiType1C.h"
59 #include "FoFiTrueType.h"
61 #include "GFXOutputDev.h"
63 //swftools header files
65 #include "../gfxdevice.h"
66 #include "../gfxtools.h"
67 #include "../gfxfont.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/arts.h"
71 #include "../devices/render.h"
73 #include "../art/libart.h"
74 #include "../devices/artsutils.c"
81 typedef struct _fontfile
88 static fontfile_t fonts[2048];
89 static int fontnum = 0;
93 static char* lastfontdir = 0;
104 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
105 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
106 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
107 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
108 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
109 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
110 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
111 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
112 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
113 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
114 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
115 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
116 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
117 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
120 static int verbose = 0;
121 static int dbgindent = 0;
122 static void dbg(const char*format, ...)
129 va_start(arglist, format);
130 vsprintf(buf, format, arglist);
133 while(l && buf[l-1]=='\n') {
138 int indent = dbgindent;
148 typedef struct _feature
151 struct _feature*next;
153 feature_t*featurewarnings = 0;
155 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
157 feature_t*f = featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = featurewarnings;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->textRender = 0;
189 this->createsoftmask = 0;
190 this->transparencygroup = 0;
192 this->grouprecording = 0;
196 GBool GFXOutputDev::interpretType3Chars()
198 return this->do_interpretType3Chars;
201 typedef struct _drawnchar
219 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
220 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
225 free(chars);chars = 0;
232 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
236 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
239 chars[num_chars].x = x;
240 chars[num_chars].y = y;
241 chars[num_chars].color = color;
242 chars[num_chars].charid = charid;
246 static char*getFontID(GfxFont*font);
248 GFXOutputDev::GFXOutputDev(parameter_t*p)
251 this->textmodeinfo = 0;
254 this->type3active = 0;
257 this->substitutepos = 0;
258 this->type3Warning = 0;
259 this->user_movex = 0;
260 this->user_movey = 0;
263 this->user_clipx1 = 0;
264 this->user_clipy1 = 0;
265 this->user_clipx2 = 0;
266 this->user_clipy2 = 0;
267 this->current_text_stroke = 0;
268 this->current_text_clip = 0;
270 this->outer_clip_box = 0;
272 this->pagebuflen = 0;
274 this->config_use_fontconfig=1;
275 this->config_break_on_warning=0;
276 this->do_interpretType3Chars = gTrue;
278 this->parameters = p;
280 memset(states, 0, sizeof(states));
282 /* configure device */
284 setParameter(p->name, p->value);
289 void GFXOutputDev::setParameter(char*key, char*value)
291 if(!strcmp(key,"rawtext")) {
292 this->do_interpretType3Chars = atoi(value)^1;
293 } else if(!strcmp(key,"breakonwarning")) {
294 this->config_break_on_warning = atoi(value);
295 } else if(!strcmp(key,"fontconfig")) {
296 this->config_use_fontconfig = atoi(value);
298 msg("<warning> Ignored parameter: %s=%s", key, value);
302 void GFXOutputDev::setDevice(gfxdevice_t*dev)
304 parameter_t*p = this->parameters;
306 /* pass parameters to output device */
310 this->device->setparameter(this->device, p->name, p->value);
316 void GFXOutputDev::setMove(int x,int y)
318 this->user_movex = x;
319 this->user_movey = y;
322 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
324 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
325 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
327 this->user_clipx1 = x1;
328 this->user_clipy1 = y1;
329 this->user_clipx2 = x2;
330 this->user_clipy2 = y2;
333 static char*getFontID(GfxFont*font)
335 Ref*ref = font->getID();
336 GString*gstr = font->getName();
337 char* fname = gstr==0?0:gstr->getCString();
340 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
342 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
347 static char*getFontName(GfxFont*font)
350 GString*gstr = font->getName();
351 char* fname = gstr==0?0:gstr->getCString();
355 sprintf(buf, "UFONT%d", r->num);
356 fontid = strdup(buf);
358 fontid = strdup(fname);
362 char* plus = strchr(fontid, '+');
363 if(plus && plus < &fontid[strlen(fontid)-1]) {
364 fontname = strdup(plus+1);
366 fontname = strdup(fontid);
372 static char mybuf[1024];
373 static char* gfxstate2str(GfxState *state)
377 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
384 if(state->getX1()!=0.0)
385 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
386 if(state->getY1()!=0.0)
387 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
388 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
389 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
390 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
391 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
392 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
393 state->getFillColor()->c[0], state->getFillColor()->c[1]);
394 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
395 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
396 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
397 state->getFillColor()->c[0], state->getFillColor()->c[1],
398 state->getFillColor()->c[2], state->getFillColor()->c[3],
399 state->getFillColor()->c[4], state->getFillColor()->c[5],
400 state->getFillColor()->c[6], state->getFillColor()->c[7]);
401 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
402 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
403 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
404 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
405 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
406 state->getFillRGB(&rgb);
407 if(rgb.r || rgb.g || rgb.b)
408 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
409 state->getStrokeRGB(&rgb);
410 if(rgb.r || rgb.g || rgb.b)
411 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
412 if(state->getFillColorSpace()->getNComps()>1)
413 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
414 if(state->getStrokeColorSpace()->getNComps()>1)
415 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
416 if(state->getFillPattern())
417 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
418 if(state->getStrokePattern())
419 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
421 if(state->getFillOpacity()!=1.0)
422 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
423 if(state->getStrokeOpacity()!=1.0)
424 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
426 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
431 state->getLineDash(&dash, &length, &start);
435 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
436 for(t=0;t<length;t++) {
437 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
439 bufpos+=sprintf(bufpos,"]");
442 if(state->getFlatness()!=1)
443 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
444 if(state->getLineJoin()!=0)
445 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
446 if(state->getLineJoin()!=0)
447 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
448 if(state->getLineJoin()!=0)
449 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
451 if(state->getFont() && getFontID(state->getFont()))
452 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
453 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
454 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
455 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
456 if(state->getCharSpace())
457 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
458 if(state->getWordSpace())
459 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
460 if(state->getHorizScaling()!=1.0)
461 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
462 if(state->getLeading())
463 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
465 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
466 if(state->getRender())
467 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
468 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
469 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
470 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
471 if(state->getLineX())
472 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
473 if(state->getLineY())
474 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
475 bufpos+=sprintf(bufpos," ");
479 static void dumpFontInfo(const char*loglevel, GfxFont*font);
480 static int lastdumps[1024];
481 static int lastdumppos = 0;
486 static void showFontError(GfxFont*font, int nr)
490 for(t=0;t<lastdumppos;t++)
491 if(lastdumps[t] == r->num)
495 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
496 lastdumps[lastdumppos++] = r->num;
498 msg("<warning> The following font caused problems:");
500 msg("<warning> The following font caused problems (substituting):");
502 msg("<warning> The following Type 3 Font will be rendered as graphics:");
503 dumpFontInfo("<warning>", font);
506 static void dumpFontInfo(const char*loglevel, GfxFont*font)
508 char* id = getFontID(font);
509 char* name = getFontName(font);
510 Ref* r=font->getID();
511 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
513 GString*gstr = font->getTag();
515 msg("%s| Tag: %s\n", loglevel, id);
517 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
519 GfxFontType type=font->getType();
521 case fontUnknownType:
522 msg("%s| Type: unknown\n",loglevel);
525 msg("%s| Type: 1\n",loglevel);
528 msg("%s| Type: 1C\n",loglevel);
531 msg("%s| Type: 3\n",loglevel);
534 msg("%s| Type: TrueType\n",loglevel);
537 msg("%s| Type: CIDType0\n",loglevel);
540 msg("%s| Type: CIDType0C\n",loglevel);
543 msg("%s| Type: CIDType2\n",loglevel);
548 GBool embedded = font->getEmbeddedFontID(&embRef);
550 if(font->getEmbeddedFontName()) {
551 embeddedName = font->getEmbeddedFontName()->getCString();
554 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
556 gstr = font->getExtFontFile();
558 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
560 // Get font descriptor flags.
561 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
562 if(font->isSerif()) msg("%s| is serif\n", loglevel);
563 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
564 if(font->isItalic()) msg("%s| is italic\n", loglevel);
565 if(font->isBold()) msg("%s| is bold\n", loglevel);
571 //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");}
572 //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");}
575 void dump_outline(gfxline_t*line)
578 if(line->type == gfx_moveTo) {
579 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
580 } else if(line->type == gfx_lineTo) {
581 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
582 } else if(line->type == gfx_splineTo) {
583 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
589 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
591 int num = path->getNumSubpaths();
594 double lastx=0,lasty=0,posx=0,posy=0;
597 msg("<warning> empty path");
601 gfxdrawer_target_gfxline(&draw);
603 for(t = 0; t < num; t++) {
604 GfxSubpath *subpath = path->getSubpath(t);
605 int subnum = subpath->getNumPoints();
606 double bx=0,by=0,cx=0,cy=0;
608 for(s=0;s<subnum;s++) {
611 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
616 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
617 draw.lineTo(&draw, lastx, lasty);
619 draw.moveTo(&draw, x,y);
624 } else if(subpath->getCurve(s) && cpos==0) {
628 } else if(subpath->getCurve(s) && cpos==1) {
636 draw.lineTo(&draw, x,y);
638 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
645 /* fix non-closed lines */
646 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
647 draw.lineTo(&draw, lastx, lasty);
649 gfxline_t*result = (gfxline_t*)draw.result(&draw);
651 gfxline_optimize(result);
656 GBool GFXOutputDev::useTilingPatternFill()
658 infofeature("tiled patterns");
662 GBool GFXOutputDev::useShadedFills()
664 infofeature("shaded fills");
668 #define STROKE_FILL 1
669 #define STROKE_CLIP 2
670 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
672 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
673 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
674 double miterLimit = state->getMiterLimit();
675 double width = state->getTransformedLineWidth();
678 double opaq = state->getStrokeOpacity();
680 state->getFillRGB(&rgb);
682 state->getStrokeRGB(&rgb);
684 col.r = colToByte(rgb.r);
685 col.g = colToByte(rgb.g);
686 col.b = colToByte(rgb.b);
687 col.a = (unsigned char)(opaq*255);
689 gfx_capType capType = gfx_capRound;
690 if(lineCap == 0) capType = gfx_capButt;
691 else if(lineCap == 1) capType = gfx_capRound;
692 else if(lineCap == 2) capType = gfx_capSquare;
694 gfx_joinType joinType = gfx_joinRound;
695 if(lineJoin == 0) joinType = gfx_joinMiter;
696 else if(lineJoin == 1) joinType = gfx_joinRound;
697 else if(lineJoin == 2) joinType = gfx_joinBevel;
700 double dashphase = 0;
702 state->getLineDash(&ldash, &dashnum, &dashphase);
706 if(dashnum && ldash) {
707 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
711 msg("<trace> %d dashes", dashnum);
712 msg("<trace> | phase: %f", dashphase);
713 for(t=0;t<dashnum;t++) {
715 msg("<trace> | d%-3d: %f", t, ldash[t]);
718 if(getLogLevel() >= LOGLEVEL_TRACE) {
722 line2 = gfxtool_dash_line(line, dash, dashphase);
725 msg("<trace> After dashing:");
728 if(getLogLevel() >= LOGLEVEL_TRACE) {
729 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
731 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
732 lineCap==0?"butt": (lineJoin==1?"round":"square"),
734 col.r,col.g,col.b,col.a
739 if(flags&STROKE_FILL) {
740 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
741 gfxline_t*gfxline = SVPtogfxline(svp);
742 if(flags&STROKE_CLIP) {
743 device->startclip(device, gfxline);
744 states[statepos].clipping++;
746 device->fill(device, gfxline, &col);
751 if(flags&STROKE_CLIP)
752 msg("<error> Stroke&clip not supported at the same time");
753 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
760 gfxcolor_t getFillColor(GfxState * state)
763 double opaq = state->getFillOpacity();
764 state->getFillRGB(&rgb);
766 col.r = colToByte(rgb.r);
767 col.g = colToByte(rgb.g);
768 col.b = colToByte(rgb.b);
769 col.a = (unsigned char)(opaq*255);
773 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
775 gfxcolor_t col = getFillColor(state);
777 if(getLogLevel() >= LOGLEVEL_TRACE) {
778 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
781 device->fill(device, line, &col);
784 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
786 if(getLogLevel() >= LOGLEVEL_TRACE) {
787 msg("<trace> clip\n");
791 device->startclip(device, line);
792 states[statepos].clipping++;
795 void GFXOutputDev::clip(GfxState *state)
797 GfxPath * path = state->getPath();
798 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
799 clipToGfxLine(state, line);
803 void GFXOutputDev::eoClip(GfxState *state)
805 GfxPath * path = state->getPath();
806 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
808 if(getLogLevel() >= LOGLEVEL_TRACE) {
809 msg("<trace> eoclip\n");
813 device->startclip(device, line);
814 states[statepos].clipping++;
817 void GFXOutputDev::clipToStrokePath(GfxState *state)
819 GfxPath * path = state->getPath();
820 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
821 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
825 void GFXOutputDev::endframe()
828 device->endclip(device);
833 void GFXOutputDev::finish()
837 device->endclip(device);
843 GFXOutputDev::~GFXOutputDev()
848 free(this->pages); this->pages = 0;
851 fontlist_t*l = this->fontlist;
853 fontlist_t*next = l->next;
855 gfxfont_free(l->font);
856 free(l->filename);l->filename=0;
862 GBool GFXOutputDev::upsideDown()
866 GBool GFXOutputDev::useDrawChar()
871 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
872 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
874 #define RENDER_FILL 0
875 #define RENDER_STROKE 1
876 #define RENDER_FILLSTROKE 2
877 #define RENDER_INVISIBLE 3
878 #define RENDER_CLIP 4
880 static char tmp_printstr[4096];
881 char* makeStringPrintable(char*str)
883 int len = strlen(str);
898 tmp_printstr[len++] = '.';
899 tmp_printstr[len++] = '.';
900 tmp_printstr[len++] = '.';
902 tmp_printstr[len] = 0;
907 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
909 const char*uniname = 0;
914 /* find out char name from unicode index
915 TODO: should be precomputed
917 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
918 if(nameToUnicodeTab[t].u == u) {
919 uniname = nameToUnicodeTab[t].name;
927 for(t=0;t<font->num_glyphs;t++) {
928 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
929 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
933 /* if we didn't find the character, maybe
934 we can find the capitalized version */
935 for(t=0;t<font->num_glyphs;t++) {
936 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
937 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
945 for(t=0;t<font->num_glyphs;t++) {
946 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
947 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
951 /* if we didn't find the character, maybe
952 we can find the capitalized version */
953 for(t=0;t<font->num_glyphs;t++) {
954 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
955 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
961 /* try to use the unicode id */
962 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
963 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
964 return font->unicode2glyph[u];
966 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
967 FIXME: do this only if we know the font is wingdings?
970 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
971 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
972 return font->unicode2glyph[u];
975 if(charnr>=0 && charnr<font->num_glyphs) {
976 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
984 void GFXOutputDev::beginString(GfxState *state, GString *s)
986 int render = state->getRender();
987 if(current_text_stroke) {
988 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
991 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
992 double m11,m21,m12,m22;
993 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
994 state->getFontTransMat(&m11, &m12, &m21, &m22);
995 m11 *= state->getHorizScaling();
996 m21 *= state->getHorizScaling();
998 this->current_font_matrix.m00 = m11 / 1024.0;
999 this->current_font_matrix.m01 = m12 / 1024.0;
1000 this->current_font_matrix.m10 = -m21 / 1024.0;
1001 this->current_font_matrix.m11 = -m22 / 1024.0;
1002 this->current_font_matrix.tx = 0;
1003 this->current_font_matrix.ty = 0;
1005 gfxmatrix_t m = this->current_font_matrix;
1007 states[statepos].textRender = render;
1010 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1011 double dx, double dy,
1012 double originX, double originY,
1013 CharCode c, int nBytes, Unicode *_u, int uLen)
1015 int render = state->getRender();
1016 // check for invisible text -- this is used by Acrobat Capture
1018 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1022 if(states[statepos].textRender != render)
1023 msg("<error> Internal error: drawChar.render!=beginString.render");
1025 gfxcolor_t col = getFillColor(state);
1027 Gushort *CIDToGIDMap = 0;
1028 GfxFont*font = state->getFont();
1030 if(font->getType() == fontType3 && do_interpretType3Chars) {
1031 /* type 3 chars are passed as graphics */
1032 msg("<debug> type3 char at %f/%f", x, y);
1042 /* char*fontname = getFontName(font);
1043 if(u<256 && strstr(fontname, "ingdings")) {
1044 // symbols are at 0xe000 in the unicode table
1049 if(font->isCIDFont()) {
1050 GfxCIDFont*cfont = (GfxCIDFont*)font;
1052 if(font->getType() == fontCIDType2)
1053 CIDToGIDMap = cfont->getCIDToGID();
1056 font8 = (Gfx8BitFont*)font;
1057 char**enc=font8->getEncoding();
1061 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);
1064 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);
1070 charid = getGfxCharID(current_gfxfont, c, name, u);
1072 charid = getGfxCharID(current_gfxfont, c, name, -1);
1075 /* multiple unicodes- should usually map to a ligature.
1076 if the ligature doesn't exist, we need to draw
1077 the characters one-by-one. */
1079 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1080 for(t=0;t<uLen;t++) {
1081 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1087 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1088 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1092 gfxmatrix_t m = this->current_font_matrix;
1093 state->transform(x, y, &m.tx, &m.ty);
1094 m.tx += user_movex + clipmovex;
1095 m.ty += user_movey + clipmovey;
1097 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1099 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1103 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1109 msg("<warning> Drawing empty character charid=%d", charid);
1113 if(render == RENDER_FILL) {
1114 device->drawchar(device, current_gfxfont, charid, &col, &m);
1116 msg("<debug> Drawing glyph %d as shape", charid);
1118 msg("<notice> Some texts will be rendered as shape");
1121 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1122 gfxline_t*tglyph = gfxline_clone(glyph);
1123 gfxline_transform(tglyph, &m);
1124 if((render&3) != RENDER_INVISIBLE) {
1125 gfxline_t*add = gfxline_clone(tglyph);
1126 current_text_stroke = gfxline_append(current_text_stroke, add);
1128 if(render&RENDER_CLIP) {
1129 gfxline_t*add = gfxline_clone(tglyph);
1130 current_text_clip = gfxline_append(current_text_clip, add);
1132 gfxline_free(tglyph);
1136 void GFXOutputDev::endString(GfxState *state)
1138 int render = state->getRender();
1139 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1140 if(states[statepos].textRender != render)
1141 msg("<error> Internal error: drawChar.render!=beginString.render");
1143 if(current_text_stroke) {
1144 /* fillstroke and stroke text rendering objects we can process right
1145 now (as there may be texts of other rendering modes in this
1146 text object)- clipping objects have to wait until endTextObject,
1148 device->setparameter(device, "mark","TXT");
1149 if((render&3) == RENDER_FILL) {
1150 fillGfxLine(state, current_text_stroke);
1151 gfxline_free(current_text_stroke);
1152 current_text_stroke = 0;
1153 } else if((render&3) == RENDER_FILLSTROKE) {
1154 fillGfxLine(state, current_text_stroke);
1155 strokeGfxline(state, current_text_stroke,0);
1156 gfxline_free(current_text_stroke);
1157 current_text_stroke = 0;
1158 } else if((render&3) == RENDER_STROKE) {
1159 strokeGfxline(state, current_text_stroke,0);
1160 gfxline_free(current_text_stroke);
1161 current_text_stroke = 0;
1163 device->setparameter(device, "mark","");
1167 void GFXOutputDev::endTextObject(GfxState *state)
1169 int render = state->getRender();
1170 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1171 if(states[statepos].textRender != render)
1172 msg("<error> Internal error: drawChar.render!=beginString.render");
1174 if(current_text_clip) {
1175 device->setparameter(device, "mark","TXT");
1176 clipToGfxLine(state, current_text_clip);
1177 device->setparameter(device, "mark","");
1178 gfxline_free(current_text_clip);
1179 current_text_clip = 0;
1183 /* the logic seems to be as following:
1184 first, beginType3Char is called, with the charcode and the coordinates.
1185 if this function returns true, it already knew about the char and has now drawn it.
1186 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1187 the all draw operations until endType3Char are part of the char (which in this moment is
1188 at the position first passed to beginType3Char). the char ends with endType3Char.
1190 The drawing operations between beginType3Char and endType3Char are somewhat different to
1191 the normal ones. For example, the fillcolor equals the stroke color.
1194 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1196 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1201 gfxcolor_t col={255,0,0,0};
1202 gfxmatrix_t m = {1,0,0, 0,1,0};
1204 for(t=0;t<uLen;t++) {
1205 device->drawchar(device, 0, u[t], &col, &m);
1208 /* the character itself is going to be passed using the draw functions */
1209 return gFalse; /* gTrue= is_in_cache? */
1212 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1213 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1215 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1216 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1220 void GFXOutputDev::endType3Char(GfxState *state)
1223 msg("<debug> endType3Char");
1226 void GFXOutputDev::startFrame(int width, int height)
1228 if(outer_clip_box) {
1229 device->endclip(device);
1233 device->startpage(device, width, height);
1234 this->width = width;
1235 this->height = height;
1238 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1240 this->currentpage = pageNum;
1242 int rot = doc->getPageRotate(1);
1245 gfxline_t clippath[5];
1247 white.r = white.g = white.b = white.a = 255;
1249 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1250 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1251 Use CropBox, not MediaBox, as page size
1258 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1259 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1261 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1262 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1264 this->clipmovex = -(int)x1;
1265 this->clipmovey = -(int)y1;
1267 /* apply user clip box */
1268 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1269 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1270 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1271 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1272 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1273 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1276 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1278 msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex + clipmovex, user_movey + clipmovey);
1280 msg("<verbose> page is rotated %d degrees\n", rot);
1282 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1283 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1284 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1285 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1286 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1287 device->startclip(device, clippath); outer_clip_box = 1;
1288 device->fill(device, clippath, &white);
1292 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1294 double x1, y1, x2, y2, w;
1295 gfxline_t points[5];
1298 msg("<debug> drawlink\n");
1300 link->getRect(&x1, &y1, &x2, &y2);
1301 cvtUserToDev(x1, y1, &x, &y);
1302 points[0].type = gfx_moveTo;
1303 points[0].x = points[4].x = x + user_movex + clipmovex;
1304 points[0].y = points[4].y = y + user_movey + clipmovey;
1305 points[0].next = &points[1];
1306 cvtUserToDev(x2, y1, &x, &y);
1307 points[1].type = gfx_lineTo;
1308 points[1].x = x + user_movex + clipmovex;
1309 points[1].y = y + user_movey + clipmovey;
1310 points[1].next = &points[2];
1311 cvtUserToDev(x2, y2, &x, &y);
1312 points[2].type = gfx_lineTo;
1313 points[2].x = x + user_movex + clipmovex;
1314 points[2].y = y + user_movey + clipmovey;
1315 points[2].next = &points[3];
1316 cvtUserToDev(x1, y2, &x, &y);
1317 points[3].type = gfx_lineTo;
1318 points[3].x = x + user_movex + clipmovex;
1319 points[3].y = y + user_movey + clipmovey;
1320 points[3].next = &points[4];
1321 cvtUserToDev(x1, y1, &x, &y);
1322 points[4].type = gfx_lineTo;
1323 points[4].x = x + user_movex + clipmovex;
1324 points[4].y = y + user_movey + clipmovey;
1327 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1328 points[0].x, points[0].y,
1329 points[1].x, points[1].y,
1330 points[2].x, points[2].y,
1331 points[3].x, points[3].y);
1333 LinkAction*action=link->getAction();
1336 const char*type = "-?-";
1339 msg("<trace> drawlink action=%d\n", action->getKind());
1340 switch(action->getKind())
1344 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1345 LinkDest *dest=NULL;
1346 if (ha->getDest()==NULL)
1347 dest=catalog->findDest(ha->getNamedDest());
1348 else dest=ha->getDest();
1350 if (dest->isPageRef()){
1351 Ref pageref=dest->getPageRef();
1352 page=catalog->findPage(pageref.num,pageref.gen);
1354 else page=dest->getPageNum();
1355 sprintf(buf, "%d", page);
1362 LinkGoToR*l = (LinkGoToR*)action;
1363 GString*g = l->getFileName();
1365 s = strdup(g->getCString());
1367 /* if the GoToR link has no filename, then
1368 try to find a refernce in the *local*
1370 GString*g = l->getNamedDest();
1372 s = strdup(g->getCString());
1378 LinkNamed*l = (LinkNamed*)action;
1379 GString*name = l->getName();
1381 s = strdup(name->lowerCase()->getCString());
1382 named = name->getCString();
1385 if(strstr(s, "next") || strstr(s, "forward"))
1387 page = currentpage + 1;
1389 else if(strstr(s, "prev") || strstr(s, "back"))
1391 page = currentpage - 1;
1393 else if(strstr(s, "last") || strstr(s, "end"))
1395 if(pages && pagepos>0)
1396 page = pages[pagepos-1];
1398 else if(strstr(s, "first") || strstr(s, "top"))
1406 case actionLaunch: {
1408 LinkLaunch*l = (LinkLaunch*)action;
1409 GString * str = new GString(l->getFileName());
1410 GString * params = l->getParams();
1412 str->append(params);
1413 s = strdup(str->getCString());
1420 LinkURI*l = (LinkURI*)action;
1421 GString*g = l->getURI();
1423 url = g->getCString();
1428 case actionUnknown: {
1430 LinkUnknown*l = (LinkUnknown*)action;
1435 msg("<error> Unknown link type!\n");
1440 if(!s) s = strdup("-?-");
1442 msg("<trace> drawlink s=%s\n", s);
1444 if(!linkinfo && (page || s))
1446 msg("<notice> File contains links");
1454 for(t=1;t<=pagepos;t++) {
1455 if(pages[t]==page) {
1464 sprintf(buf, "page%d", lpage);
1465 device->drawlink(device, points, buf);
1469 device->drawlink(device, points, s);
1472 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1476 void GFXOutputDev::saveState(GfxState *state) {
1477 dbg("saveState");dbgindent+=2;
1479 msg("<trace> saveState\n");
1482 msg("<error> Too many nested states in pdf.");
1486 states[statepos].textRender = states[statepos-1].textRender;
1487 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1488 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1489 states[statepos].clipping = 0;
1492 void GFXOutputDev::restoreState(GfxState *state) {
1493 dbgindent-=2; dbg("restoreState");
1496 msg("<error> Invalid restoreState");
1499 msg("<trace> restoreState");
1500 if(states[statepos].softmask) {
1501 clearSoftMask(state);
1504 while(states[statepos].clipping) {
1505 device->endclip(device);
1506 states[statepos].clipping--;
1511 char* writeOutStdFont(fontentry* f)
1516 char* tmpFileName = mktmpname(namebuf1);
1518 sprintf(namebuf2, "%s.afm", tmpFileName);
1519 fi = fopen(namebuf2, "wb");
1522 fwrite(f->afm, 1, f->afmlen, fi);
1525 sprintf(namebuf2, "%s.pfb", tmpFileName);
1526 fi = fopen(namebuf2, "wb");
1529 fwrite(f->pfb, 1, f->pfblen, fi);
1532 return strdup(namebuf2);
1535 char* GFXOutputDev::searchFont(const char*name)
1540 msg("<verbose> SearchFont(%s)", name);
1542 /* see if it is a pdf standard font */
1543 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1545 if(!strcmp(name, pdf2t1map[i].pdffont))
1547 if(!pdf2t1map[i].fullfilename) {
1548 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1549 if(!pdf2t1map[i].fullfilename) {
1550 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1552 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1555 return strdup(pdf2t1map[i].fullfilename);
1558 /* else look in all font files */
1559 for(i=0;i<fontnum;i++)
1561 if(strstr(fonts[i].filename, name)) {
1562 return strdup(fonts[i].filename);
1568 void GFXOutputDev::updateLineWidth(GfxState *state)
1570 double width = state->getTransformedLineWidth();
1571 //swfoutput_setlinewidth(&device, width);
1574 void GFXOutputDev::updateLineCap(GfxState *state)
1576 int c = state->getLineCap();
1579 void GFXOutputDev::updateLineJoin(GfxState *state)
1581 int j = state->getLineJoin();
1584 void GFXOutputDev::updateFillColor(GfxState *state)
1587 double opaq = state->getFillOpacity();
1588 state->getFillRGB(&rgb);
1590 void GFXOutputDev::updateFillOpacity(GfxState *state)
1593 double opaq = state->getFillOpacity();
1594 state->getFillRGB(&rgb);
1595 dbg("update fillopaq %f", opaq);
1597 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1599 double opaq = state->getFillOpacity();
1600 dbg("update strokeopaq %f", opaq);
1602 void GFXOutputDev::updateFillOverprint(GfxState *state)
1604 double opaq = state->getFillOverprint();
1605 dbg("update filloverprint %f", opaq);
1607 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1609 double opaq = state->getStrokeOverprint();
1610 dbg("update strokeoverprint %f", opaq);
1612 void GFXOutputDev::updateTransfer(GfxState *state)
1614 dbg("update transfer");
1618 void GFXOutputDev::updateStrokeColor(GfxState *state)
1621 double opaq = state->getStrokeOpacity();
1622 state->getStrokeRGB(&rgb);
1625 void FoFiWrite(void *stream, char *data, int len)
1627 int ret = fwrite(data, len, 1, (FILE*)stream);
1630 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1632 char*tmpFileName = NULL;
1638 Object refObj, strObj;
1640 tmpFileName = mktmpname(namebuf);
1643 ret = font->getEmbeddedFontID(&embRef);
1645 msg("<verbose> Didn't get embedded font id");
1646 /* not embedded- the caller should now search the font
1647 directories for this font */
1651 f = fopen(tmpFileName, "wb");
1653 msg("<error> Couldn't create temporary Type 1 font file");
1657 /*if(font->isCIDFont()) {
1658 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1659 GString c = cidFont->getCollection();
1660 msg("<notice> Collection: %s", c.getCString());
1663 //if (font->getType() == fontType1C) {
1664 if (0) { //font->getType() == fontType1C) {
1665 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1667 msg("<error> Couldn't read embedded font file");
1670 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1672 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1673 //cvt->convertToCIDType0("test", f);
1674 //cvt->convertToType0("test", f);
1677 } else if(font->getType() == fontTrueType) {
1678 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1679 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1681 msg("<error> Couldn't read embedded font file");
1684 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1685 cvt->writeTTF(FoFiWrite, f);
1689 font->getEmbeddedFontID(&embRef);
1690 refObj.initRef(embRef.num, embRef.gen);
1691 refObj.fetch(ref, &strObj);
1693 strObj.streamReset();
1698 f4[t] = strObj.streamGetChar();
1699 f4c[t] = (char)f4[t];
1704 if(!strncmp(f4c, "true", 4)) {
1705 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1706 Change this on the fly */
1707 f4[0] = f4[2] = f4[3] = 0;
1715 while ((c = strObj.streamGetChar()) != EOF) {
1719 strObj.streamClose();
1724 return strdup(tmpFileName);
1727 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1729 char*name = getFontName(gfxFont);
1733 if(!this->config_use_fontconfig)
1736 #ifdef HAVE_FONTCONFIG
1737 FcPattern *pattern, *match;
1741 static int fcinitcalled = false;
1743 msg("<debug> searchForSuitableFont(%s)", name);
1745 // call init ony once
1746 if (!fcinitcalled) {
1747 msg("<debug> Initializing FontConfig...");
1748 fcinitcalled = true;
1750 msg("<debug> FontConfig Initialization failed. Disabling.");
1751 config_use_fontconfig = 0;
1754 msg("<debug> ...initialized FontConfig");
1757 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1758 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1759 if (gfxFont->isItalic()) // check for italic
1760 msg("<debug> FontConfig: Adding Italic Slant");
1761 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1762 if (gfxFont->isBold()) // check for bold
1763 msg("<debug> FontConfig: Adding Bold Weight");
1764 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1766 msg("<debug> FontConfig: Try to match...");
1767 // configure and match using the original font name
1768 FcConfigSubstitute(0, pattern, FcMatchPattern);
1769 FcDefaultSubstitute(pattern);
1770 match = FcFontMatch(0, pattern, &result);
1772 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1773 msg("<debug> FontConfig: family=%s", (char*)v);
1774 // if we get an exact match
1775 if (strcmp((char *)v, name) == 0) {
1776 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1777 filename = strdup((char*)v); // mem leak
1778 char *nfn = strrchr(filename, '/');
1779 if(nfn) fontname = strdup(nfn+1);
1780 else fontname = filename;
1782 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1784 // initialize patterns
1785 FcPatternDestroy(pattern);
1786 FcPatternDestroy(match);
1788 // now match against serif etc.
1789 if (gfxFont->isSerif()) {
1790 msg("<debug> FontConfig: Create Serif Family Pattern");
1791 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1792 } else if (gfxFont->isFixedWidth()) {
1793 msg("<debug> FontConfig: Create Monospace Family Pattern");
1794 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1796 msg("<debug> FontConfig: Create Sans Family Pattern");
1797 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1801 if (gfxFont->isItalic()) {
1802 msg("<debug> FontConfig: Adding Italic Slant");
1803 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1806 if (gfxFont->isBold()) {
1807 msg("<debug> FontConfig: Adding Bold Weight");
1808 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1811 msg("<debug> FontConfig: Try to match... (2)");
1812 // configure and match using serif etc
1813 FcConfigSubstitute (0, pattern, FcMatchPattern);
1814 FcDefaultSubstitute (pattern);
1815 match = FcFontMatch (0, pattern, &result);
1817 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1818 filename = strdup((char*)v); // mem leak
1819 char *nfn = strrchr(filename, '/');
1820 if(nfn) fontname = strdup(nfn+1);
1821 else fontname = filename;
1823 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1827 //printf("FONTCONFIG: pattern");
1828 //FcPatternPrint(pattern);
1829 //printf("FONTCONFIG: match");
1830 //FcPatternPrint(match);
1832 FcPatternDestroy(pattern);
1833 FcPatternDestroy(match);
1835 pdfswf_addfont(filename);
1842 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1844 const char*fontname = 0, *filename = 0;
1845 msg("<notice> substituteFont(%s)", oldname);
1847 if(!(fontname = searchForSuitableFont(gfxFont))) {
1848 fontname = "Times-Roman";
1850 filename = searchFont(fontname);
1852 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1856 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1857 msg("<fatal> Too many fonts in file.");
1861 substitutesource[substitutepos] = strdup(oldname); //mem leak
1862 substitutetarget[substitutepos] = fontname;
1863 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1866 return strdup(filename); //mem leak
1869 void unlinkfont(char* filename)
1876 if(!strncmp(&filename[l-4],".afm",4)) {
1877 memcpy(&filename[l-4],".pfb",4);
1879 memcpy(&filename[l-4],".pfa",4);
1881 memcpy(&filename[l-4],".afm",4);
1884 if(!strncmp(&filename[l-4],".pfa",4)) {
1885 memcpy(&filename[l-4],".afm",4);
1887 memcpy(&filename[l-4],".pfa",4);
1890 if(!strncmp(&filename[l-4],".pfb",4)) {
1891 memcpy(&filename[l-4],".afm",4);
1893 memcpy(&filename[l-4],".pfb",4);
1898 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1904 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1907 fontlist_t*last=0,*l = this->fontlist;
1910 msg("<error> Internal Error: FontID is null");
1912 /* TODO: should this be part of the state? */
1915 if(!strcmp(l->font->id, id)) {
1916 current_gfxfont = l->font;
1918 device->addfont(device, current_gfxfont);
1923 if(!filename) return 0;
1925 /* A font size of e.g. 9 means the font will be scaled down by
1926 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1927 we have to divide 0.05 by (fontsize/1024)
1929 double quality = (1024 * 0.05) / maxSize;
1931 msg("<verbose> Loading %s...", filename);
1932 font = gfxfont_load(id, filename, quality);
1934 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1937 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1941 l->filename = strdup(filename);
1943 current_gfxfont = l->font;
1949 device->addfont(device, current_gfxfont);
1953 void GFXOutputDev::updateFont(GfxState *state)
1955 GfxFont*gfxFont = state->getFont();
1961 char * fontid = getFontID(gfxFont);
1962 char * fontname = getFontName(gfxFont);
1964 double maxSize = 1.0;
1967 maxSize = this->info->getMaximumFontSize(fontid);
1971 /* first, look if we substituted this font before-
1972 this way, we don't initialize the T1 Fonts
1974 for(t=0;t<substitutepos;t++) {
1975 if(!strcmp(fontid, substitutesource[t])) {
1976 free(fontid);fontid=0;
1977 fontid = strdup(substitutetarget[t]);
1982 /* second, see if this is a font which was used before-
1983 if so, we are done */
1984 if(setGfxFont(fontid, fontname, 0, 0)) {
1989 /* if(swfoutput_queryfont(&device, fontid))
1990 swfoutput_setfont(&device, fontid, 0);
1992 msg("<debug> updateFont(%s) [cached]", fontid);
1996 // look for Type 3 font
1997 if (gfxFont->getType() == fontType3) {
1999 type3Warning = gTrue;
2000 showFontError(gfxFont, 2);
2007 /* now either load the font, or find a substitution */
2010 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2015 (gfxFont->getType() == fontType1 ||
2016 gfxFont->getType() == fontType1C ||
2017 gfxFont->getType() == fontCIDType0C ||
2018 gfxFont->getType() == fontTrueType ||
2019 gfxFont->getType() == fontCIDType2
2022 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2023 if(!fileName) showFontError(gfxFont,0);
2026 fileName = searchFont(fontname);
2027 if(!fileName) showFontError(gfxFont,0);
2030 char * fontname = getFontName(gfxFont);
2031 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2034 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2036 msg("<warning> Try specifying one or more font directories");
2038 fileName = substituteFont(gfxFont, fontid);
2041 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2042 msg("<notice> Font is now %s (%s)", fontid, fileName);
2046 msg("<error> Couldn't set font %s\n", fontid);
2052 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2053 dumpFontInfo("<verbose>", gfxFont);
2055 //swfoutput_setfont(&device, fontid, fileName);
2057 if(!setGfxFont(fontid, fontname, 0, 0)) {
2058 setGfxFont(fontid, fontname, fileName, maxSize);
2062 unlinkfont(fileName);
2072 #define SQR(x) ((x)*(x))
2074 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2076 if((newwidth<2 || newheight<2) ||
2077 (width<=newwidth || height<=newheight))
2079 unsigned char*newdata;
2081 newdata= (unsigned char*)malloc(newwidth*newheight);
2083 double fx = (double)(width)/newwidth;
2084 double fy = (double)(height)/newheight;
2086 int blocksize = (int)(8192/(fx*fy));
2087 int r = 8192*256/palettesize;
2088 for(x=0;x<newwidth;x++) {
2089 double ex = px + fx;
2090 int fromx = (int)px;
2092 int xweight1 = (int)(((fromx+1)-px)*256);
2093 int xweight2 = (int)((ex-tox)*256);
2095 for(y=0;y<newheight;y++) {
2096 double ey = py + fy;
2097 int fromy = (int)py;
2099 int yweight1 = (int)(((fromy+1)-py)*256);
2100 int yweight2 = (int)((ey-toy)*256);
2103 for(xx=fromx;xx<=tox;xx++)
2104 for(yy=fromy;yy<=toy;yy++) {
2105 int b = 1-data[width*yy+xx];
2107 if(xx==fromx) weight = (weight*xweight1)/256;
2108 if(xx==tox) weight = (weight*xweight2)/256;
2109 if(yy==fromy) weight = (weight*yweight1)/256;
2110 if(yy==toy) weight = (weight*yweight2)/256;
2113 //if(a) a=(palettesize-1)*r/blocksize;
2114 newdata[y*newwidth+x] = (a*blocksize)/r;
2122 #define IMAGE_TYPE_JPEG 0
2123 #define IMAGE_TYPE_LOSSLESS 1
2125 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2126 double x1,double y1,
2127 double x2,double y2,
2128 double x3,double y3,
2129 double x4,double y4, int type)
2131 gfxcolor_t*newpic=0;
2133 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2134 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2136 gfxline_t p1,p2,p3,p4,p5;
2137 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2138 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2139 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2140 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2141 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2143 {p1.x = (int)(p1.x*20)/20.0;
2144 p1.y = (int)(p1.y*20)/20.0;
2145 p2.x = (int)(p2.x*20)/20.0;
2146 p2.y = (int)(p2.y*20)/20.0;
2147 p3.x = (int)(p3.x*20)/20.0;
2148 p3.y = (int)(p3.y*20)/20.0;
2149 p4.x = (int)(p4.x*20)/20.0;
2150 p4.y = (int)(p4.y*20)/20.0;
2151 p5.x = (int)(p5.x*20)/20.0;
2152 p5.y = (int)(p5.y*20)/20.0;
2159 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2160 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2165 img.data = (gfxcolor_t*)data;
2169 if(type == IMAGE_TYPE_JPEG)
2170 /* TODO: pass image_dpi to device instead */
2171 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2173 dev->fillbitmap(dev, &p1, &img, &m, 0);
2176 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2177 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2179 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2182 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2183 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2185 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2189 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2190 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2191 GBool inlineImg, int mask, int*maskColors,
2192 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2194 double x1,y1,x2,y2,x3,y3,x4,y4;
2195 ImageStream *imgStr;
2200 unsigned char* maskbitmap = 0;
2203 ncomps = colorMap->getNumPixelComps();
2204 bits = colorMap->getBits();
2209 unsigned char buf[8];
2210 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2212 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2213 imgMaskStr->reset();
2214 unsigned char pal[256];
2215 int n = 1 << colorMap->getBits();
2220 maskColorMap->getGray(pixBuf, &gray);
2221 pal[t] = colToByte(gray);
2223 for (y = 0; y < maskHeight; y++) {
2224 for (x = 0; x < maskWidth; x++) {
2225 imgMaskStr->getPixel(buf);
2226 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2231 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2232 imgMaskStr->reset();
2233 for (y = 0; y < maskHeight; y++) {
2234 for (x = 0; x < maskWidth; x++) {
2235 imgMaskStr->getPixel(buf);
2237 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2245 imgStr = new ImageStream(str, width, ncomps,bits);
2248 if(!width || !height || (height<=1 && width<=1))
2250 msg("<verbose> Ignoring %d by %d image", width, height);
2251 unsigned char buf[8];
2253 for (y = 0; y < height; ++y)
2254 for (x = 0; x < width; ++x) {
2255 imgStr->getPixel(buf);
2263 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2264 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2265 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2266 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2268 if(!pbminfo && !(str->getKind()==strDCT)) {
2270 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2274 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2276 if(!jpeginfo && (str->getKind()==strDCT)) {
2277 msg("<notice> file contains jpeg pictures");
2283 unsigned char buf[8];
2285 unsigned char*pic = new unsigned char[width*height];
2286 gfxcolor_t pal[256];
2288 state->getFillRGB(&rgb);
2290 memset(pal,255,sizeof(pal));
2291 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2292 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2293 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2294 pal[0].a = 255; pal[1].a = 0;
2297 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2298 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2299 for (y = 0; y < height; ++y)
2300 for (x = 0; x < width; ++x)
2302 imgStr->getPixel(buf);
2305 pic[width*y+x] = buf[0];
2308 /* the size of the drawn image is added to the identifier
2309 as the same image may require different bitmaps if displayed
2310 at different sizes (due to antialiasing): */
2313 unsigned char*pic2 = 0;
2316 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2325 height = realheight;
2329 /* make a black/white palette */
2331 float r = 255/(numpalette-1);
2333 for(t=0;t<numpalette;t++) {
2334 pal[t].r = colToByte(rgb.r);
2335 pal[t].g = colToByte(rgb.g);
2336 pal[t].b = colToByte(rgb.b);
2337 pal[t].a = (unsigned char)(t*r);
2341 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2342 for (y = 0; y < height; ++y) {
2343 for (x = 0; x < width; ++x) {
2344 pic2[width*y+x] = pal[pic[y*width+x]];
2347 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2351 if(maskbitmap) free(maskbitmap);
2357 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2358 gfxcolor_t*pic=new gfxcolor_t[width*height];
2359 for (y = 0; y < height; ++y) {
2360 for (x = 0; x < width; ++x) {
2361 imgStr->getPixel(pixBuf);
2362 colorMap->getRGB(pixBuf, &rgb);
2363 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2364 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2365 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2366 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2368 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2372 if(str->getKind()==strDCT)
2373 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2375 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2378 if(maskbitmap) free(maskbitmap);
2381 gfxcolor_t*pic=new gfxcolor_t[width*height];
2382 gfxcolor_t pal[256];
2383 int n = 1 << colorMap->getBits();
2385 for(t=0;t<256;t++) {
2387 colorMap->getRGB(pixBuf, &rgb);
2389 {/*if(maskColors && *maskColors==t) {
2390 msg("<notice> Color %d is transparent", t);
2391 if (imgData->maskColors) {
2393 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2394 if (pix[i] < imgData->maskColors[2*i] ||
2395 pix[i] > imgData->maskColors[2*i+1]) {
2410 pal[t].r = (unsigned char)(colToByte(rgb.r));
2411 pal[t].g = (unsigned char)(colToByte(rgb.g));
2412 pal[t].b = (unsigned char)(colToByte(rgb.b));
2413 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2416 for (y = 0; y < height; ++y) {
2417 for (x = 0; x < width; ++x) {
2418 imgStr->getPixel(pixBuf);
2419 pic[width*y+x] = pal[pixBuf[0]];
2421 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2425 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2429 if(maskbitmap) free(maskbitmap);
2434 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2435 int width, int height, GBool invert,
2438 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2439 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2440 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2443 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2444 int width, int height, GfxImageColorMap *colorMap,
2445 int *maskColors, GBool inlineImg)
2447 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2448 colorMap?"colorMap":"no colorMap",
2449 maskColors?"maskColors":"no maskColors",
2451 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2452 colorMap?"colorMap":"no colorMap",
2453 maskColors?"maskColors":"no maskColors",
2456 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2457 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2458 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2461 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2462 int width, int height,
2463 GfxImageColorMap *colorMap,
2464 Stream *maskStr, int maskWidth, int maskHeight,
2467 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2468 colorMap?"colorMap":"no colorMap",
2469 maskWidth, maskHeight);
2470 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2471 colorMap?"colorMap":"no colorMap",
2472 maskWidth, maskHeight);
2474 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2475 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2476 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2479 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2480 int width, int height,
2481 GfxImageColorMap *colorMap,
2483 int maskWidth, int maskHeight,
2484 GfxImageColorMap *maskColorMap)
2486 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2487 colorMap?"colorMap":"no colorMap",
2488 maskWidth, maskHeight);
2489 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2490 colorMap?"colorMap":"no colorMap",
2491 maskWidth, maskHeight);
2493 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2494 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2495 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2498 void GFXOutputDev::stroke(GfxState *state)
2502 GfxPath * path = state->getPath();
2503 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2504 strokeGfxline(state, line, 0);
2508 void GFXOutputDev::fill(GfxState *state)
2510 gfxcolor_t col = getFillColor(state);
2511 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2513 GfxPath * path = state->getPath();
2514 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2515 fillGfxLine(state, line);
2519 void GFXOutputDev::eoFill(GfxState *state)
2521 gfxcolor_t col = getFillColor(state);
2522 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2524 GfxPath * path = state->getPath();
2525 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2526 fillGfxLine(state, line);
2531 static const char* dirseparator()
2540 void addGlobalFont(const char*filename)
2543 memset(&f, 0, sizeof(fontfile_t));
2544 f.filename = filename;
2545 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2546 msg("<verbose> Adding font \"%s\".", filename);
2547 fonts[fontnum++] = f;
2549 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2553 void addGlobalLanguageDir(const char*dir)
2556 globalParams = new GlobalParams((char*)"");
2558 msg("<notice> Adding %s to language pack directories", dir);
2562 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2563 strcpy(config_file, dir);
2564 strcat(config_file, dirseparator());
2565 strcat(config_file, "add-to-xpdfrc");
2567 fi = fopen(config_file, "rb");
2569 msg("<error> Could not open %s", config_file);
2572 globalParams->parseFile(new GString(config_file), fi);
2576 void addGlobalFontDir(const char*dirname)
2578 #ifdef HAVE_DIRENT_H
2579 msg("<notice> Adding %s to font directories", dirname);
2580 lastfontdir = strdup(dirname);
2581 DIR*dir = opendir(dirname);
2583 msg("<warning> Couldn't open directory %s\n", dirname);
2588 ent = readdir (dir);
2592 char*name = ent->d_name;
2598 if(!strncasecmp(&name[l-4], ".pfa", 4))
2600 if(!strncasecmp(&name[l-4], ".pfb", 4))
2602 if(!strncasecmp(&name[l-4], ".ttf", 4))
2606 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2607 strcpy(fontname, dirname);
2608 strcat(fontname, dirseparator());
2609 strcat(fontname, name);
2610 addGlobalFont(fontname);
2615 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2619 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2625 this->pagebuflen = 1024;
2626 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2627 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2629 while(pdfpage >= this->pagebuflen)
2631 int oldlen = this->pagebuflen;
2632 this->pagebuflen+=1024;
2633 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2634 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2637 this->pages[pdfpage] = outputpage;
2638 if(pdfpage>this->pagepos)
2639 this->pagepos = pdfpage;
2645 double width,height;
2648 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2650 double xMin, yMin, xMax, yMax, x, y;
2651 double tx, ty, w, h;
2652 // transform the bbox
2653 state->transform(bbox[0], bbox[1], &x, &y);
2656 state->transform(bbox[0], bbox[3], &x, &y);
2659 } else if (x > xMax) {
2664 } else if (y > yMax) {
2667 state->transform(bbox[2], bbox[1], &x, &y);
2670 } else if (x > xMax) {
2675 } else if (y > yMax) {
2678 state->transform(bbox[2], bbox[3], &x, &y);
2681 } else if (x > xMax) {
2686 } else if (y > yMax) {
2689 tx = (int)floor(xMin);
2692 } else if (tx > width) {
2695 ty = (int)floor(yMin);
2698 } else if (ty > height) {
2701 w = (int)ceil(xMax) - tx + 1;
2702 if (tx + w > width) {
2708 h = (int)ceil(yMax) - ty + 1;
2709 if (ty + h > height) {
2723 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2724 GfxColorSpace *blendingColorSpace,
2725 GBool isolated, GBool knockout,
2728 const char*colormodename = "";
2729 BBox rect = mkBBox(state, bbox, this->width, this->height);
2731 if(blendingColorSpace) {
2732 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2734 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2735 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2736 msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2738 states[statepos].createsoftmask |= forSoftMask;
2739 states[statepos].transparencygroup = !forSoftMask;
2740 states[statepos].isolated = isolated;
2742 states[statepos].olddevice = this->device;
2743 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2745 gfxdevice_record_init(this->device);
2747 /*if(!forSoftMask) { ////???
2748 state->setFillOpacity(0.0);
2753 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2756 dbg("endTransparencyGroup");
2757 msg("<verbose> endTransparencyGroup");
2759 gfxdevice_t*r = this->device;
2761 this->device = states[statepos].olddevice;
2763 if(states[statepos].createsoftmask) {
2764 states[statepos-1].softmaskrecording = r->finish(r);
2766 states[statepos-1].grouprecording = r->finish(r);
2769 states[statepos].createsoftmask = 0;
2770 states[statepos].transparencygroup = 0;
2774 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2776 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2777 "colordodge","colorburn","hardlight","softlight","difference",
2778 "exclusion","hue","saturation","color","luminosity"};
2780 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2781 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2783 if(state->getBlendMode() == gfxBlendNormal)
2784 infofeature("transparency groups");
2787 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2788 warnfeature(buffer, 0);
2791 gfxresult_t*grouprecording = states[statepos].grouprecording;
2793 if(state->getBlendMode() == gfxBlendNormal) {
2795 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2796 gfxresult_record_replay(grouprecording, &ops);
2799 grouprecording->destroy(grouprecording);
2801 states[statepos].grouprecording = 0;
2804 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2806 /* alpha = 1: retrieve mask values from alpha layer
2807 alpha = 0: retrieve mask values from luminance */
2808 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2809 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2810 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2811 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2813 infofeature("soft masks");
2815 warnfeature("soft masks from alpha channel",0);
2817 states[statepos].olddevice = this->device;
2818 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2819 gfxdevice_record_init(this->device);
2821 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2823 states[statepos].softmask = 1;
2824 states[statepos].softmask_alpha = alpha;
2827 static inline Guchar div255(int x) {
2828 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2831 void GFXOutputDev::clearSoftMask(GfxState *state)
2833 if(!states[statepos].softmask)
2835 states[statepos].softmask = 0;
2836 dbg("clearSoftMask statepos=%d", statepos);
2837 msg("<verbose> clearSoftMask");
2839 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2840 msg("<error> Error in softmask/tgroup ordering");
2844 gfxresult_t*mask = states[statepos].softmaskrecording;
2845 gfxresult_t*below = this->device->finish(this->device);
2846 this->device = states[statepos].olddevice;
2848 /* get outline of all objects below the soft mask */
2849 gfxdevice_t uniondev;
2850 gfxdevice_union_init(&uniondev, 0);
2851 gfxresult_record_replay(below, &uniondev);
2852 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2853 uniondev.finish(&uniondev);
2855 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2857 this->device->startclip(this->device, belowoutline);
2858 gfxresult_record_replay(below, this->device);
2859 gfxresult_record_replay(mask, this->device);
2860 this->device->endclip(this->device);
2861 gfxline_free(belowoutline);
2864 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2865 if(width<=0 || height<=0)
2868 gfxdevice_t belowrender;
2869 gfxdevice_render_init(&belowrender);
2870 if(states[statepos+1].isolated) {
2871 belowrender.setparameter(&belowrender, "fillwhite", "1");
2873 belowrender.setparameter(&belowrender, "antialize", "2");
2874 belowrender.startpage(&belowrender, width, height);
2875 gfxresult_record_replay(below, &belowrender);
2876 belowrender.endpage(&belowrender);
2877 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2878 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2879 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2881 gfxdevice_t maskrender;
2882 gfxdevice_render_init(&maskrender);
2883 maskrender.startpage(&maskrender, width, height);
2884 gfxresult_record_replay(mask, &maskrender);
2885 maskrender.endpage(&maskrender);
2886 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2887 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2889 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2890 msg("<fatal> Internal error in mask drawing");
2895 for(y=0;y<height;y++) {
2896 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2897 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2898 for(x=0;x<width;x++) {
2900 if(states[statepos].softmask_alpha) {
2903 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2906 /* premultiply alpha */
2907 l2->a = div255(alpha*l2->a);
2908 l2->r = div255(alpha*l2->r);
2909 l2->g = div255(alpha*l2->g);
2910 l2->b = div255(alpha*l2->b);
2916 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2919 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2920 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2922 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2924 mask->destroy(mask);
2925 below->destroy(below);
2926 maskresult->destroy(maskresult);
2927 belowresult->destroy(belowresult);
2928 states[statepos].softmaskrecording = 0;
2935 delete globalParams;globalParams=0;
2936 Object::memCheck(stderr);