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->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
197 return this->do_interpretType3Chars;
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 static char*getFontID(GfxFont*font);
247 GFXOutputDev::GFXOutputDev(parameter_t*p)
250 this->textmodeinfo = 0;
253 this->type3active = 0;
256 this->substitutepos = 0;
257 this->type3Warning = 0;
258 this->user_movex = 0;
259 this->user_movey = 0;
262 this->user_clipx1 = 0;
263 this->user_clipy1 = 0;
264 this->user_clipx2 = 0;
265 this->user_clipy2 = 0;
266 this->current_text_stroke = 0;
267 this->current_text_clip = 0;
269 this->outer_clip_box = 0;
271 this->pagebuflen = 0;
273 this->config_use_fontconfig=1;
274 this->config_break_on_warning=0;
275 this->do_interpretType3Chars = gTrue;
277 this->parameters = p;
279 memset(states, 0, sizeof(states));
281 /* configure device */
283 setParameter(p->name, p->value);
288 void GFXOutputDev::setParameter(const char*key, const char*value)
290 if(!strcmp(key,"rawtext")) {
291 this->do_interpretType3Chars = atoi(value)^1;
292 } else if(!strcmp(key,"breakonwarning")) {
293 this->config_break_on_warning = atoi(value);
294 } else if(!strcmp(key,"fontconfig")) {
295 this->config_use_fontconfig = atoi(value);
297 msg("<warning> Ignored parameter: %s=%s", key, value);
301 void GFXOutputDev::setDevice(gfxdevice_t*dev)
303 parameter_t*p = this->parameters;
305 /* pass parameters to output device */
309 this->device->setparameter(this->device, p->name, p->value);
315 void GFXOutputDev::setMove(int x,int y)
317 this->user_movex = x;
318 this->user_movey = y;
321 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
323 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
324 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
326 this->user_clipx1 = x1;
327 this->user_clipy1 = y1;
328 this->user_clipx2 = x2;
329 this->user_clipy2 = y2;
332 static char*getFontID(GfxFont*font)
334 Ref*ref = font->getID();
335 GString*gstr = font->getName();
336 char* fname = gstr==0?0:gstr->getCString();
339 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
341 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
346 static char*getFontName(GfxFont*font)
349 GString*gstr = font->getName();
350 char* fname = gstr==0?0:gstr->getCString();
354 sprintf(buf, "UFONT%d", r->num);
355 fontid = strdup(buf);
357 fontid = strdup(fname);
361 char* plus = strchr(fontid, '+');
362 if(plus && plus < &fontid[strlen(fontid)-1]) {
363 fontname = strdup(plus+1);
365 fontname = strdup(fontid);
371 static char mybuf[1024];
372 static char* gfxstate2str(GfxState *state)
376 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
383 if(state->getX1()!=0.0)
384 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
385 if(state->getY1()!=0.0)
386 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
387 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
388 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
389 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
390 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
391 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
392 state->getFillColor()->c[0], state->getFillColor()->c[1]);
393 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
394 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
395 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
396 state->getFillColor()->c[0], state->getFillColor()->c[1],
397 state->getFillColor()->c[2], state->getFillColor()->c[3],
398 state->getFillColor()->c[4], state->getFillColor()->c[5],
399 state->getFillColor()->c[6], state->getFillColor()->c[7]);
400 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
401 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
402 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
403 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
404 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
405 state->getFillRGB(&rgb);
406 if(rgb.r || rgb.g || rgb.b)
407 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
408 state->getStrokeRGB(&rgb);
409 if(rgb.r || rgb.g || rgb.b)
410 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
411 if(state->getFillColorSpace()->getNComps()>1)
412 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
413 if(state->getStrokeColorSpace()->getNComps()>1)
414 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
415 if(state->getFillPattern())
416 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
417 if(state->getStrokePattern())
418 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
420 if(state->getFillOpacity()!=1.0)
421 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
422 if(state->getStrokeOpacity()!=1.0)
423 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
425 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
430 state->getLineDash(&dash, &length, &start);
434 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
435 for(t=0;t<length;t++) {
436 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
438 bufpos+=sprintf(bufpos,"]");
441 if(state->getFlatness()!=1)
442 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
443 if(state->getLineJoin()!=0)
444 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
445 if(state->getLineJoin()!=0)
446 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
447 if(state->getLineJoin()!=0)
448 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
450 if(state->getFont() && getFontID(state->getFont()))
451 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
452 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
453 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
454 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
455 if(state->getCharSpace())
456 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
457 if(state->getWordSpace())
458 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
459 if(state->getHorizScaling()!=1.0)
460 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
461 if(state->getLeading())
462 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
464 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
465 if(state->getRender())
466 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
467 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
468 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
469 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
470 if(state->getLineX())
471 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
472 if(state->getLineY())
473 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
474 bufpos+=sprintf(bufpos," ");
478 static void dumpFontInfo(const char*loglevel, GfxFont*font);
479 static int lastdumps[1024];
480 static int lastdumppos = 0;
485 static void showFontError(GfxFont*font, int nr)
489 for(t=0;t<lastdumppos;t++)
490 if(lastdumps[t] == r->num)
494 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
495 lastdumps[lastdumppos++] = r->num;
497 msg("<warning> The following font caused problems:");
499 msg("<warning> The following font caused problems (substituting):");
501 msg("<warning> The following Type 3 Font will be rendered as graphics:");
502 dumpFontInfo("<warning>", font);
505 static void dumpFontInfo(const char*loglevel, GfxFont*font)
507 char* id = getFontID(font);
508 char* name = getFontName(font);
509 Ref* r=font->getID();
510 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
512 GString*gstr = font->getTag();
514 msg("%s| Tag: %s\n", loglevel, id);
516 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
518 GfxFontType type=font->getType();
520 case fontUnknownType:
521 msg("%s| Type: unknown\n",loglevel);
524 msg("%s| Type: 1\n",loglevel);
527 msg("%s| Type: 1C\n",loglevel);
530 msg("%s| Type: 3\n",loglevel);
533 msg("%s| Type: TrueType\n",loglevel);
536 msg("%s| Type: CIDType0\n",loglevel);
539 msg("%s| Type: CIDType0C\n",loglevel);
542 msg("%s| Type: CIDType2\n",loglevel);
547 GBool embedded = font->getEmbeddedFontID(&embRef);
549 if(font->getEmbeddedFontName()) {
550 embeddedName = font->getEmbeddedFontName()->getCString();
553 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
555 gstr = font->getExtFontFile();
557 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
559 // Get font descriptor flags.
560 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
561 if(font->isSerif()) msg("%s| is serif\n", loglevel);
562 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
563 if(font->isItalic()) msg("%s| is italic\n", loglevel);
564 if(font->isBold()) msg("%s| is bold\n", loglevel);
570 //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");}
571 //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");}
574 void dump_outline(gfxline_t*line)
577 if(line->type == gfx_moveTo) {
578 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
579 } else if(line->type == gfx_lineTo) {
580 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
581 } else if(line->type == gfx_splineTo) {
582 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
588 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
590 int num = path->getNumSubpaths();
593 double lastx=0,lasty=0,posx=0,posy=0;
596 msg("<warning> empty path");
600 gfxdrawer_target_gfxline(&draw);
602 for(t = 0; t < num; t++) {
603 GfxSubpath *subpath = path->getSubpath(t);
604 int subnum = subpath->getNumPoints();
605 double bx=0,by=0,cx=0,cy=0;
607 for(s=0;s<subnum;s++) {
610 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
615 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
616 draw.lineTo(&draw, lastx, lasty);
618 draw.moveTo(&draw, x,y);
623 } else if(subpath->getCurve(s) && cpos==0) {
627 } else if(subpath->getCurve(s) && cpos==1) {
635 draw.lineTo(&draw, x,y);
637 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
644 /* fix non-closed lines */
645 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
646 draw.lineTo(&draw, lastx, lasty);
648 gfxline_t*result = (gfxline_t*)draw.result(&draw);
650 gfxline_optimize(result);
655 GBool GFXOutputDev::useTilingPatternFill()
657 infofeature("tiled patterns");
661 GBool GFXOutputDev::useShadedFills()
663 infofeature("shaded fills");
667 #define STROKE_FILL 1
668 #define STROKE_CLIP 2
669 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
671 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
672 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
673 double miterLimit = state->getMiterLimit();
674 double width = state->getTransformedLineWidth();
677 double opaq = state->getStrokeOpacity();
679 state->getFillRGB(&rgb);
681 state->getStrokeRGB(&rgb);
683 col.r = colToByte(rgb.r);
684 col.g = colToByte(rgb.g);
685 col.b = colToByte(rgb.b);
686 col.a = (unsigned char)(opaq*255);
688 gfx_capType capType = gfx_capRound;
689 if(lineCap == 0) capType = gfx_capButt;
690 else if(lineCap == 1) capType = gfx_capRound;
691 else if(lineCap == 2) capType = gfx_capSquare;
693 gfx_joinType joinType = gfx_joinRound;
694 if(lineJoin == 0) joinType = gfx_joinMiter;
695 else if(lineJoin == 1) joinType = gfx_joinRound;
696 else if(lineJoin == 2) joinType = gfx_joinBevel;
699 double dashphase = 0;
701 state->getLineDash(&ldash, &dashnum, &dashphase);
705 if(dashnum && ldash) {
706 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
710 msg("<trace> %d dashes", dashnum);
711 msg("<trace> | phase: %f", dashphase);
712 for(t=0;t<dashnum;t++) {
714 msg("<trace> | d%-3d: %f", t, ldash[t]);
717 if(getLogLevel() >= LOGLEVEL_TRACE) {
721 line2 = gfxtool_dash_line(line, dash, dashphase);
724 msg("<trace> After dashing:");
727 if(getLogLevel() >= LOGLEVEL_TRACE) {
728 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
730 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
731 lineCap==0?"butt": (lineJoin==1?"round":"square"),
733 col.r,col.g,col.b,col.a
738 if(flags&STROKE_FILL) {
739 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
740 gfxline_t*gfxline = SVPtogfxline(svp);
741 if(flags&STROKE_CLIP) {
742 device->startclip(device, gfxline);
743 states[statepos].clipping++;
745 device->fill(device, gfxline, &col);
750 if(flags&STROKE_CLIP)
751 msg("<error> Stroke&clip not supported at the same time");
752 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
759 gfxcolor_t getFillColor(GfxState * state)
762 double opaq = state->getFillOpacity();
763 state->getFillRGB(&rgb);
765 col.r = colToByte(rgb.r);
766 col.g = colToByte(rgb.g);
767 col.b = colToByte(rgb.b);
768 col.a = (unsigned char)(opaq*255);
772 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
774 gfxcolor_t col = getFillColor(state);
776 if(getLogLevel() >= LOGLEVEL_TRACE) {
777 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
780 device->fill(device, line, &col);
783 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
785 if(getLogLevel() >= LOGLEVEL_TRACE) {
786 msg("<trace> clip\n");
790 device->startclip(device, line);
791 states[statepos].clipping++;
794 void GFXOutputDev::clip(GfxState *state)
796 GfxPath * path = state->getPath();
797 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
798 clipToGfxLine(state, line);
802 void GFXOutputDev::eoClip(GfxState *state)
804 GfxPath * path = state->getPath();
805 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
807 if(getLogLevel() >= LOGLEVEL_TRACE) {
808 msg("<trace> eoclip\n");
812 device->startclip(device, line);
813 states[statepos].clipping++;
816 void GFXOutputDev::clipToStrokePath(GfxState *state)
818 GfxPath * path = state->getPath();
819 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
820 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
824 void GFXOutputDev::endframe()
827 device->endclip(device);
832 void GFXOutputDev::finish()
836 device->endclip(device);
842 GFXOutputDev::~GFXOutputDev()
847 free(this->pages); this->pages = 0;
850 fontlist_t*l = this->fontlist;
852 fontlist_t*next = l->next;
854 gfxfont_free(l->font);
855 free(l->filename);l->filename=0;
861 GBool GFXOutputDev::upsideDown()
865 GBool GFXOutputDev::useDrawChar()
870 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
871 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
873 #define RENDER_FILL 0
874 #define RENDER_STROKE 1
875 #define RENDER_FILLSTROKE 2
876 #define RENDER_INVISIBLE 3
877 #define RENDER_CLIP 4
879 static char tmp_printstr[4096];
880 char* makeStringPrintable(char*str)
882 int len = strlen(str);
897 tmp_printstr[len++] = '.';
898 tmp_printstr[len++] = '.';
899 tmp_printstr[len++] = '.';
901 tmp_printstr[len] = 0;
906 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
908 const char*uniname = 0;
913 /* find out char name from unicode index
914 TODO: should be precomputed
916 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
917 if(nameToUnicodeTab[t].u == u) {
918 uniname = nameToUnicodeTab[t].name;
926 for(t=0;t<font->num_glyphs;t++) {
927 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
928 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
932 /* if we didn't find the character, maybe
933 we can find the capitalized version */
934 for(t=0;t<font->num_glyphs;t++) {
935 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
936 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
944 for(t=0;t<font->num_glyphs;t++) {
945 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
946 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
950 /* if we didn't find the character, maybe
951 we can find the capitalized version */
952 for(t=0;t<font->num_glyphs;t++) {
953 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
954 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
960 /* try to use the unicode id */
961 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
962 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
963 return font->unicode2glyph[u];
965 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
966 FIXME: do this only if we know the font is wingdings?
969 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
970 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
971 return font->unicode2glyph[u];
974 if(charnr>=0 && charnr<font->num_glyphs) {
975 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
983 void GFXOutputDev::beginString(GfxState *state, GString *s)
985 int render = state->getRender();
986 if(current_text_stroke) {
987 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
990 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
991 double m11,m21,m12,m22;
992 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
993 state->getFontTransMat(&m11, &m12, &m21, &m22);
994 m11 *= state->getHorizScaling();
995 m21 *= state->getHorizScaling();
997 this->current_font_matrix.m00 = m11 / 1024.0;
998 this->current_font_matrix.m01 = m12 / 1024.0;
999 this->current_font_matrix.m10 = -m21 / 1024.0;
1000 this->current_font_matrix.m11 = -m22 / 1024.0;
1001 this->current_font_matrix.tx = 0;
1002 this->current_font_matrix.ty = 0;
1004 gfxmatrix_t m = this->current_font_matrix;
1007 static gfxline_t* mkEmptyGfxShape(double x, double y)
1009 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1010 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1014 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1015 double dx, double dy,
1016 double originX, double originY,
1017 CharCode c, int nBytes, Unicode *_u, int uLen)
1019 int render = state->getRender();
1020 // check for invisible text -- this is used by Acrobat Capture
1022 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1026 gfxcolor_t col = getFillColor(state);
1028 Gushort *CIDToGIDMap = 0;
1029 GfxFont*font = state->getFont();
1031 if(font->getType() == fontType3 && do_interpretType3Chars) {
1032 /* type 3 chars are passed as graphics */
1033 msg("<debug> type3 char at %f/%f", x, y);
1043 /* char*fontname = getFontName(font);
1044 if(u<256 && strstr(fontname, "ingdings")) {
1045 // symbols are at 0xe000 in the unicode table
1050 if(font->isCIDFont()) {
1051 GfxCIDFont*cfont = (GfxCIDFont*)font;
1053 if(font->getType() == fontCIDType2)
1054 CIDToGIDMap = cfont->getCIDToGID();
1057 font8 = (Gfx8BitFont*)font;
1058 char**enc=font8->getEncoding();
1062 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);
1065 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);
1071 charid = getGfxCharID(current_gfxfont, c, name, u);
1073 charid = getGfxCharID(current_gfxfont, c, name, -1);
1076 /* multiple unicodes- should usually map to a ligature.
1077 if the ligature doesn't exist, we need to draw
1078 the characters one-by-one. */
1080 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1081 for(t=0;t<uLen;t++) {
1082 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1088 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1089 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1093 gfxmatrix_t m = this->current_font_matrix;
1094 state->transform(x, y, &m.tx, &m.ty);
1095 m.tx += user_movex + clipmovex;
1096 m.ty += user_movey + clipmovey;
1098 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1100 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1104 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1110 msg("<warning> Drawing empty character charid=%d", charid);
1114 if(render == RENDER_FILL) {
1115 device->drawchar(device, current_gfxfont, charid, &col, &m);
1117 msg("<debug> Drawing glyph %d as shape", charid);
1119 msg("<notice> Some texts will be rendered as shape");
1122 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1123 gfxline_t*tglyph = gfxline_clone(glyph);
1124 gfxline_transform(tglyph, &m);
1125 if((render&3) != RENDER_INVISIBLE) {
1126 gfxline_t*add = gfxline_clone(tglyph);
1127 current_text_stroke = gfxline_append(current_text_stroke, add);
1129 if(render&RENDER_CLIP) {
1130 gfxline_t*add = gfxline_clone(tglyph);
1131 current_text_clip = gfxline_append(current_text_clip, add);
1132 if(!current_text_clip) {
1133 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1136 gfxline_free(tglyph);
1140 void GFXOutputDev::endString(GfxState *state)
1142 int render = state->getRender();
1143 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1145 if(current_text_stroke) {
1146 /* fillstroke and stroke text rendering objects we can process right
1147 now (as there may be texts of other rendering modes in this
1148 text object)- clipping objects have to wait until endTextObject,
1150 device->setparameter(device, "mark","TXT");
1151 if((render&3) == RENDER_FILL) {
1152 fillGfxLine(state, current_text_stroke);
1153 gfxline_free(current_text_stroke);
1154 current_text_stroke = 0;
1155 } else if((render&3) == RENDER_FILLSTROKE) {
1156 fillGfxLine(state, current_text_stroke);
1157 strokeGfxline(state, current_text_stroke,0);
1158 gfxline_free(current_text_stroke);
1159 current_text_stroke = 0;
1160 } else if((render&3) == RENDER_STROKE) {
1161 strokeGfxline(state, current_text_stroke,0);
1162 gfxline_free(current_text_stroke);
1163 current_text_stroke = 0;
1165 device->setparameter(device, "mark","");
1169 void GFXOutputDev::endTextObject(GfxState *state)
1171 int render = state->getRender();
1172 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
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].createsoftmask = states[statepos-1].createsoftmask;
1487 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1488 states[statepos].clipping = 0;
1491 void GFXOutputDev::restoreState(GfxState *state) {
1492 dbgindent-=2; dbg("restoreState");
1495 msg("<error> Invalid restoreState");
1498 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1499 states[statepos].clipping?" (end clipping)":"");
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 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2833 if(c < min) c = min;
2834 if(c > max) c = max;
2838 void GFXOutputDev::clearSoftMask(GfxState *state)
2840 if(!states[statepos].softmask)
2842 states[statepos].softmask = 0;
2843 dbg("clearSoftMask statepos=%d", statepos);
2844 msg("<verbose> clearSoftMask");
2846 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2847 msg("<error> Error in softmask/tgroup ordering");
2851 gfxresult_t*mask = states[statepos].softmaskrecording;
2852 gfxresult_t*below = this->device->finish(this->device);
2853 this->device = states[statepos].olddevice;
2855 /* get outline of all objects below the soft mask */
2856 gfxdevice_t uniondev;
2857 gfxdevice_union_init(&uniondev, 0);
2858 gfxresult_record_replay(below, &uniondev);
2859 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2860 uniondev.finish(&uniondev);
2862 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2864 this->device->startclip(this->device, belowoutline);
2865 gfxresult_record_replay(below, this->device);
2866 gfxresult_record_replay(mask, this->device);
2867 this->device->endclip(this->device);
2868 gfxline_free(belowoutline);
2871 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2872 if(width<=0 || height<=0)
2875 gfxdevice_t belowrender;
2876 gfxdevice_render_init(&belowrender);
2877 if(states[statepos+1].isolated) {
2878 belowrender.setparameter(&belowrender, "fillwhite", "1");
2880 belowrender.setparameter(&belowrender, "antialize", "2");
2881 belowrender.startpage(&belowrender, width, height);
2882 gfxresult_record_replay(below, &belowrender);
2883 belowrender.endpage(&belowrender);
2884 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2885 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2886 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2888 gfxdevice_t maskrender;
2889 gfxdevice_render_init(&maskrender);
2890 maskrender.startpage(&maskrender, width, height);
2891 gfxresult_record_replay(mask, &maskrender);
2892 maskrender.endpage(&maskrender);
2893 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2894 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2896 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2897 msg("<fatal> Internal error in mask drawing");
2902 for(y=0;y<height;y++) {
2903 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2904 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2905 for(x=0;x<width;x++) {
2907 if(states[statepos].softmask_alpha) {
2910 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2913 l2->a = div255(alpha*l2->a);
2915 /* DON'T premultiply alpha- this is done by fillbitmap,
2916 depending on the output device */
2917 //l2->r = div255(alpha*l2->r);
2918 //l2->g = div255(alpha*l2->g);
2919 //l2->b = div255(alpha*l2->b);
2925 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2928 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2929 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2931 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2933 mask->destroy(mask);
2934 below->destroy(below);
2935 maskresult->destroy(maskresult);
2936 belowresult->destroy(belowresult);
2937 states[statepos].softmaskrecording = 0;
2944 delete globalParams;globalParams=0;
2945 Object::memCheck(stderr);