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(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(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(char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(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(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(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 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)
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];
967 if(charnr>=0 && charnr<font->num_glyphs) {
968 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
976 void GFXOutputDev::beginString(GfxState *state, GString *s)
978 int render = state->getRender();
979 if(current_text_stroke) {
980 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
983 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
984 double m11,m21,m12,m22;
985 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
986 state->getFontTransMat(&m11, &m12, &m21, &m22);
987 m11 *= state->getHorizScaling();
988 m21 *= state->getHorizScaling();
990 this->current_font_matrix.m00 = m11 / 1024.0;
991 this->current_font_matrix.m01 = m12 / 1024.0;
992 this->current_font_matrix.m10 = -m21 / 1024.0;
993 this->current_font_matrix.m11 = -m22 / 1024.0;
994 this->current_font_matrix.tx = 0;
995 this->current_font_matrix.ty = 0;
997 gfxmatrix_t m = this->current_font_matrix;
999 states[statepos].textRender = render;
1002 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1003 double dx, double dy,
1004 double originX, double originY,
1005 CharCode c, int nBytes, Unicode *_u, int uLen)
1007 int render = state->getRender();
1008 // check for invisible text -- this is used by Acrobat Capture
1010 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1014 if(states[statepos].textRender != render)
1015 msg("<error> Internal error: drawChar.render!=beginString.render");
1017 gfxcolor_t col = getFillColor(state);
1019 Gushort *CIDToGIDMap = 0;
1020 GfxFont*font = state->getFont();
1022 if(font->getType() == fontType3 && do_interpretType3Chars) {
1023 /* type 3 chars are passed as graphics */
1024 msg("<debug> type3 char at %f/%f", x, y);
1034 if(font->isCIDFont()) {
1035 GfxCIDFont*cfont = (GfxCIDFont*)font;
1037 if(font->getType() == fontCIDType2)
1038 CIDToGIDMap = cfont->getCIDToGID();
1041 font8 = (Gfx8BitFont*)font;
1042 char**enc=font8->getEncoding();
1046 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);
1049 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);
1055 charid = getGfxCharID(current_gfxfont, c, name, u);
1057 charid = getGfxCharID(current_gfxfont, c, name, -1);
1060 /* multiple unicodes- should usually map to a ligature.
1061 if the ligature doesn't exist, we need to draw
1062 the characters one-by-one. */
1064 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1065 for(t=0;t<uLen;t++) {
1066 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1072 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1073 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1077 gfxmatrix_t m = this->current_font_matrix;
1078 state->transform(x, y, &m.tx, &m.ty);
1079 m.tx += user_movex + clipmovex;
1080 m.ty += user_movey + clipmovey;
1082 if(render == RENDER_FILL) {
1083 device->drawchar(device, current_gfxfont, charid, &col, &m);
1085 msg("<debug> Drawing glyph %d as shape", charid);
1087 msg("<notice> Some texts will be rendered as shape");
1090 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1091 gfxline_t*tglyph = gfxline_clone(glyph);
1092 gfxline_transform(tglyph, &m);
1093 if((render&3) != RENDER_INVISIBLE) {
1094 gfxline_t*add = gfxline_clone(tglyph);
1095 current_text_stroke = gfxline_append(current_text_stroke, add);
1097 if(render&RENDER_CLIP) {
1098 gfxline_t*add = gfxline_clone(tglyph);
1099 current_text_clip = gfxline_append(current_text_clip, add);
1101 gfxline_free(tglyph);
1105 void GFXOutputDev::endString(GfxState *state)
1107 int render = state->getRender();
1108 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1109 if(states[statepos].textRender != render)
1110 msg("<error> Internal error: drawChar.render!=beginString.render");
1112 if(current_text_stroke) {
1113 /* fillstroke and stroke text rendering objects we can process right
1114 now (as there may be texts of other rendering modes in this
1115 text object)- clipping objects have to wait until endTextObject,
1117 device->setparameter(device, "mark","TXT");
1118 if((render&3) == RENDER_FILL) {
1119 fillGfxLine(state, current_text_stroke);
1120 gfxline_free(current_text_stroke);
1121 current_text_stroke = 0;
1122 } else if((render&3) == RENDER_FILLSTROKE) {
1123 fillGfxLine(state, current_text_stroke);
1124 strokeGfxline(state, current_text_stroke,0);
1125 gfxline_free(current_text_stroke);
1126 current_text_stroke = 0;
1127 } else if((render&3) == RENDER_STROKE) {
1128 strokeGfxline(state, current_text_stroke,0);
1129 gfxline_free(current_text_stroke);
1130 current_text_stroke = 0;
1132 device->setparameter(device, "mark","");
1136 void GFXOutputDev::endTextObject(GfxState *state)
1138 int render = state->getRender();
1139 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1140 if(states[statepos].textRender != render)
1141 msg("<error> Internal error: drawChar.render!=beginString.render");
1143 if(current_text_clip) {
1144 device->setparameter(device, "mark","TXT");
1145 clipToGfxLine(state, current_text_clip);
1146 device->setparameter(device, "mark","");
1147 gfxline_free(current_text_clip);
1148 current_text_clip = 0;
1152 /* the logic seems to be as following:
1153 first, beginType3Char is called, with the charcode and the coordinates.
1154 if this function returns true, it already knew about the char and has now drawn it.
1155 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1156 the all draw operations until endType3Char are part of the char (which in this moment is
1157 at the position first passed to beginType3Char). the char ends with endType3Char.
1159 The drawing operations between beginType3Char and endType3Char are somewhat different to
1160 the normal ones. For example, the fillcolor equals the stroke color.
1163 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1165 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1170 gfxcolor_t col={255,0,0,0};
1171 gfxmatrix_t m = {1,0,0, 0,1,0};
1173 for(t=0;t<uLen;t++) {
1174 device->drawchar(device, 0, u[t], &col, &m);
1177 /* the character itself is going to be passed using the draw functions */
1178 return gFalse; /* gTrue= is_in_cache? */
1181 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1182 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1184 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1185 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1189 void GFXOutputDev::endType3Char(GfxState *state)
1192 msg("<debug> endType3Char");
1195 void GFXOutputDev::startFrame(int width, int height)
1197 if(outer_clip_box) {
1198 device->endclip(device);
1202 device->startpage(device, width, height);
1203 this->width = width;
1204 this->height = height;
1207 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1209 this->currentpage = pageNum;
1211 int rot = doc->getPageRotate(1);
1214 gfxline_t clippath[5];
1216 white.r = white.g = white.b = white.a = 255;
1218 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1219 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1220 Use CropBox, not MediaBox, as page size
1227 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1228 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1230 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1231 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1233 this->clipmovex = -(int)x1;
1234 this->clipmovey = -(int)y1;
1236 /* apply user clip box */
1237 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1238 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1239 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1240 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1241 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1242 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1245 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1247 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);
1249 msg("<verbose> page is rotated %d degrees\n", rot);
1251 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1252 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1253 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1254 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1255 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1256 device->startclip(device, clippath); outer_clip_box = 1;
1257 device->fill(device, clippath, &white);
1261 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1263 double x1, y1, x2, y2, w;
1264 gfxline_t points[5];
1267 msg("<debug> drawlink\n");
1269 link->getRect(&x1, &y1, &x2, &y2);
1270 cvtUserToDev(x1, y1, &x, &y);
1271 points[0].type = gfx_moveTo;
1272 points[0].x = points[4].x = x + user_movex + clipmovex;
1273 points[0].y = points[4].y = y + user_movey + clipmovey;
1274 points[0].next = &points[1];
1275 cvtUserToDev(x2, y1, &x, &y);
1276 points[1].type = gfx_lineTo;
1277 points[1].x = x + user_movex + clipmovex;
1278 points[1].y = y + user_movey + clipmovey;
1279 points[1].next = &points[2];
1280 cvtUserToDev(x2, y2, &x, &y);
1281 points[2].type = gfx_lineTo;
1282 points[2].x = x + user_movex + clipmovex;
1283 points[2].y = y + user_movey + clipmovey;
1284 points[2].next = &points[3];
1285 cvtUserToDev(x1, y2, &x, &y);
1286 points[3].type = gfx_lineTo;
1287 points[3].x = x + user_movex + clipmovex;
1288 points[3].y = y + user_movey + clipmovey;
1289 points[3].next = &points[4];
1290 cvtUserToDev(x1, y1, &x, &y);
1291 points[4].type = gfx_lineTo;
1292 points[4].x = x + user_movex + clipmovex;
1293 points[4].y = y + user_movey + clipmovey;
1296 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1297 points[0].x, points[0].y,
1298 points[1].x, points[1].y,
1299 points[2].x, points[2].y,
1300 points[3].x, points[3].y);
1302 LinkAction*action=link->getAction();
1308 msg("<trace> drawlink action=%d\n", action->getKind());
1309 switch(action->getKind())
1313 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1314 LinkDest *dest=NULL;
1315 if (ha->getDest()==NULL)
1316 dest=catalog->findDest(ha->getNamedDest());
1317 else dest=ha->getDest();
1319 if (dest->isPageRef()){
1320 Ref pageref=dest->getPageRef();
1321 page=catalog->findPage(pageref.num,pageref.gen);
1323 else page=dest->getPageNum();
1324 sprintf(buf, "%d", page);
1331 LinkGoToR*l = (LinkGoToR*)action;
1332 GString*g = l->getFileName();
1334 s = strdup(g->getCString());
1336 /* if the GoToR link has no filename, then
1337 try to find a refernce in the *local*
1339 GString*g = l->getNamedDest();
1341 s = strdup(g->getCString());
1347 LinkNamed*l = (LinkNamed*)action;
1348 GString*name = l->getName();
1350 s = strdup(name->lowerCase()->getCString());
1351 named = name->getCString();
1354 if(strstr(s, "next") || strstr(s, "forward"))
1356 page = currentpage + 1;
1358 else if(strstr(s, "prev") || strstr(s, "back"))
1360 page = currentpage - 1;
1362 else if(strstr(s, "last") || strstr(s, "end"))
1364 if(pages && pagepos>0)
1365 page = pages[pagepos-1];
1367 else if(strstr(s, "first") || strstr(s, "top"))
1375 case actionLaunch: {
1377 LinkLaunch*l = (LinkLaunch*)action;
1378 GString * str = new GString(l->getFileName());
1379 GString * params = l->getParams();
1381 str->append(params);
1382 s = strdup(str->getCString());
1389 LinkURI*l = (LinkURI*)action;
1390 GString*g = l->getURI();
1392 url = g->getCString();
1397 case actionUnknown: {
1399 LinkUnknown*l = (LinkUnknown*)action;
1404 msg("<error> Unknown link type!\n");
1409 if(!s) s = strdup("-?-");
1411 msg("<trace> drawlink s=%s\n", s);
1413 if(!linkinfo && (page || s))
1415 msg("<notice> File contains links");
1423 for(t=1;t<=pagepos;t++) {
1424 if(pages[t]==page) {
1433 sprintf(buf, "page%d", lpage);
1434 device->drawlink(device, points, buf);
1438 device->drawlink(device, points, s);
1441 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1445 void GFXOutputDev::saveState(GfxState *state) {
1446 dbg("saveState");dbgindent+=2;
1448 msg("<trace> saveState\n");
1451 msg("<error> Too many nested states in pdf.");
1455 states[statepos].textRender = states[statepos-1].textRender;
1456 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1457 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1458 states[statepos].clipping = 0;
1461 void GFXOutputDev::restoreState(GfxState *state) {
1462 dbgindent-=2; dbg("restoreState");
1465 msg("<error> Invalid restoreState");
1468 msg("<trace> restoreState");
1469 if(states[statepos].softmask) {
1470 clearSoftMask(state);
1473 while(states[statepos].clipping) {
1474 device->endclip(device);
1475 states[statepos].clipping--;
1480 char* writeOutStdFont(fontentry* f)
1485 char* tmpFileName = mktmpname(namebuf1);
1487 sprintf(namebuf2, "%s.afm", tmpFileName);
1488 fi = fopen(namebuf2, "wb");
1491 fwrite(f->afm, 1, f->afmlen, fi);
1494 sprintf(namebuf2, "%s.pfb", tmpFileName);
1495 fi = fopen(namebuf2, "wb");
1498 fwrite(f->pfb, 1, f->pfblen, fi);
1501 return strdup(namebuf2);
1504 char* GFXOutputDev::searchFont(char*name)
1509 msg("<verbose> SearchFont(%s)", name);
1511 /* see if it is a pdf standard font */
1512 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1514 if(!strcmp(name, pdf2t1map[i].pdffont))
1516 if(!pdf2t1map[i].fullfilename) {
1517 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1518 if(!pdf2t1map[i].fullfilename) {
1519 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1521 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1524 return strdup(pdf2t1map[i].fullfilename);
1527 /* else look in all font files */
1528 for(i=0;i<fontnum;i++)
1530 if(strstr(fonts[i].filename, name)) {
1531 return strdup(fonts[i].filename);
1537 void GFXOutputDev::updateLineWidth(GfxState *state)
1539 double width = state->getTransformedLineWidth();
1540 //swfoutput_setlinewidth(&device, width);
1543 void GFXOutputDev::updateLineCap(GfxState *state)
1545 int c = state->getLineCap();
1548 void GFXOutputDev::updateLineJoin(GfxState *state)
1550 int j = state->getLineJoin();
1553 void GFXOutputDev::updateFillColor(GfxState *state)
1556 double opaq = state->getFillOpacity();
1557 state->getFillRGB(&rgb);
1559 void GFXOutputDev::updateFillOpacity(GfxState *state)
1562 double opaq = state->getFillOpacity();
1563 state->getFillRGB(&rgb);
1564 dbg("update fillopaq %f", opaq);
1566 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1568 double opaq = state->getFillOpacity();
1569 dbg("update strokeopaq %f", opaq);
1571 void GFXOutputDev::updateFillOverprint(GfxState *state)
1573 double opaq = state->getFillOverprint();
1574 dbg("update filloverprint %f", opaq);
1576 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1578 double opaq = state->getStrokeOverprint();
1579 dbg("update strokeoverprint %f", opaq);
1581 void GFXOutputDev::updateTransfer(GfxState *state)
1583 dbg("update transfer");
1587 void GFXOutputDev::updateStrokeColor(GfxState *state)
1590 double opaq = state->getStrokeOpacity();
1591 state->getStrokeRGB(&rgb);
1594 void FoFiWrite(void *stream, char *data, int len)
1596 int ret = fwrite(data, len, 1, (FILE*)stream);
1599 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1601 char*tmpFileName = NULL;
1607 Object refObj, strObj;
1609 tmpFileName = mktmpname(namebuf);
1612 ret = font->getEmbeddedFontID(&embRef);
1614 msg("<verbose> Didn't get embedded font id");
1615 /* not embedded- the caller should now search the font
1616 directories for this font */
1620 f = fopen(tmpFileName, "wb");
1622 msg("<error> Couldn't create temporary Type 1 font file");
1626 /*if(font->isCIDFont()) {
1627 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1628 GString c = cidFont->getCollection();
1629 msg("<notice> Collection: %s", c.getCString());
1632 //if (font->getType() == fontType1C) {
1633 if (0) { //font->getType() == fontType1C) {
1634 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1636 msg("<error> Couldn't read embedded font file");
1639 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1641 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1642 //cvt->convertToCIDType0("test", f);
1643 //cvt->convertToType0("test", f);
1646 } else if(font->getType() == fontTrueType) {
1647 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1648 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1650 msg("<error> Couldn't read embedded font file");
1653 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1654 cvt->writeTTF(FoFiWrite, f);
1658 font->getEmbeddedFontID(&embRef);
1659 refObj.initRef(embRef.num, embRef.gen);
1660 refObj.fetch(ref, &strObj);
1662 strObj.streamReset();
1667 f4[t] = strObj.streamGetChar();
1668 f4c[t] = (char)f4[t];
1673 if(!strncmp(f4c, "true", 4)) {
1674 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1675 Change this on the fly */
1676 f4[0] = f4[2] = f4[3] = 0;
1684 while ((c = strObj.streamGetChar()) != EOF) {
1688 strObj.streamClose();
1693 return strdup(tmpFileName);
1696 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1698 char*name = getFontName(gfxFont);
1702 if(!this->config_use_fontconfig)
1705 #ifdef HAVE_FONTCONFIG
1706 FcPattern *pattern, *match;
1710 static int fcinitcalled = false;
1712 msg("<debug> searchForSuitableFont(%s)", name);
1714 // call init ony once
1715 if (!fcinitcalled) {
1716 msg("<debug> Initializing FontConfig...");
1717 fcinitcalled = true;
1719 msg("<debug> FontConfig Initialization failed. Disabling.");
1720 config_use_fontconfig = 0;
1723 msg("<debug> ...initialized FontConfig");
1726 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1727 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1728 if (gfxFont->isItalic()) // check for italic
1729 msg("<debug> FontConfig: Adding Italic Slant");
1730 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1731 if (gfxFont->isBold()) // check for bold
1732 msg("<debug> FontConfig: Adding Bold Weight");
1733 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1735 msg("<debug> FontConfig: Try to match...");
1736 // configure and match using the original font name
1737 FcConfigSubstitute(0, pattern, FcMatchPattern);
1738 FcDefaultSubstitute(pattern);
1739 match = FcFontMatch(0, pattern, &result);
1741 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1742 msg("<debug> FontConfig: family=%s", (char*)v);
1743 // if we get an exact match
1744 if (strcmp((char *)v, name) == 0) {
1745 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1746 filename = strdup((char*)v); // mem leak
1747 char *nfn = strrchr(filename, '/');
1748 if(nfn) fontname = strdup(nfn+1);
1749 else fontname = filename;
1751 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1753 // initialize patterns
1754 FcPatternDestroy(pattern);
1755 FcPatternDestroy(match);
1757 // now match against serif etc.
1758 if (gfxFont->isSerif()) {
1759 msg("<debug> FontConfig: Create Serif Family Pattern");
1760 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1761 } else if (gfxFont->isFixedWidth()) {
1762 msg("<debug> FontConfig: Create Monospace Family Pattern");
1763 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1765 msg("<debug> FontConfig: Create Sans Family Pattern");
1766 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1770 if (gfxFont->isItalic()) {
1771 msg("<debug> FontConfig: Adding Italic Slant");
1772 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1775 if (gfxFont->isBold()) {
1776 msg("<debug> FontConfig: Adding Bold Weight");
1777 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1780 msg("<debug> FontConfig: Try to match... (2)");
1781 // configure and match using serif etc
1782 FcConfigSubstitute (0, pattern, FcMatchPattern);
1783 FcDefaultSubstitute (pattern);
1784 match = FcFontMatch (0, pattern, &result);
1786 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1787 filename = strdup((char*)v); // mem leak
1788 char *nfn = strrchr(filename, '/');
1789 if(nfn) fontname = strdup(nfn+1);
1790 else fontname = filename;
1792 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1796 //printf("FONTCONFIG: pattern");
1797 //FcPatternPrint(pattern);
1798 //printf("FONTCONFIG: match");
1799 //FcPatternPrint(match);
1801 FcPatternDestroy(pattern);
1802 FcPatternDestroy(match);
1804 pdfswf_addfont(filename);
1811 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1813 char*fontname = 0, *filename = 0;
1814 msg("<notice> substituteFont(%s)", oldname);
1816 if(!(fontname = searchForSuitableFont(gfxFont))) {
1817 fontname = "Times-Roman";
1819 filename = searchFont(fontname);
1821 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1825 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1826 msg("<fatal> Too many fonts in file.");
1830 substitutesource[substitutepos] = strdup(oldname); //mem leak
1831 substitutetarget[substitutepos] = fontname;
1832 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1835 return strdup(filename); //mem leak
1838 void unlinkfont(char* filename)
1845 if(!strncmp(&filename[l-4],".afm",4)) {
1846 memcpy(&filename[l-4],".pfb",4);
1848 memcpy(&filename[l-4],".pfa",4);
1850 memcpy(&filename[l-4],".afm",4);
1853 if(!strncmp(&filename[l-4],".pfa",4)) {
1854 memcpy(&filename[l-4],".afm",4);
1856 memcpy(&filename[l-4],".pfa",4);
1859 if(!strncmp(&filename[l-4],".pfb",4)) {
1860 memcpy(&filename[l-4],".afm",4);
1862 memcpy(&filename[l-4],".pfb",4);
1867 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1873 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1876 fontlist_t*last=0,*l = this->fontlist;
1879 msg("<error> Internal Error: FontID is null");
1881 /* TODO: should this be part of the state? */
1884 if(!strcmp(l->font->id, id)) {
1885 current_gfxfont = l->font;
1887 device->addfont(device, current_gfxfont);
1892 if(!filename) return 0;
1894 /* A font size of e.g. 9 means the font will be scaled down by
1895 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1896 we have to divide 0.05 by (fontsize/1024)
1898 double quality = (1024 * 0.05) / maxSize;
1900 msg("<verbose> Loading %s...", filename);
1901 font = gfxfont_load(id, filename, quality);
1903 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1906 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1910 l->filename = strdup(filename);
1912 current_gfxfont = l->font;
1918 device->addfont(device, current_gfxfont);
1922 void GFXOutputDev::updateFont(GfxState *state)
1924 GfxFont*gfxFont = state->getFont();
1930 char * fontid = getFontID(gfxFont);
1931 char * fontname = getFontName(gfxFont);
1933 double maxSize = 1.0;
1936 maxSize = this->info->getMaximumFontSize(fontid);
1940 /* first, look if we substituted this font before-
1941 this way, we don't initialize the T1 Fonts
1943 for(t=0;t<substitutepos;t++) {
1944 if(!strcmp(fontid, substitutesource[t])) {
1945 free(fontid);fontid=0;
1946 fontid = strdup(substitutetarget[t]);
1951 /* second, see if this is a font which was used before-
1952 if so, we are done */
1953 if(setGfxFont(fontid, fontname, 0, 0)) {
1958 /* if(swfoutput_queryfont(&device, fontid))
1959 swfoutput_setfont(&device, fontid, 0);
1961 msg("<debug> updateFont(%s) [cached]", fontid);
1965 // look for Type 3 font
1966 if (gfxFont->getType() == fontType3) {
1968 type3Warning = gTrue;
1969 showFontError(gfxFont, 2);
1976 /* now either load the font, or find a substitution */
1979 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1984 (gfxFont->getType() == fontType1 ||
1985 gfxFont->getType() == fontType1C ||
1986 gfxFont->getType() == fontCIDType0C ||
1987 gfxFont->getType() == fontTrueType ||
1988 gfxFont->getType() == fontCIDType2
1991 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1992 if(!fileName) showFontError(gfxFont,0);
1995 fileName = searchFont(fontname);
1996 if(!fileName) showFontError(gfxFont,0);
1999 char * fontname = getFontName(gfxFont);
2000 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2003 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2005 msg("<warning> Try specifying one or more font directories");
2007 fileName = substituteFont(gfxFont, fontid);
2010 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2011 msg("<notice> Font is now %s (%s)", fontid, fileName);
2015 msg("<error> Couldn't set font %s\n", fontid);
2021 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2022 dumpFontInfo("<verbose>", gfxFont);
2024 //swfoutput_setfont(&device, fontid, fileName);
2026 if(!setGfxFont(fontid, fontname, 0, 0)) {
2027 setGfxFont(fontid, fontname, fileName, maxSize);
2031 unlinkfont(fileName);
2041 #define SQR(x) ((x)*(x))
2043 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2045 if((newwidth<2 || newheight<2) ||
2046 (width<=newwidth || height<=newheight))
2048 unsigned char*newdata;
2050 newdata= (unsigned char*)malloc(newwidth*newheight);
2052 double fx = (double)(width)/newwidth;
2053 double fy = (double)(height)/newheight;
2055 int blocksize = (int)(8192/(fx*fy));
2056 int r = 8192*256/palettesize;
2057 for(x=0;x<newwidth;x++) {
2058 double ex = px + fx;
2059 int fromx = (int)px;
2061 int xweight1 = (int)(((fromx+1)-px)*256);
2062 int xweight2 = (int)((ex-tox)*256);
2064 for(y=0;y<newheight;y++) {
2065 double ey = py + fy;
2066 int fromy = (int)py;
2068 int yweight1 = (int)(((fromy+1)-py)*256);
2069 int yweight2 = (int)((ey-toy)*256);
2072 for(xx=fromx;xx<=tox;xx++)
2073 for(yy=fromy;yy<=toy;yy++) {
2074 int b = 1-data[width*yy+xx];
2076 if(xx==fromx) weight = (weight*xweight1)/256;
2077 if(xx==tox) weight = (weight*xweight2)/256;
2078 if(yy==fromy) weight = (weight*yweight1)/256;
2079 if(yy==toy) weight = (weight*yweight2)/256;
2082 //if(a) a=(palettesize-1)*r/blocksize;
2083 newdata[y*newwidth+x] = (a*blocksize)/r;
2091 #define IMAGE_TYPE_JPEG 0
2092 #define IMAGE_TYPE_LOSSLESS 1
2094 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2095 double x1,double y1,
2096 double x2,double y2,
2097 double x3,double y3,
2098 double x4,double y4, int type)
2100 gfxcolor_t*newpic=0;
2102 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2103 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2105 gfxline_t p1,p2,p3,p4,p5;
2106 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2107 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2108 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2109 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2110 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2112 {p1.x = (int)(p1.x*20)/20.0;
2113 p1.y = (int)(p1.y*20)/20.0;
2114 p2.x = (int)(p2.x*20)/20.0;
2115 p2.y = (int)(p2.y*20)/20.0;
2116 p3.x = (int)(p3.x*20)/20.0;
2117 p3.y = (int)(p3.y*20)/20.0;
2118 p4.x = (int)(p4.x*20)/20.0;
2119 p4.y = (int)(p4.y*20)/20.0;
2120 p5.x = (int)(p5.x*20)/20.0;
2121 p5.y = (int)(p5.y*20)/20.0;
2128 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2129 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2134 img.data = (gfxcolor_t*)data;
2138 if(type == IMAGE_TYPE_JPEG)
2139 /* TODO: pass image_dpi to device instead */
2140 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2142 dev->fillbitmap(dev, &p1, &img, &m, 0);
2145 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2146 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2148 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2151 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2152 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2154 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2158 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2159 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2160 GBool inlineImg, int mask, int*maskColors,
2161 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2163 double x1,y1,x2,y2,x3,y3,x4,y4;
2164 ImageStream *imgStr;
2169 unsigned char* maskbitmap = 0;
2172 ncomps = colorMap->getNumPixelComps();
2173 bits = colorMap->getBits();
2178 unsigned char buf[8];
2179 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2181 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2182 imgMaskStr->reset();
2183 unsigned char pal[256];
2184 int n = 1 << colorMap->getBits();
2189 maskColorMap->getGray(pixBuf, &gray);
2190 pal[t] = colToByte(gray);
2192 for (y = 0; y < maskHeight; y++) {
2193 for (x = 0; x < maskWidth; x++) {
2194 imgMaskStr->getPixel(buf);
2195 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2200 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2201 imgMaskStr->reset();
2202 for (y = 0; y < maskHeight; y++) {
2203 for (x = 0; x < maskWidth; x++) {
2204 imgMaskStr->getPixel(buf);
2206 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2214 imgStr = new ImageStream(str, width, ncomps,bits);
2217 if(!width || !height || (height<=1 && width<=1))
2219 msg("<verbose> Ignoring %d by %d image", width, height);
2220 unsigned char buf[8];
2222 for (y = 0; y < height; ++y)
2223 for (x = 0; x < width; ++x) {
2224 imgStr->getPixel(buf);
2232 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2233 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2234 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2235 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2237 if(!pbminfo && !(str->getKind()==strDCT)) {
2239 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2243 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2245 if(!jpeginfo && (str->getKind()==strDCT)) {
2246 msg("<notice> file contains jpeg pictures");
2252 unsigned char buf[8];
2254 unsigned char*pic = new unsigned char[width*height];
2255 gfxcolor_t pal[256];
2257 state->getFillRGB(&rgb);
2259 memset(pal,255,sizeof(pal));
2260 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2261 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2262 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2263 pal[0].a = 255; pal[1].a = 0;
2266 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2267 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2268 for (y = 0; y < height; ++y)
2269 for (x = 0; x < width; ++x)
2271 imgStr->getPixel(buf);
2274 pic[width*y+x] = buf[0];
2277 /* the size of the drawn image is added to the identifier
2278 as the same image may require different bitmaps if displayed
2279 at different sizes (due to antialiasing): */
2282 unsigned char*pic2 = 0;
2285 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2294 height = realheight;
2298 /* make a black/white palette */
2300 float r = 255/(numpalette-1);
2302 for(t=0;t<numpalette;t++) {
2303 pal[t].r = colToByte(rgb.r);
2304 pal[t].g = colToByte(rgb.g);
2305 pal[t].b = colToByte(rgb.b);
2306 pal[t].a = (unsigned char)(t*r);
2310 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2311 for (y = 0; y < height; ++y) {
2312 for (x = 0; x < width; ++x) {
2313 pic2[width*y+x] = pal[pic[y*width+x]];
2316 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2320 if(maskbitmap) free(maskbitmap);
2326 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2327 gfxcolor_t*pic=new gfxcolor_t[width*height];
2328 for (y = 0; y < height; ++y) {
2329 for (x = 0; x < width; ++x) {
2330 imgStr->getPixel(pixBuf);
2331 colorMap->getRGB(pixBuf, &rgb);
2332 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2333 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2334 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2335 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2337 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2341 if(str->getKind()==strDCT)
2342 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2344 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2347 if(maskbitmap) free(maskbitmap);
2350 gfxcolor_t*pic=new gfxcolor_t[width*height];
2351 gfxcolor_t pal[256];
2352 int n = 1 << colorMap->getBits();
2354 for(t=0;t<256;t++) {
2356 colorMap->getRGB(pixBuf, &rgb);
2358 {/*if(maskColors && *maskColors==t) {
2359 msg("<notice> Color %d is transparent", t);
2360 if (imgData->maskColors) {
2362 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2363 if (pix[i] < imgData->maskColors[2*i] ||
2364 pix[i] > imgData->maskColors[2*i+1]) {
2379 pal[t].r = (unsigned char)(colToByte(rgb.r));
2380 pal[t].g = (unsigned char)(colToByte(rgb.g));
2381 pal[t].b = (unsigned char)(colToByte(rgb.b));
2382 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2385 for (y = 0; y < height; ++y) {
2386 for (x = 0; x < width; ++x) {
2387 imgStr->getPixel(pixBuf);
2388 pic[width*y+x] = pal[pixBuf[0]];
2390 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2394 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2398 if(maskbitmap) free(maskbitmap);
2403 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2404 int width, int height, GBool invert,
2407 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2408 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2409 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2412 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2413 int width, int height, GfxImageColorMap *colorMap,
2414 int *maskColors, GBool inlineImg)
2416 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2417 colorMap?"colorMap":"no colorMap",
2418 maskColors?"maskColors":"no maskColors",
2420 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2421 colorMap?"colorMap":"no colorMap",
2422 maskColors?"maskColors":"no maskColors",
2425 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2426 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2427 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2430 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2431 int width, int height,
2432 GfxImageColorMap *colorMap,
2433 Stream *maskStr, int maskWidth, int maskHeight,
2436 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2437 colorMap?"colorMap":"no colorMap",
2438 maskWidth, maskHeight);
2439 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2440 colorMap?"colorMap":"no colorMap",
2441 maskWidth, maskHeight);
2443 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2444 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2445 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2448 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2449 int width, int height,
2450 GfxImageColorMap *colorMap,
2452 int maskWidth, int maskHeight,
2453 GfxImageColorMap *maskColorMap)
2455 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2456 colorMap?"colorMap":"no colorMap",
2457 maskWidth, maskHeight);
2458 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2459 colorMap?"colorMap":"no colorMap",
2460 maskWidth, maskHeight);
2462 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2463 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2464 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2467 void GFXOutputDev::stroke(GfxState *state)
2471 GfxPath * path = state->getPath();
2472 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2473 strokeGfxline(state, line, 0);
2477 void GFXOutputDev::fill(GfxState *state)
2479 gfxcolor_t col = getFillColor(state);
2480 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2482 GfxPath * path = state->getPath();
2483 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2484 fillGfxLine(state, line);
2488 void GFXOutputDev::eoFill(GfxState *state)
2490 gfxcolor_t col = getFillColor(state);
2491 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2493 GfxPath * path = state->getPath();
2494 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2495 fillGfxLine(state, line);
2500 static char* dirseparator()
2509 void addGlobalFont(char*filename)
2512 memset(&f, 0, sizeof(fontfile_t));
2513 f.filename = filename;
2514 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2515 msg("<verbose> Adding font \"%s\".", filename);
2516 fonts[fontnum++] = f;
2518 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2522 void addGlobalLanguageDir(char*dir)
2525 globalParams = new GlobalParams("");
2527 msg("<notice> Adding %s to language pack directories", dir);
2531 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2532 strcpy(config_file, dir);
2533 strcat(config_file, dirseparator());
2534 strcat(config_file, "add-to-xpdfrc");
2536 fi = fopen(config_file, "rb");
2538 msg("<error> Could not open %s", config_file);
2541 globalParams->parseFile(new GString(config_file), fi);
2545 void addGlobalFontDir(char*dirname)
2547 #ifdef HAVE_DIRENT_H
2548 msg("<notice> Adding %s to font directories", dirname);
2549 lastfontdir = strdup(dirname);
2550 DIR*dir = opendir(dirname);
2552 msg("<warning> Couldn't open directory %s\n", dirname);
2557 ent = readdir (dir);
2561 char*name = ent->d_name;
2567 if(!strncasecmp(&name[l-4], ".pfa", 4))
2569 if(!strncasecmp(&name[l-4], ".pfb", 4))
2571 if(!strncasecmp(&name[l-4], ".ttf", 4))
2575 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2576 strcpy(fontname, dirname);
2577 strcat(fontname, dirseparator());
2578 strcat(fontname, name);
2579 addGlobalFont(fontname);
2584 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2588 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2594 this->pagebuflen = 1024;
2595 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2596 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2598 while(pdfpage >= this->pagebuflen)
2600 int oldlen = this->pagebuflen;
2601 this->pagebuflen+=1024;
2602 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2603 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2606 this->pages[pdfpage] = outputpage;
2607 if(pdfpage>this->pagepos)
2608 this->pagepos = pdfpage;
2614 double width,height;
2617 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2619 double xMin, yMin, xMax, yMax, x, y;
2620 double tx, ty, w, h;
2621 // transform the bbox
2622 state->transform(bbox[0], bbox[1], &x, &y);
2625 state->transform(bbox[0], bbox[3], &x, &y);
2628 } else if (x > xMax) {
2633 } else if (y > yMax) {
2636 state->transform(bbox[2], bbox[1], &x, &y);
2639 } else if (x > xMax) {
2644 } else if (y > yMax) {
2647 state->transform(bbox[2], bbox[3], &x, &y);
2650 } else if (x > xMax) {
2655 } else if (y > yMax) {
2658 tx = (int)floor(xMin);
2661 } else if (tx > width) {
2664 ty = (int)floor(yMin);
2667 } else if (ty > height) {
2670 w = (int)ceil(xMax) - tx + 1;
2671 if (tx + w > width) {
2677 h = (int)ceil(yMax) - ty + 1;
2678 if (ty + h > height) {
2692 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2693 GfxColorSpace *blendingColorSpace,
2694 GBool isolated, GBool knockout,
2697 char*colormodename = "";
2698 BBox rect = mkBBox(state, bbox, this->width, this->height);
2700 if(blendingColorSpace) {
2701 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2703 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);
2704 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2705 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);
2707 states[statepos].createsoftmask |= forSoftMask;
2708 states[statepos].transparencygroup = !forSoftMask;
2709 states[statepos].isolated = isolated;
2711 states[statepos].olddevice = this->device;
2712 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2714 gfxdevice_record_init(this->device);
2716 /*if(!forSoftMask) { ////???
2717 state->setFillOpacity(0.0);
2722 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2725 dbg("endTransparencyGroup");
2726 msg("<verbose> endTransparencyGroup");
2728 gfxdevice_t*r = this->device;
2730 this->device = states[statepos].olddevice;
2732 if(states[statepos].createsoftmask) {
2733 states[statepos-1].softmaskrecording = r->finish(r);
2735 states[statepos-1].grouprecording = r->finish(r);
2738 states[statepos].createsoftmask = 0;
2739 states[statepos].transparencygroup = 0;
2743 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2745 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2746 "colordodge","colorburn","hardlight","softlight","difference",
2747 "exclusion","hue","saturation","color","luminosity"};
2749 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2750 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2752 if(state->getBlendMode() == gfxBlendNormal)
2753 infofeature("transparency groups");
2756 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2757 warnfeature(buffer, 0);
2760 gfxresult_t*grouprecording = states[statepos].grouprecording;
2762 if(state->getBlendMode() == gfxBlendNormal) {
2764 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2765 gfxresult_record_replay(grouprecording, &ops);
2768 grouprecording->destroy(grouprecording);
2770 states[statepos].grouprecording = 0;
2773 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2775 /* alpha = 1: retrieve mask values from alpha layer
2776 alpha = 0: retrieve mask values from luminance */
2777 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2778 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2779 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2780 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2782 infofeature("soft masks");
2784 warnfeature("soft masks from alpha channel",0);
2786 states[statepos].olddevice = this->device;
2787 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2788 gfxdevice_record_init(this->device);
2790 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2792 states[statepos].softmask = 1;
2793 states[statepos].softmask_alpha = alpha;
2796 static inline Guchar div255(int x) {
2797 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2800 void GFXOutputDev::clearSoftMask(GfxState *state)
2802 if(!states[statepos].softmask)
2804 states[statepos].softmask = 0;
2805 dbg("clearSoftMask statepos=%d", statepos);
2806 msg("<verbose> clearSoftMask");
2808 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2809 msg("<error> Error in softmask/tgroup ordering");
2813 gfxresult_t*mask = states[statepos].softmaskrecording;
2814 gfxresult_t*below = this->device->finish(this->device);
2815 this->device = states[statepos].olddevice;
2817 /* get outline of all objects below the soft mask */
2818 gfxdevice_t uniondev;
2819 gfxdevice_union_init(&uniondev, 0);
2820 gfxresult_record_replay(below, &uniondev);
2821 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2822 uniondev.finish(&uniondev);
2824 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2826 this->device->startclip(this->device, belowoutline);
2827 gfxresult_record_replay(below, this->device);
2828 gfxresult_record_replay(mask, this->device);
2829 this->device->endclip(this->device);
2830 gfxline_free(belowoutline);
2833 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2834 if(width<=0 || height<=0)
2837 gfxdevice_t belowrender;
2838 gfxdevice_render_init(&belowrender);
2839 if(states[statepos+1].isolated) {
2840 belowrender.setparameter(&belowrender, "fillwhite", "1");
2842 belowrender.setparameter(&belowrender, "antialize", "2");
2843 belowrender.startpage(&belowrender, width, height);
2844 gfxresult_record_replay(below, &belowrender);
2845 belowrender.endpage(&belowrender);
2846 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2847 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2848 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2850 gfxdevice_t maskrender;
2851 gfxdevice_render_init(&maskrender);
2852 maskrender.startpage(&maskrender, width, height);
2853 gfxresult_record_replay(mask, &maskrender);
2854 maskrender.endpage(&maskrender);
2855 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2856 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2858 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2859 msg("<fatal> Internal error in mask drawing");
2864 for(y=0;y<height;y++) {
2865 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2866 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2867 for(x=0;x<width;x++) {
2869 if(states[statepos].softmask_alpha) {
2872 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2875 /* premultiply alpha */
2876 l2->a = div255(alpha*l2->a);
2877 l2->r = div255(alpha*l2->r);
2878 l2->g = div255(alpha*l2->g);
2879 l2->b = div255(alpha*l2->b);
2885 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2888 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2889 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2891 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2893 mask->destroy(mask);
2894 below->destroy(below);
2895 maskresult->destroy(maskresult);
2896 belowresult->destroy(belowresult);
2897 states[statepos].softmaskrecording = 0;
2904 delete globalParams;globalParams=0;
2905 Object::memCheck(stderr);