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 GBool GFXOutputDev::useDrawForm()
669 infofeature("forms");
672 void GFXOutputDev::drawForm(Ref id)
674 msg("<error> drawForm not implemented");
676 GBool GFXOutputDev::needNonText()
680 void GFXOutputDev::endPage()
682 msg("<verbose> endPage");
685 #define STROKE_FILL 1
686 #define STROKE_CLIP 2
687 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
689 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
690 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
691 double miterLimit = state->getMiterLimit();
692 double width = state->getTransformedLineWidth();
695 double opaq = state->getStrokeOpacity();
697 state->getFillRGB(&rgb);
699 state->getStrokeRGB(&rgb);
701 col.r = colToByte(rgb.r);
702 col.g = colToByte(rgb.g);
703 col.b = colToByte(rgb.b);
704 col.a = (unsigned char)(opaq*255);
706 gfx_capType capType = gfx_capRound;
707 if(lineCap == 0) capType = gfx_capButt;
708 else if(lineCap == 1) capType = gfx_capRound;
709 else if(lineCap == 2) capType = gfx_capSquare;
711 gfx_joinType joinType = gfx_joinRound;
712 if(lineJoin == 0) joinType = gfx_joinMiter;
713 else if(lineJoin == 1) joinType = gfx_joinRound;
714 else if(lineJoin == 2) joinType = gfx_joinBevel;
717 double dashphase = 0;
719 state->getLineDash(&ldash, &dashnum, &dashphase);
723 if(dashnum && ldash) {
724 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
728 msg("<trace> %d dashes", dashnum);
729 msg("<trace> | phase: %f", dashphase);
730 for(t=0;t<dashnum;t++) {
732 msg("<trace> | d%-3d: %f", t, ldash[t]);
735 if(getLogLevel() >= LOGLEVEL_TRACE) {
739 line2 = gfxtool_dash_line(line, dash, dashphase);
742 msg("<trace> After dashing:");
745 if(getLogLevel() >= LOGLEVEL_TRACE) {
746 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
748 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
749 lineCap==0?"butt": (lineJoin==1?"round":"square"),
751 col.r,col.g,col.b,col.a
756 if(flags&STROKE_FILL) {
757 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
758 gfxline_t*gfxline = SVPtogfxline(svp);
759 if(flags&STROKE_CLIP) {
760 device->startclip(device, gfxline);
761 states[statepos].clipping++;
763 device->fill(device, gfxline, &col);
768 if(flags&STROKE_CLIP)
769 msg("<error> Stroke&clip not supported at the same time");
770 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
777 gfxcolor_t getFillColor(GfxState * state)
780 double opaq = state->getFillOpacity();
781 state->getFillRGB(&rgb);
783 col.r = colToByte(rgb.r);
784 col.g = colToByte(rgb.g);
785 col.b = colToByte(rgb.b);
786 col.a = (unsigned char)(opaq*255);
790 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
792 gfxcolor_t col = getFillColor(state);
794 if(getLogLevel() >= LOGLEVEL_TRACE) {
795 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
798 device->fill(device, line, &col);
801 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
803 if(getLogLevel() >= LOGLEVEL_TRACE) {
804 msg("<trace> clip\n");
808 device->startclip(device, line);
809 states[statepos].clipping++;
812 void GFXOutputDev::clip(GfxState *state)
814 GfxPath * path = state->getPath();
815 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
816 clipToGfxLine(state, line);
820 void GFXOutputDev::eoClip(GfxState *state)
822 GfxPath * path = state->getPath();
823 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
825 if(getLogLevel() >= LOGLEVEL_TRACE) {
826 msg("<trace> eoclip\n");
830 device->startclip(device, line);
831 states[statepos].clipping++;
834 void GFXOutputDev::clipToStrokePath(GfxState *state)
836 GfxPath * path = state->getPath();
837 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
838 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
842 void GFXOutputDev::endframe()
845 device->endclip(device);
850 void GFXOutputDev::finish()
854 device->endclip(device);
860 GFXOutputDev::~GFXOutputDev()
865 free(this->pages); this->pages = 0;
868 fontlist_t*l = this->fontlist;
870 fontlist_t*next = l->next;
872 gfxfont_free(l->font);
873 free(l->filename);l->filename=0;
879 GBool GFXOutputDev::upsideDown()
883 GBool GFXOutputDev::useDrawChar()
888 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
889 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
891 #define RENDER_FILL 0
892 #define RENDER_STROKE 1
893 #define RENDER_FILLSTROKE 2
894 #define RENDER_INVISIBLE 3
895 #define RENDER_CLIP 4
897 static char tmp_printstr[4096];
898 char* makeStringPrintable(char*str)
900 int len = strlen(str);
915 tmp_printstr[len++] = '.';
916 tmp_printstr[len++] = '.';
917 tmp_printstr[len++] = '.';
919 tmp_printstr[len] = 0;
924 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
926 const char*uniname = 0;
931 /* find out char name from unicode index
932 TODO: should be precomputed
934 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
935 if(nameToUnicodeTab[t].u == u) {
936 uniname = nameToUnicodeTab[t].name;
944 for(t=0;t<font->num_glyphs;t++) {
945 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
946 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, 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,charname)) {
954 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
962 for(t=0;t<font->num_glyphs;t++) {
963 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
964 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
968 /* if we didn't find the character, maybe
969 we can find the capitalized version */
970 for(t=0;t<font->num_glyphs;t++) {
971 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
972 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
978 /* try to use the unicode id */
979 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
980 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
981 return font->unicode2glyph[u];
983 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
984 FIXME: do this only if we know the font is wingdings?
987 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
988 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
989 return font->unicode2glyph[u];
992 if(charnr>=0 && charnr<font->num_glyphs) {
993 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1001 void GFXOutputDev::beginString(GfxState *state, GString *s)
1003 int render = state->getRender();
1004 if(current_text_stroke) {
1005 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1008 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1009 double m11,m21,m12,m22;
1010 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
1011 state->getFontTransMat(&m11, &m12, &m21, &m22);
1012 m11 *= state->getHorizScaling();
1013 m21 *= state->getHorizScaling();
1015 this->current_font_matrix.m00 = m11 / 1024.0;
1016 this->current_font_matrix.m01 = m12 / 1024.0;
1017 this->current_font_matrix.m10 = -m21 / 1024.0;
1018 this->current_font_matrix.m11 = -m22 / 1024.0;
1019 this->current_font_matrix.tx = 0;
1020 this->current_font_matrix.ty = 0;
1022 gfxmatrix_t m = this->current_font_matrix;
1025 static gfxline_t* mkEmptyGfxShape(double x, double y)
1027 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1028 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1032 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1033 double dx, double dy,
1034 double originX, double originY,
1035 CharCode c, int nBytes, Unicode *_u, int uLen)
1037 int render = state->getRender();
1038 // check for invisible text -- this is used by Acrobat Capture
1040 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1044 gfxcolor_t col = getFillColor(state);
1046 Gushort *CIDToGIDMap = 0;
1047 GfxFont*font = state->getFont();
1049 if(font->getType() == fontType3 && do_interpretType3Chars) {
1050 /* type 3 chars are passed as graphics */
1051 msg("<debug> type3 char at %f/%f", x, y);
1061 /* char*fontname = getFontName(font);
1062 if(u<256 && strstr(fontname, "ingdings")) {
1063 // symbols are at 0xe000 in the unicode table
1068 if(font->isCIDFont()) {
1069 GfxCIDFont*cfont = (GfxCIDFont*)font;
1071 if(font->getType() == fontCIDType2)
1072 CIDToGIDMap = cfont->getCIDToGID();
1075 font8 = (Gfx8BitFont*)font;
1076 char**enc=font8->getEncoding();
1080 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);
1083 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);
1089 charid = getGfxCharID(current_gfxfont, c, name, u);
1091 charid = getGfxCharID(current_gfxfont, c, name, -1);
1094 /* multiple unicodes- should usually map to a ligature.
1095 if the ligature doesn't exist, we need to draw
1096 the characters one-by-one. */
1098 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1099 for(t=0;t<uLen;t++) {
1100 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1106 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1107 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1111 gfxmatrix_t m = this->current_font_matrix;
1112 state->transform(x, y, &m.tx, &m.ty);
1113 m.tx += user_movex + clipmovex;
1114 m.ty += user_movey + clipmovey;
1116 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1118 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1122 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1128 msg("<warning> Drawing empty character charid=%d", charid);
1132 if(render == RENDER_FILL) {
1133 device->drawchar(device, current_gfxfont, charid, &col, &m);
1135 msg("<debug> Drawing glyph %d as shape", charid);
1137 msg("<notice> Some texts will be rendered as shape");
1140 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1141 gfxline_t*tglyph = gfxline_clone(glyph);
1142 gfxline_transform(tglyph, &m);
1143 if((render&3) != RENDER_INVISIBLE) {
1144 gfxline_t*add = gfxline_clone(tglyph);
1145 current_text_stroke = gfxline_append(current_text_stroke, add);
1147 if(render&RENDER_CLIP) {
1148 gfxline_t*add = gfxline_clone(tglyph);
1149 current_text_clip = gfxline_append(current_text_clip, add);
1150 if(!current_text_clip) {
1151 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1154 gfxline_free(tglyph);
1158 void GFXOutputDev::endString(GfxState *state)
1160 int render = state->getRender();
1161 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1163 if(current_text_stroke) {
1164 /* fillstroke and stroke text rendering objects we can process right
1165 now (as there may be texts of other rendering modes in this
1166 text object)- clipping objects have to wait until endTextObject,
1168 device->setparameter(device, "mark","TXT");
1169 if((render&3) == RENDER_FILL) {
1170 fillGfxLine(state, current_text_stroke);
1171 gfxline_free(current_text_stroke);
1172 current_text_stroke = 0;
1173 } else if((render&3) == RENDER_FILLSTROKE) {
1174 fillGfxLine(state, current_text_stroke);
1175 strokeGfxline(state, current_text_stroke,0);
1176 gfxline_free(current_text_stroke);
1177 current_text_stroke = 0;
1178 } else if((render&3) == RENDER_STROKE) {
1179 strokeGfxline(state, current_text_stroke,0);
1180 gfxline_free(current_text_stroke);
1181 current_text_stroke = 0;
1183 device->setparameter(device, "mark","");
1187 void GFXOutputDev::endTextObject(GfxState *state)
1189 int render = state->getRender();
1190 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1192 if(current_text_clip) {
1193 device->setparameter(device, "mark","TXT");
1194 clipToGfxLine(state, current_text_clip);
1195 device->setparameter(device, "mark","");
1196 gfxline_free(current_text_clip);
1197 current_text_clip = 0;
1201 /* the logic seems to be as following:
1202 first, beginType3Char is called, with the charcode and the coordinates.
1203 if this function returns true, it already knew about the char and has now drawn it.
1204 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1205 the all draw operations until endType3Char are part of the char (which in this moment is
1206 at the position first passed to beginType3Char). the char ends with endType3Char.
1208 The drawing operations between beginType3Char and endType3Char are somewhat different to
1209 the normal ones. For example, the fillcolor equals the stroke color.
1212 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1214 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1219 gfxcolor_t col={255,0,0,0};
1220 gfxmatrix_t m = {1,0,0, 0,1,0};
1222 for(t=0;t<uLen;t++) {
1223 device->drawchar(device, 0, u[t], &col, &m);
1226 /* the character itself is going to be passed using the draw functions */
1227 return gFalse; /* gTrue= is_in_cache? */
1230 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1231 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1233 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1234 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1238 void GFXOutputDev::endType3Char(GfxState *state)
1241 msg("<debug> endType3Char");
1244 void GFXOutputDev::startFrame(int width, int height)
1246 if(outer_clip_box) {
1247 device->endclip(device);
1251 device->startpage(device, width, height);
1252 this->width = width;
1253 this->height = height;
1256 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1258 this->currentpage = pageNum;
1260 int rot = doc->getPageRotate(1);
1263 gfxline_t clippath[5];
1265 white.r = white.g = white.b = white.a = 255;
1267 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1268 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1269 Use CropBox, not MediaBox, as page size
1276 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1277 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1279 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1280 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1282 this->clipmovex = -(int)x1;
1283 this->clipmovey = -(int)y1;
1285 /* apply user clip box */
1286 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1287 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1288 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1289 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1290 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1291 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1294 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1296 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);
1298 msg("<verbose> page is rotated %d degrees\n", rot);
1300 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1301 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1302 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1303 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1304 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1305 device->startclip(device, clippath); outer_clip_box = 1;
1306 device->fill(device, clippath, &white);
1310 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1312 double x1, y1, x2, y2, w;
1313 gfxline_t points[5];
1316 msg("<debug> drawlink\n");
1318 link->getRect(&x1, &y1, &x2, &y2);
1319 cvtUserToDev(x1, y1, &x, &y);
1320 points[0].type = gfx_moveTo;
1321 points[0].x = points[4].x = x + user_movex + clipmovex;
1322 points[0].y = points[4].y = y + user_movey + clipmovey;
1323 points[0].next = &points[1];
1324 cvtUserToDev(x2, y1, &x, &y);
1325 points[1].type = gfx_lineTo;
1326 points[1].x = x + user_movex + clipmovex;
1327 points[1].y = y + user_movey + clipmovey;
1328 points[1].next = &points[2];
1329 cvtUserToDev(x2, y2, &x, &y);
1330 points[2].type = gfx_lineTo;
1331 points[2].x = x + user_movex + clipmovex;
1332 points[2].y = y + user_movey + clipmovey;
1333 points[2].next = &points[3];
1334 cvtUserToDev(x1, y2, &x, &y);
1335 points[3].type = gfx_lineTo;
1336 points[3].x = x + user_movex + clipmovex;
1337 points[3].y = y + user_movey + clipmovey;
1338 points[3].next = &points[4];
1339 cvtUserToDev(x1, y1, &x, &y);
1340 points[4].type = gfx_lineTo;
1341 points[4].x = x + user_movex + clipmovex;
1342 points[4].y = y + user_movey + clipmovey;
1345 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1346 points[0].x, points[0].y,
1347 points[1].x, points[1].y,
1348 points[2].x, points[2].y,
1349 points[3].x, points[3].y);
1351 LinkAction*action=link->getAction();
1354 const char*type = "-?-";
1357 msg("<trace> drawlink action=%d\n", action->getKind());
1358 switch(action->getKind())
1362 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1363 LinkDest *dest=NULL;
1364 if (ha->getDest()==NULL)
1365 dest=catalog->findDest(ha->getNamedDest());
1366 else dest=ha->getDest();
1368 if (dest->isPageRef()){
1369 Ref pageref=dest->getPageRef();
1370 page=catalog->findPage(pageref.num,pageref.gen);
1372 else page=dest->getPageNum();
1373 sprintf(buf, "%d", page);
1380 LinkGoToR*l = (LinkGoToR*)action;
1381 GString*g = l->getFileName();
1383 s = strdup(g->getCString());
1385 /* if the GoToR link has no filename, then
1386 try to find a refernce in the *local*
1388 GString*g = l->getNamedDest();
1390 s = strdup(g->getCString());
1396 LinkNamed*l = (LinkNamed*)action;
1397 GString*name = l->getName();
1399 s = strdup(name->lowerCase()->getCString());
1400 named = name->getCString();
1403 if(strstr(s, "next") || strstr(s, "forward"))
1405 page = currentpage + 1;
1407 else if(strstr(s, "prev") || strstr(s, "back"))
1409 page = currentpage - 1;
1411 else if(strstr(s, "last") || strstr(s, "end"))
1413 if(pages && pagepos>0)
1414 page = pages[pagepos-1];
1416 else if(strstr(s, "first") || strstr(s, "top"))
1424 case actionLaunch: {
1426 LinkLaunch*l = (LinkLaunch*)action;
1427 GString * str = new GString(l->getFileName());
1428 GString * params = l->getParams();
1430 str->append(params);
1431 s = strdup(str->getCString());
1438 LinkURI*l = (LinkURI*)action;
1439 GString*g = l->getURI();
1441 url = g->getCString();
1446 case actionUnknown: {
1448 LinkUnknown*l = (LinkUnknown*)action;
1453 msg("<error> Unknown link type!\n");
1458 if(!s) s = strdup("-?-");
1460 msg("<trace> drawlink s=%s\n", s);
1462 if(!linkinfo && (page || s))
1464 msg("<notice> File contains links");
1472 for(t=1;t<=pagepos;t++) {
1473 if(pages[t]==page) {
1482 sprintf(buf, "page%d", lpage);
1483 device->drawlink(device, points, buf);
1487 device->drawlink(device, points, s);
1490 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1494 void GFXOutputDev::saveState(GfxState *state) {
1495 dbg("saveState");dbgindent+=2;
1497 msg("<trace> saveState\n");
1500 msg("<error> Too many nested states in pdf.");
1504 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1505 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1506 states[statepos].clipping = 0;
1509 void GFXOutputDev::restoreState(GfxState *state) {
1510 dbgindent-=2; dbg("restoreState");
1513 msg("<error> Invalid restoreState");
1516 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1517 states[statepos].clipping?" (end clipping)":"");
1518 if(states[statepos].softmask) {
1519 clearSoftMask(state);
1522 while(states[statepos].clipping) {
1523 device->endclip(device);
1524 states[statepos].clipping--;
1529 char* writeOutStdFont(fontentry* f)
1534 char* tmpFileName = mktmpname(namebuf1);
1536 sprintf(namebuf2, "%s.afm", tmpFileName);
1537 fi = fopen(namebuf2, "wb");
1540 fwrite(f->afm, 1, f->afmlen, fi);
1543 sprintf(namebuf2, "%s.pfb", tmpFileName);
1544 fi = fopen(namebuf2, "wb");
1547 fwrite(f->pfb, 1, f->pfblen, fi);
1550 return strdup(namebuf2);
1553 char* GFXOutputDev::searchFont(const char*name)
1558 msg("<verbose> SearchFont(%s)", name);
1560 /* see if it is a pdf standard font */
1561 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1563 if(!strcmp(name, pdf2t1map[i].pdffont))
1565 if(!pdf2t1map[i].fullfilename) {
1566 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1567 if(!pdf2t1map[i].fullfilename) {
1568 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1570 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1573 return strdup(pdf2t1map[i].fullfilename);
1576 /* else look in all font files */
1577 for(i=0;i<fontnum;i++)
1579 if(strstr(fonts[i].filename, name)) {
1580 return strdup(fonts[i].filename);
1586 void GFXOutputDev::updateLineWidth(GfxState *state)
1588 double width = state->getTransformedLineWidth();
1589 //swfoutput_setlinewidth(&device, width);
1592 void GFXOutputDev::updateLineCap(GfxState *state)
1594 int c = state->getLineCap();
1597 void GFXOutputDev::updateLineJoin(GfxState *state)
1599 int j = state->getLineJoin();
1602 void GFXOutputDev::updateFillColor(GfxState *state)
1605 double opaq = state->getFillOpacity();
1606 state->getFillRGB(&rgb);
1608 void GFXOutputDev::updateFillOpacity(GfxState *state)
1611 double opaq = state->getFillOpacity();
1612 state->getFillRGB(&rgb);
1613 dbg("update fillopaq %f", opaq);
1615 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1617 double opaq = state->getFillOpacity();
1618 dbg("update strokeopaq %f", opaq);
1620 void GFXOutputDev::updateFillOverprint(GfxState *state)
1622 double opaq = state->getFillOverprint();
1623 dbg("update filloverprint %f", opaq);
1625 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1627 double opaq = state->getStrokeOverprint();
1628 dbg("update strokeoverprint %f", opaq);
1630 void GFXOutputDev::updateTransfer(GfxState *state)
1632 dbg("update transfer");
1636 void GFXOutputDev::updateStrokeColor(GfxState *state)
1639 double opaq = state->getStrokeOpacity();
1640 state->getStrokeRGB(&rgb);
1643 void FoFiWrite(void *stream, char *data, int len)
1645 int ret = fwrite(data, len, 1, (FILE*)stream);
1648 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1650 char*tmpFileName = NULL;
1656 Object refObj, strObj;
1658 tmpFileName = mktmpname(namebuf);
1661 ret = font->getEmbeddedFontID(&embRef);
1663 msg("<verbose> Didn't get embedded font id");
1664 /* not embedded- the caller should now search the font
1665 directories for this font */
1669 f = fopen(tmpFileName, "wb");
1671 msg("<error> Couldn't create temporary Type 1 font file");
1675 /*if(font->isCIDFont()) {
1676 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1677 GString c = cidFont->getCollection();
1678 msg("<notice> Collection: %s", c.getCString());
1681 //if (font->getType() == fontType1C) {
1682 if (0) { //font->getType() == fontType1C) {
1683 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1685 msg("<error> Couldn't read embedded font file");
1688 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1690 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1691 //cvt->convertToCIDType0("test", f);
1692 //cvt->convertToType0("test", f);
1695 } else if(font->getType() == fontTrueType) {
1696 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1697 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1699 msg("<error> Couldn't read embedded font file");
1702 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1703 cvt->writeTTF(FoFiWrite, f);
1707 font->getEmbeddedFontID(&embRef);
1708 refObj.initRef(embRef.num, embRef.gen);
1709 refObj.fetch(ref, &strObj);
1711 strObj.streamReset();
1716 f4[t] = strObj.streamGetChar();
1717 f4c[t] = (char)f4[t];
1722 if(!strncmp(f4c, "true", 4)) {
1723 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1724 Change this on the fly */
1725 f4[0] = f4[2] = f4[3] = 0;
1733 while ((c = strObj.streamGetChar()) != EOF) {
1737 strObj.streamClose();
1742 return strdup(tmpFileName);
1745 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1747 char*name = getFontName(gfxFont);
1751 if(!this->config_use_fontconfig)
1754 #ifdef HAVE_FONTCONFIG
1755 FcPattern *pattern, *match;
1759 static int fcinitcalled = false;
1761 msg("<debug> searchForSuitableFont(%s)", name);
1763 // call init ony once
1764 if (!fcinitcalled) {
1765 msg("<debug> Initializing FontConfig...");
1766 fcinitcalled = true;
1768 msg("<debug> FontConfig Initialization failed. Disabling.");
1769 config_use_fontconfig = 0;
1772 msg("<debug> ...initialized FontConfig");
1775 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1776 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1777 if (gfxFont->isItalic()) // check for italic
1778 msg("<debug> FontConfig: Adding Italic Slant");
1779 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1780 if (gfxFont->isBold()) // check for bold
1781 msg("<debug> FontConfig: Adding Bold Weight");
1782 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1784 msg("<debug> FontConfig: Try to match...");
1785 // configure and match using the original font name
1786 FcConfigSubstitute(0, pattern, FcMatchPattern);
1787 FcDefaultSubstitute(pattern);
1788 match = FcFontMatch(0, pattern, &result);
1790 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1791 msg("<debug> FontConfig: family=%s", (char*)v);
1792 // if we get an exact match
1793 if (strcmp((char *)v, name) == 0) {
1794 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1795 filename = strdup((char*)v); // mem leak
1796 char *nfn = strrchr(filename, '/');
1797 if(nfn) fontname = strdup(nfn+1);
1798 else fontname = filename;
1800 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1802 // initialize patterns
1803 FcPatternDestroy(pattern);
1804 FcPatternDestroy(match);
1806 // now match against serif etc.
1807 if (gfxFont->isSerif()) {
1808 msg("<debug> FontConfig: Create Serif Family Pattern");
1809 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1810 } else if (gfxFont->isFixedWidth()) {
1811 msg("<debug> FontConfig: Create Monospace Family Pattern");
1812 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1814 msg("<debug> FontConfig: Create Sans Family Pattern");
1815 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1819 if (gfxFont->isItalic()) {
1820 msg("<debug> FontConfig: Adding Italic Slant");
1821 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1824 if (gfxFont->isBold()) {
1825 msg("<debug> FontConfig: Adding Bold Weight");
1826 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1829 msg("<debug> FontConfig: Try to match... (2)");
1830 // configure and match using serif etc
1831 FcConfigSubstitute (0, pattern, FcMatchPattern);
1832 FcDefaultSubstitute (pattern);
1833 match = FcFontMatch (0, pattern, &result);
1835 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1836 filename = strdup((char*)v); // mem leak
1837 char *nfn = strrchr(filename, '/');
1838 if(nfn) fontname = strdup(nfn+1);
1839 else fontname = filename;
1841 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1845 //printf("FONTCONFIG: pattern");
1846 //FcPatternPrint(pattern);
1847 //printf("FONTCONFIG: match");
1848 //FcPatternPrint(match);
1850 FcPatternDestroy(pattern);
1851 FcPatternDestroy(match);
1853 pdfswf_addfont(filename);
1860 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1862 const char*fontname = 0, *filename = 0;
1863 msg("<notice> substituteFont(%s)", oldname);
1865 if(!(fontname = searchForSuitableFont(gfxFont))) {
1866 fontname = "Times-Roman";
1868 filename = searchFont(fontname);
1870 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1874 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1875 msg("<fatal> Too many fonts in file.");
1879 substitutesource[substitutepos] = strdup(oldname); //mem leak
1880 substitutetarget[substitutepos] = fontname;
1881 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1884 return strdup(filename); //mem leak
1887 void unlinkfont(char* filename)
1894 if(!strncmp(&filename[l-4],".afm",4)) {
1895 memcpy(&filename[l-4],".pfb",4);
1897 memcpy(&filename[l-4],".pfa",4);
1899 memcpy(&filename[l-4],".afm",4);
1902 if(!strncmp(&filename[l-4],".pfa",4)) {
1903 memcpy(&filename[l-4],".afm",4);
1905 memcpy(&filename[l-4],".pfa",4);
1908 if(!strncmp(&filename[l-4],".pfb",4)) {
1909 memcpy(&filename[l-4],".afm",4);
1911 memcpy(&filename[l-4],".pfb",4);
1916 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1922 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1925 fontlist_t*last=0,*l = this->fontlist;
1928 msg("<error> Internal Error: FontID is null");
1930 /* TODO: should this be part of the state? */
1933 if(!strcmp(l->font->id, id)) {
1934 current_gfxfont = l->font;
1936 device->addfont(device, current_gfxfont);
1941 if(!filename) return 0;
1943 /* A font size of e.g. 9 means the font will be scaled down by
1944 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1945 we have to divide 0.05 by (fontsize/1024)
1947 double quality = (1024 * 0.05) / maxSize;
1949 msg("<verbose> Loading %s...", filename);
1950 font = gfxfont_load(id, filename, quality);
1952 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1955 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1959 l->filename = strdup(filename);
1961 current_gfxfont = l->font;
1967 device->addfont(device, current_gfxfont);
1971 void GFXOutputDev::updateFont(GfxState *state)
1973 GfxFont*gfxFont = state->getFont();
1979 char * fontid = getFontID(gfxFont);
1980 char * fontname = getFontName(gfxFont);
1982 double maxSize = 1.0;
1985 maxSize = this->info->getMaximumFontSize(fontid);
1989 /* first, look if we substituted this font before-
1990 this way, we don't initialize the T1 Fonts
1992 for(t=0;t<substitutepos;t++) {
1993 if(!strcmp(fontid, substitutesource[t])) {
1994 free(fontid);fontid=0;
1995 fontid = strdup(substitutetarget[t]);
2000 /* second, see if this is a font which was used before-
2001 if so, we are done */
2002 if(setGfxFont(fontid, fontname, 0, 0)) {
2007 /* if(swfoutput_queryfont(&device, fontid))
2008 swfoutput_setfont(&device, fontid, 0);
2010 msg("<debug> updateFont(%s) [cached]", fontid);
2014 // look for Type 3 font
2015 if (gfxFont->getType() == fontType3) {
2017 type3Warning = gTrue;
2018 showFontError(gfxFont, 2);
2025 /* now either load the font, or find a substitution */
2028 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2033 (gfxFont->getType() == fontType1 ||
2034 gfxFont->getType() == fontType1C ||
2035 gfxFont->getType() == fontCIDType0C ||
2036 gfxFont->getType() == fontTrueType ||
2037 gfxFont->getType() == fontCIDType2
2040 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2041 if(!fileName) showFontError(gfxFont,0);
2044 fileName = searchFont(fontname);
2045 if(!fileName) showFontError(gfxFont,0);
2048 char * fontname = getFontName(gfxFont);
2049 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2052 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2054 msg("<warning> Try specifying one or more font directories");
2056 fileName = substituteFont(gfxFont, fontid);
2059 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2060 msg("<notice> Font is now %s (%s)", fontid, fileName);
2064 msg("<error> Couldn't set font %s\n", fontid);
2070 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2071 dumpFontInfo("<verbose>", gfxFont);
2073 //swfoutput_setfont(&device, fontid, fileName);
2075 if(!setGfxFont(fontid, fontname, 0, 0)) {
2076 setGfxFont(fontid, fontname, fileName, maxSize);
2080 unlinkfont(fileName);
2090 #define SQR(x) ((x)*(x))
2092 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2094 if((newwidth<2 || newheight<2) ||
2095 (width<=newwidth || height<=newheight))
2097 unsigned char*newdata;
2099 newdata= (unsigned char*)malloc(newwidth*newheight);
2101 double fx = (double)(width)/newwidth;
2102 double fy = (double)(height)/newheight;
2104 int blocksize = (int)(8192/(fx*fy));
2105 int r = 8192*256/palettesize;
2106 for(x=0;x<newwidth;x++) {
2107 double ex = px + fx;
2108 int fromx = (int)px;
2110 int xweight1 = (int)(((fromx+1)-px)*256);
2111 int xweight2 = (int)((ex-tox)*256);
2113 for(y=0;y<newheight;y++) {
2114 double ey = py + fy;
2115 int fromy = (int)py;
2117 int yweight1 = (int)(((fromy+1)-py)*256);
2118 int yweight2 = (int)((ey-toy)*256);
2121 for(xx=fromx;xx<=tox;xx++)
2122 for(yy=fromy;yy<=toy;yy++) {
2123 int b = 1-data[width*yy+xx];
2125 if(xx==fromx) weight = (weight*xweight1)/256;
2126 if(xx==tox) weight = (weight*xweight2)/256;
2127 if(yy==fromy) weight = (weight*yweight1)/256;
2128 if(yy==toy) weight = (weight*yweight2)/256;
2131 //if(a) a=(palettesize-1)*r/blocksize;
2132 newdata[y*newwidth+x] = (a*blocksize)/r;
2140 #define IMAGE_TYPE_JPEG 0
2141 #define IMAGE_TYPE_LOSSLESS 1
2143 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2144 double x1,double y1,
2145 double x2,double y2,
2146 double x3,double y3,
2147 double x4,double y4, int type)
2149 gfxcolor_t*newpic=0;
2151 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2152 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2154 gfxline_t p1,p2,p3,p4,p5;
2155 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2156 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2157 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2158 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2159 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2161 {p1.x = (int)(p1.x*20)/20.0;
2162 p1.y = (int)(p1.y*20)/20.0;
2163 p2.x = (int)(p2.x*20)/20.0;
2164 p2.y = (int)(p2.y*20)/20.0;
2165 p3.x = (int)(p3.x*20)/20.0;
2166 p3.y = (int)(p3.y*20)/20.0;
2167 p4.x = (int)(p4.x*20)/20.0;
2168 p4.y = (int)(p4.y*20)/20.0;
2169 p5.x = (int)(p5.x*20)/20.0;
2170 p5.y = (int)(p5.y*20)/20.0;
2177 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2178 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2183 img.data = (gfxcolor_t*)data;
2187 if(type == IMAGE_TYPE_JPEG)
2188 /* TODO: pass image_dpi to device instead */
2189 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2191 dev->fillbitmap(dev, &p1, &img, &m, 0);
2194 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2195 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2197 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2200 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2201 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2203 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2207 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2208 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2209 GBool inlineImg, int mask, int*maskColors,
2210 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2212 double x1,y1,x2,y2,x3,y3,x4,y4;
2213 ImageStream *imgStr;
2218 unsigned char* maskbitmap = 0;
2221 ncomps = colorMap->getNumPixelComps();
2222 bits = colorMap->getBits();
2227 unsigned char buf[8];
2228 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2230 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2231 imgMaskStr->reset();
2232 unsigned char pal[256];
2233 int n = 1 << colorMap->getBits();
2238 maskColorMap->getGray(pixBuf, &gray);
2239 pal[t] = colToByte(gray);
2241 for (y = 0; y < maskHeight; y++) {
2242 for (x = 0; x < maskWidth; x++) {
2243 imgMaskStr->getPixel(buf);
2244 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2249 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2250 imgMaskStr->reset();
2251 for (y = 0; y < maskHeight; y++) {
2252 for (x = 0; x < maskWidth; x++) {
2253 imgMaskStr->getPixel(buf);
2255 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2263 imgStr = new ImageStream(str, width, ncomps,bits);
2266 if(!width || !height || (height<=1 && width<=1))
2268 msg("<verbose> Ignoring %d by %d image", width, height);
2269 unsigned char buf[8];
2271 for (y = 0; y < height; ++y)
2272 for (x = 0; x < width; ++x) {
2273 imgStr->getPixel(buf);
2281 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2282 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2283 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2284 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2286 if(!pbminfo && !(str->getKind()==strDCT)) {
2288 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2292 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2294 if(!jpeginfo && (str->getKind()==strDCT)) {
2295 msg("<notice> file contains jpeg pictures");
2301 unsigned char buf[8];
2303 unsigned char*pic = new unsigned char[width*height];
2304 gfxcolor_t pal[256];
2306 state->getFillRGB(&rgb);
2308 memset(pal,255,sizeof(pal));
2309 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2310 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2311 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2312 pal[0].a = 255; pal[1].a = 0;
2315 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2316 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2317 for (y = 0; y < height; ++y)
2318 for (x = 0; x < width; ++x)
2320 imgStr->getPixel(buf);
2323 pic[width*y+x] = buf[0];
2326 /* the size of the drawn image is added to the identifier
2327 as the same image may require different bitmaps if displayed
2328 at different sizes (due to antialiasing): */
2331 unsigned char*pic2 = 0;
2334 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2343 height = realheight;
2347 /* make a black/white palette */
2349 float r = 255/(numpalette-1);
2351 for(t=0;t<numpalette;t++) {
2352 pal[t].r = colToByte(rgb.r);
2353 pal[t].g = colToByte(rgb.g);
2354 pal[t].b = colToByte(rgb.b);
2355 pal[t].a = (unsigned char)(t*r);
2359 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2360 for (y = 0; y < height; ++y) {
2361 for (x = 0; x < width; ++x) {
2362 pic2[width*y+x] = pal[pic[y*width+x]];
2365 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2369 if(maskbitmap) free(maskbitmap);
2375 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2376 gfxcolor_t*pic=new gfxcolor_t[width*height];
2377 for (y = 0; y < height; ++y) {
2378 for (x = 0; x < width; ++x) {
2379 imgStr->getPixel(pixBuf);
2380 colorMap->getRGB(pixBuf, &rgb);
2381 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2382 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2383 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2384 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2386 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2390 if(str->getKind()==strDCT)
2391 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2393 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2396 if(maskbitmap) free(maskbitmap);
2399 gfxcolor_t*pic=new gfxcolor_t[width*height];
2400 gfxcolor_t pal[256];
2401 int n = 1 << colorMap->getBits();
2403 for(t=0;t<256;t++) {
2405 colorMap->getRGB(pixBuf, &rgb);
2407 {/*if(maskColors && *maskColors==t) {
2408 msg("<notice> Color %d is transparent", t);
2409 if (imgData->maskColors) {
2411 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2412 if (pix[i] < imgData->maskColors[2*i] ||
2413 pix[i] > imgData->maskColors[2*i+1]) {
2428 pal[t].r = (unsigned char)(colToByte(rgb.r));
2429 pal[t].g = (unsigned char)(colToByte(rgb.g));
2430 pal[t].b = (unsigned char)(colToByte(rgb.b));
2431 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2434 for (y = 0; y < height; ++y) {
2435 for (x = 0; x < width; ++x) {
2436 imgStr->getPixel(pixBuf);
2437 pic[width*y+x] = pal[pixBuf[0]];
2439 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2443 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2447 if(maskbitmap) free(maskbitmap);
2452 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2453 int width, int height, GBool invert,
2456 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2457 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2458 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2461 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2462 int width, int height, GfxImageColorMap *colorMap,
2463 int *maskColors, GBool inlineImg)
2465 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2466 colorMap?"colorMap":"no colorMap",
2467 maskColors?"maskColors":"no maskColors",
2469 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2470 colorMap?"colorMap":"no colorMap",
2471 maskColors?"maskColors":"no maskColors",
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,inlineImg,0,maskColors, 0,0,0,0, 0);
2479 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2480 int width, int height,
2481 GfxImageColorMap *colorMap,
2482 Stream *maskStr, int maskWidth, int maskHeight,
2485 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2486 colorMap?"colorMap":"no colorMap",
2487 maskWidth, maskHeight);
2488 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2489 colorMap?"colorMap":"no colorMap",
2490 maskWidth, maskHeight);
2492 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2493 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2494 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2497 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2498 int width, int height,
2499 GfxImageColorMap *colorMap,
2501 int maskWidth, int maskHeight,
2502 GfxImageColorMap *maskColorMap)
2504 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2505 colorMap?"colorMap":"no colorMap",
2506 maskWidth, maskHeight);
2507 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2508 colorMap?"colorMap":"no colorMap",
2509 maskWidth, maskHeight);
2511 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2512 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2513 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2516 void GFXOutputDev::stroke(GfxState *state)
2520 GfxPath * path = state->getPath();
2521 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2522 strokeGfxline(state, line, 0);
2526 void GFXOutputDev::fill(GfxState *state)
2528 gfxcolor_t col = getFillColor(state);
2529 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2531 GfxPath * path = state->getPath();
2532 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2533 fillGfxLine(state, line);
2537 void GFXOutputDev::eoFill(GfxState *state)
2539 gfxcolor_t col = getFillColor(state);
2540 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2542 GfxPath * path = state->getPath();
2543 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2544 fillGfxLine(state, line);
2549 static const char* dirseparator()
2558 void addGlobalFont(const char*filename)
2561 memset(&f, 0, sizeof(fontfile_t));
2562 f.filename = filename;
2563 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2564 msg("<verbose> Adding font \"%s\".", filename);
2565 fonts[fontnum++] = f;
2567 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2571 void addGlobalLanguageDir(const char*dir)
2574 globalParams = new GlobalParams((char*)"");
2576 msg("<notice> Adding %s to language pack directories", dir);
2580 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2581 strcpy(config_file, dir);
2582 strcat(config_file, dirseparator());
2583 strcat(config_file, "add-to-xpdfrc");
2585 fi = fopen(config_file, "rb");
2587 msg("<error> Could not open %s", config_file);
2590 globalParams->parseFile(new GString(config_file), fi);
2594 void addGlobalFontDir(const char*dirname)
2596 #ifdef HAVE_DIRENT_H
2597 msg("<notice> Adding %s to font directories", dirname);
2598 lastfontdir = strdup(dirname);
2599 DIR*dir = opendir(dirname);
2601 msg("<warning> Couldn't open directory %s\n", dirname);
2606 ent = readdir (dir);
2610 char*name = ent->d_name;
2616 if(!strncasecmp(&name[l-4], ".pfa", 4))
2618 if(!strncasecmp(&name[l-4], ".pfb", 4))
2620 if(!strncasecmp(&name[l-4], ".ttf", 4))
2624 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2625 strcpy(fontname, dirname);
2626 strcat(fontname, dirseparator());
2627 strcat(fontname, name);
2628 addGlobalFont(fontname);
2633 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2637 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2643 this->pagebuflen = 1024;
2644 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2645 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2647 while(pdfpage >= this->pagebuflen)
2649 int oldlen = this->pagebuflen;
2650 this->pagebuflen+=1024;
2651 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2652 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2655 this->pages[pdfpage] = outputpage;
2656 if(pdfpage>this->pagepos)
2657 this->pagepos = pdfpage;
2663 double width,height;
2666 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2668 double xMin, yMin, xMax, yMax, x, y;
2669 double tx, ty, w, h;
2670 // transform the bbox
2671 state->transform(bbox[0], bbox[1], &x, &y);
2674 state->transform(bbox[0], bbox[3], &x, &y);
2677 } else if (x > xMax) {
2682 } else if (y > yMax) {
2685 state->transform(bbox[2], bbox[1], &x, &y);
2688 } else if (x > xMax) {
2693 } else if (y > yMax) {
2696 state->transform(bbox[2], bbox[3], &x, &y);
2699 } else if (x > xMax) {
2704 } else if (y > yMax) {
2707 tx = (int)floor(xMin);
2710 } else if (tx > width) {
2713 ty = (int)floor(yMin);
2716 } else if (ty > height) {
2719 w = (int)ceil(xMax) - tx + 1;
2720 if (tx + w > width) {
2726 h = (int)ceil(yMax) - ty + 1;
2727 if (ty + h > height) {
2741 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2742 GfxColorSpace *blendingColorSpace,
2743 GBool isolated, GBool knockout,
2746 const char*colormodename = "";
2747 BBox rect = mkBBox(state, bbox, this->width, this->height);
2749 if(blendingColorSpace) {
2750 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2752 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);
2753 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2754 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);
2756 states[statepos].createsoftmask |= forSoftMask;
2757 states[statepos].transparencygroup = !forSoftMask;
2758 states[statepos].isolated = isolated;
2760 states[statepos].olddevice = this->device;
2761 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2763 gfxdevice_record_init(this->device);
2765 /*if(!forSoftMask) { ////???
2766 state->setFillOpacity(0.0);
2771 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2774 dbg("endTransparencyGroup");
2775 msg("<verbose> endTransparencyGroup");
2777 gfxdevice_t*r = this->device;
2779 this->device = states[statepos].olddevice;
2781 if(states[statepos].createsoftmask) {
2782 states[statepos-1].softmaskrecording = r->finish(r);
2784 states[statepos-1].grouprecording = r->finish(r);
2787 states[statepos].createsoftmask = 0;
2788 states[statepos].transparencygroup = 0;
2792 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2794 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2795 "colordodge","colorburn","hardlight","softlight","difference",
2796 "exclusion","hue","saturation","color","luminosity"};
2798 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2799 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2801 if(state->getBlendMode() == gfxBlendNormal)
2802 infofeature("transparency groups");
2805 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2806 warnfeature(buffer, 0);
2809 gfxresult_t*grouprecording = states[statepos].grouprecording;
2811 if(state->getBlendMode() == gfxBlendNormal) {
2813 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2814 gfxresult_record_replay(grouprecording, &ops);
2817 grouprecording->destroy(grouprecording);
2819 states[statepos].grouprecording = 0;
2822 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2824 /* alpha = 1: retrieve mask values from alpha layer
2825 alpha = 0: retrieve mask values from luminance */
2826 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2827 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2828 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2829 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2831 infofeature("soft masks");
2833 warnfeature("soft masks from alpha channel",0);
2835 states[statepos].olddevice = this->device;
2836 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2837 gfxdevice_record_init(this->device);
2839 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2841 states[statepos].softmask = 1;
2842 states[statepos].softmask_alpha = alpha;
2845 static inline Guchar div255(int x) {
2846 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2849 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2851 if(c < min) c = min;
2852 if(c > max) c = max;
2856 void GFXOutputDev::clearSoftMask(GfxState *state)
2858 if(!states[statepos].softmask)
2860 states[statepos].softmask = 0;
2861 dbg("clearSoftMask statepos=%d", statepos);
2862 msg("<verbose> clearSoftMask");
2864 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2865 msg("<error> Error in softmask/tgroup ordering");
2869 gfxresult_t*mask = states[statepos].softmaskrecording;
2870 gfxresult_t*below = this->device->finish(this->device);
2871 this->device = states[statepos].olddevice;
2873 /* get outline of all objects below the soft mask */
2874 gfxdevice_t uniondev;
2875 gfxdevice_union_init(&uniondev, 0);
2876 gfxresult_record_replay(below, &uniondev);
2877 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2878 uniondev.finish(&uniondev);
2880 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2882 this->device->startclip(this->device, belowoutline);
2883 gfxresult_record_replay(below, this->device);
2884 gfxresult_record_replay(mask, this->device);
2885 this->device->endclip(this->device);
2886 gfxline_free(belowoutline);
2889 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2890 if(width<=0 || height<=0)
2893 gfxdevice_t belowrender;
2894 gfxdevice_render_init(&belowrender);
2895 if(states[statepos+1].isolated) {
2896 belowrender.setparameter(&belowrender, "fillwhite", "1");
2898 belowrender.setparameter(&belowrender, "antialize", "2");
2899 belowrender.startpage(&belowrender, width, height);
2900 gfxresult_record_replay(below, &belowrender);
2901 belowrender.endpage(&belowrender);
2902 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2903 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2904 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2906 gfxdevice_t maskrender;
2907 gfxdevice_render_init(&maskrender);
2908 maskrender.startpage(&maskrender, width, height);
2909 gfxresult_record_replay(mask, &maskrender);
2910 maskrender.endpage(&maskrender);
2911 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2912 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2914 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2915 msg("<fatal> Internal error in mask drawing");
2920 for(y=0;y<height;y++) {
2921 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2922 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2923 for(x=0;x<width;x++) {
2925 if(states[statepos].softmask_alpha) {
2928 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2931 l2->a = div255(alpha*l2->a);
2933 /* DON'T premultiply alpha- this is done by fillbitmap,
2934 depending on the output device */
2935 //l2->r = div255(alpha*l2->r);
2936 //l2->g = div255(alpha*l2->g);
2937 //l2->b = div255(alpha*l2->b);
2943 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2946 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2947 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2949 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2951 mask->destroy(mask);
2952 below->destroy(below);
2953 maskresult->destroy(maskresult);
2954 belowresult->destroy(belowresult);
2955 states[statepos].softmaskrecording = 0;
2962 delete globalParams;globalParams=0;
2963 Object::memCheck(stderr);