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->config_remapunicode=0;
276 this->do_interpretType3Chars = gTrue;
278 this->parameters = p;
280 memset(states, 0, sizeof(states));
282 /* configure device */
284 setParameter(p->name, p->value);
289 void GFXOutputDev::setParameter(const char*key, const char*value)
291 if(!strcmp(key,"rawtext")) {
292 this->do_interpretType3Chars = atoi(value)^1;
293 } else if(!strcmp(key,"breakonwarning")) {
294 this->config_break_on_warning = atoi(value);
295 } else if(!strcmp(key,"fontconfig")) {
296 this->config_use_fontconfig = atoi(value);
297 } else if(!strcmp(key,"remapunicode")) {
298 this->config_remapunicode = atoi(value);
300 msg("<warning> Ignored parameter: %s=%s", key, value);
304 void GFXOutputDev::setDevice(gfxdevice_t*dev)
306 parameter_t*p = this->parameters;
308 /* pass parameters to output device */
312 this->device->setparameter(this->device, p->name, p->value);
318 void GFXOutputDev::setMove(int x,int y)
320 this->user_movex = x;
321 this->user_movey = y;
324 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
326 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
327 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
329 this->user_clipx1 = x1;
330 this->user_clipy1 = y1;
331 this->user_clipx2 = x2;
332 this->user_clipy2 = y2;
335 static char*getFontID(GfxFont*font)
337 Ref*ref = font->getID();
338 GString*gstr = font->getName();
339 char* fname = gstr==0?0:gstr->getCString();
342 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
344 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
349 static char*getFontName(GfxFont*font)
352 GString*gstr = font->getName();
353 char* fname = gstr==0?0:gstr->getCString();
357 sprintf(buf, "UFONT%d", r->num);
358 fontid = strdup(buf);
360 fontid = strdup(fname);
364 char* plus = strchr(fontid, '+');
365 if(plus && plus < &fontid[strlen(fontid)-1]) {
366 fontname = strdup(plus+1);
368 fontname = strdup(fontid);
374 static char mybuf[1024];
375 static char* gfxstate2str(GfxState *state)
379 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
386 if(state->getX1()!=0.0)
387 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
388 if(state->getY1()!=0.0)
389 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
390 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
391 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
392 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
393 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
394 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
395 state->getFillColor()->c[0], state->getFillColor()->c[1]);
396 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
397 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
398 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
399 state->getFillColor()->c[0], state->getFillColor()->c[1],
400 state->getFillColor()->c[2], state->getFillColor()->c[3],
401 state->getFillColor()->c[4], state->getFillColor()->c[5],
402 state->getFillColor()->c[6], state->getFillColor()->c[7]);
403 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
404 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
405 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
406 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
407 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
408 state->getFillRGB(&rgb);
409 if(rgb.r || rgb.g || rgb.b)
410 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
411 state->getStrokeRGB(&rgb);
412 if(rgb.r || rgb.g || rgb.b)
413 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
414 if(state->getFillColorSpace()->getNComps()>1)
415 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
416 if(state->getStrokeColorSpace()->getNComps()>1)
417 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
418 if(state->getFillPattern())
419 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
420 if(state->getStrokePattern())
421 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
423 if(state->getFillOpacity()!=1.0)
424 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
425 if(state->getStrokeOpacity()!=1.0)
426 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
428 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
433 state->getLineDash(&dash, &length, &start);
437 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
438 for(t=0;t<length;t++) {
439 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
441 bufpos+=sprintf(bufpos,"]");
444 if(state->getFlatness()!=1)
445 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
446 if(state->getLineJoin()!=0)
447 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
448 if(state->getLineJoin()!=0)
449 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
450 if(state->getLineJoin()!=0)
451 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
453 if(state->getFont() && getFontID(state->getFont()))
454 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
455 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
456 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
457 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
458 if(state->getCharSpace())
459 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
460 if(state->getWordSpace())
461 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
462 if(state->getHorizScaling()!=1.0)
463 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
464 if(state->getLeading())
465 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
467 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
468 if(state->getRender())
469 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
470 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
471 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
472 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
473 if(state->getLineX())
474 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
475 if(state->getLineY())
476 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
477 bufpos+=sprintf(bufpos," ");
481 static void dumpFontInfo(const char*loglevel, GfxFont*font);
482 static int lastdumps[1024];
483 static int lastdumppos = 0;
488 static void showFontError(GfxFont*font, int nr)
492 for(t=0;t<lastdumppos;t++)
493 if(lastdumps[t] == r->num)
497 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
498 lastdumps[lastdumppos++] = r->num;
500 msg("<warning> The following font caused problems:");
502 msg("<warning> The following font caused problems (substituting):");
504 msg("<warning> The following Type 3 Font will be rendered as graphics:");
505 dumpFontInfo("<warning>", font);
508 static void dumpFontInfo(const char*loglevel, GfxFont*font)
510 char* id = getFontID(font);
511 char* name = getFontName(font);
512 Ref* r=font->getID();
513 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
515 GString*gstr = font->getTag();
517 msg("%s| Tag: %s\n", loglevel, id);
519 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
521 GfxFontType type=font->getType();
523 case fontUnknownType:
524 msg("%s| Type: unknown\n",loglevel);
527 msg("%s| Type: 1\n",loglevel);
530 msg("%s| Type: 1C\n",loglevel);
533 msg("%s| Type: 3\n",loglevel);
536 msg("%s| Type: TrueType\n",loglevel);
539 msg("%s| Type: CIDType0\n",loglevel);
542 msg("%s| Type: CIDType0C\n",loglevel);
545 msg("%s| Type: CIDType2\n",loglevel);
550 GBool embedded = font->getEmbeddedFontID(&embRef);
552 if(font->getEmbeddedFontName()) {
553 embeddedName = font->getEmbeddedFontName()->getCString();
556 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
558 gstr = font->getExtFontFile();
560 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
562 // Get font descriptor flags.
563 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
564 if(font->isSerif()) msg("%s| is serif\n", loglevel);
565 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
566 if(font->isItalic()) msg("%s| is italic\n", loglevel);
567 if(font->isBold()) msg("%s| is bold\n", loglevel);
573 //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");}
574 //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");}
577 void dump_outline(gfxline_t*line)
580 if(line->type == gfx_moveTo) {
581 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
582 } else if(line->type == gfx_lineTo) {
583 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
584 } else if(line->type == gfx_splineTo) {
585 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
591 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
593 int num = path->getNumSubpaths();
596 double lastx=0,lasty=0,posx=0,posy=0;
599 msg("<warning> empty path");
603 gfxdrawer_target_gfxline(&draw);
605 for(t = 0; t < num; t++) {
606 GfxSubpath *subpath = path->getSubpath(t);
607 int subnum = subpath->getNumPoints();
608 double bx=0,by=0,cx=0,cy=0;
610 for(s=0;s<subnum;s++) {
613 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
618 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
619 draw.lineTo(&draw, lastx, lasty);
621 draw.moveTo(&draw, x,y);
626 } else if(subpath->getCurve(s) && cpos==0) {
630 } else if(subpath->getCurve(s) && cpos==1) {
638 draw.lineTo(&draw, x,y);
640 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
647 /* fix non-closed lines */
648 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
649 draw.lineTo(&draw, lastx, lasty);
651 gfxline_t*result = (gfxline_t*)draw.result(&draw);
653 gfxline_optimize(result);
658 GBool GFXOutputDev::useTilingPatternFill()
660 infofeature("tiled patterns");
664 GBool GFXOutputDev::useShadedFills()
666 infofeature("shaded fills");
670 GBool GFXOutputDev::useDrawForm()
672 infofeature("forms");
675 void GFXOutputDev::drawForm(Ref id)
677 msg("<error> drawForm not implemented");
679 GBool GFXOutputDev::needNonText()
683 void GFXOutputDev::endPage()
685 msg("<verbose> endPage");
688 #define STROKE_FILL 1
689 #define STROKE_CLIP 2
690 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
692 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
693 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
694 double miterLimit = state->getMiterLimit();
695 double width = state->getTransformedLineWidth();
698 double opaq = state->getStrokeOpacity();
700 state->getFillRGB(&rgb);
702 state->getStrokeRGB(&rgb);
704 col.r = colToByte(rgb.r);
705 col.g = colToByte(rgb.g);
706 col.b = colToByte(rgb.b);
707 col.a = (unsigned char)(opaq*255);
709 gfx_capType capType = gfx_capRound;
710 if(lineCap == 0) capType = gfx_capButt;
711 else if(lineCap == 1) capType = gfx_capRound;
712 else if(lineCap == 2) capType = gfx_capSquare;
714 gfx_joinType joinType = gfx_joinRound;
715 if(lineJoin == 0) joinType = gfx_joinMiter;
716 else if(lineJoin == 1) joinType = gfx_joinRound;
717 else if(lineJoin == 2) joinType = gfx_joinBevel;
720 double dashphase = 0;
722 state->getLineDash(&ldash, &dashnum, &dashphase);
726 if(dashnum && ldash) {
727 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
731 msg("<trace> %d dashes", dashnum);
732 msg("<trace> | phase: %f", dashphase);
733 for(t=0;t<dashnum;t++) {
735 msg("<trace> | d%-3d: %f", t, ldash[t]);
738 if(getLogLevel() >= LOGLEVEL_TRACE) {
742 line2 = gfxtool_dash_line(line, dash, dashphase);
745 msg("<trace> After dashing:");
748 if(getLogLevel() >= LOGLEVEL_TRACE) {
749 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
751 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
752 lineCap==0?"butt": (lineJoin==1?"round":"square"),
754 col.r,col.g,col.b,col.a
759 if(flags&STROKE_FILL) {
760 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
761 gfxline_t*gfxline = SVPtogfxline(svp);
762 if(flags&STROKE_CLIP) {
763 device->startclip(device, gfxline);
764 states[statepos].clipping++;
766 device->fill(device, gfxline, &col);
771 if(flags&STROKE_CLIP)
772 msg("<error> Stroke&clip not supported at the same time");
773 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
780 gfxcolor_t getFillColor(GfxState * state)
783 double opaq = state->getFillOpacity();
784 state->getFillRGB(&rgb);
786 col.r = colToByte(rgb.r);
787 col.g = colToByte(rgb.g);
788 col.b = colToByte(rgb.b);
789 col.a = (unsigned char)(opaq*255);
793 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
795 gfxcolor_t col = getFillColor(state);
797 if(getLogLevel() >= LOGLEVEL_TRACE) {
798 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
801 device->fill(device, line, &col);
804 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
806 if(getLogLevel() >= LOGLEVEL_TRACE) {
807 msg("<trace> clip\n");
811 device->startclip(device, line);
812 states[statepos].clipping++;
815 void GFXOutputDev::clip(GfxState *state)
817 GfxPath * path = state->getPath();
818 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
819 clipToGfxLine(state, line);
823 void GFXOutputDev::eoClip(GfxState *state)
825 GfxPath * path = state->getPath();
826 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
828 if(getLogLevel() >= LOGLEVEL_TRACE) {
829 msg("<trace> eoclip\n");
833 device->startclip(device, line);
834 states[statepos].clipping++;
837 void GFXOutputDev::clipToStrokePath(GfxState *state)
839 GfxPath * path = state->getPath();
840 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
841 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
845 void GFXOutputDev::endframe()
848 device->endclip(device);
853 void GFXOutputDev::finish()
857 device->endclip(device);
863 GFXOutputDev::~GFXOutputDev()
868 free(this->pages); this->pages = 0;
871 fontlist_t*l = this->fontlist;
873 fontlist_t*next = l->next;
875 gfxfont_free(l->font);
876 free(l->filename);l->filename=0;
882 GBool GFXOutputDev::upsideDown()
886 GBool GFXOutputDev::useDrawChar()
891 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
892 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
894 #define RENDER_FILL 0
895 #define RENDER_STROKE 1
896 #define RENDER_FILLSTROKE 2
897 #define RENDER_INVISIBLE 3
898 #define RENDER_CLIP 4
900 static char tmp_printstr[4096];
901 char* makeStringPrintable(char*str)
903 int len = strlen(str);
918 tmp_printstr[len++] = '.';
919 tmp_printstr[len++] = '.';
920 tmp_printstr[len++] = '.';
922 tmp_printstr[len] = 0;
927 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
929 const char*uniname = 0;
934 /* find out char name from unicode index
935 TODO: should be precomputed
937 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
938 if(nameToUnicodeTab[t].u == u) {
939 uniname = nameToUnicodeTab[t].name;
947 for(t=0;t<font->num_glyphs;t++) {
948 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
949 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
953 /* if we didn't find the character, maybe
954 we can find the capitalized version */
955 for(t=0;t<font->num_glyphs;t++) {
956 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
957 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
965 for(t=0;t<font->num_glyphs;t++) {
966 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
967 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
971 /* if we didn't find the character, maybe
972 we can find the capitalized version */
973 for(t=0;t<font->num_glyphs;t++) {
974 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
975 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
981 /* try to use the unicode id */
982 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
983 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
984 return font->unicode2glyph[u];
986 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
987 FIXME: do this only if we know the font is wingdings?
990 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
991 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
992 return font->unicode2glyph[u];
995 if(charnr>=0 && charnr<font->num_glyphs) {
996 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1004 void GFXOutputDev::beginString(GfxState *state, GString *s)
1006 int render = state->getRender();
1007 if(current_text_stroke) {
1008 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1011 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1012 double m11,m21,m12,m22;
1013 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
1014 state->getFontTransMat(&m11, &m12, &m21, &m22);
1015 m11 *= state->getHorizScaling();
1016 m21 *= state->getHorizScaling();
1018 this->current_font_matrix.m00 = m11 / 1024.0;
1019 this->current_font_matrix.m01 = m12 / 1024.0;
1020 this->current_font_matrix.m10 = -m21 / 1024.0;
1021 this->current_font_matrix.m11 = -m22 / 1024.0;
1022 this->current_font_matrix.tx = 0;
1023 this->current_font_matrix.ty = 0;
1025 gfxmatrix_t m = this->current_font_matrix;
1028 static gfxline_t* mkEmptyGfxShape(double x, double y)
1030 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1031 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1035 static char isValidUnicode(int c)
1037 if(c>=32 && c<0x2fffe)
1042 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1043 double dx, double dy,
1044 double originX, double originY,
1045 CharCode c, int nBytes, Unicode *_u, int uLen)
1047 int render = state->getRender();
1048 // check for invisible text -- this is used by Acrobat Capture
1050 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1054 gfxcolor_t col = getFillColor(state);
1056 Gushort *CIDToGIDMap = 0;
1057 GfxFont*font = state->getFont();
1059 if(font->getType() == fontType3 && do_interpretType3Chars) {
1060 /* type 3 chars are passed as graphics */
1061 msg("<debug> type3 char at %f/%f", x, y);
1071 /* char*fontname = getFontName(font);
1072 if(u<256 && strstr(fontname, "ingdings")) {
1073 // symbols are at 0xe000 in the unicode table
1078 if(font->isCIDFont()) {
1079 GfxCIDFont*cfont = (GfxCIDFont*)font;
1081 if(font->getType() == fontCIDType2)
1082 CIDToGIDMap = cfont->getCIDToGID();
1085 font8 = (Gfx8BitFont*)font;
1086 char**enc=font8->getEncoding();
1090 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);
1093 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);
1099 charid = getGfxCharID(current_gfxfont, c, name, u);
1101 charid = getGfxCharID(current_gfxfont, c, name, -1);
1104 /* multiple unicodes- should usually map to a ligature.
1105 if the ligature doesn't exist, we need to draw
1106 the characters one-by-one. */
1108 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1109 for(t=0;t<uLen;t++) {
1110 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1116 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1117 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1120 //useless- the font has already been passed to the output device
1121 //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) {
1122 // current_gfxfont->glyphs[charid].
1125 gfxmatrix_t m = this->current_font_matrix;
1126 state->transform(x, y, &m.tx, &m.ty);
1127 m.tx += user_movex + clipmovex;
1128 m.ty += user_movey + clipmovey;
1130 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1132 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1136 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1142 static int lastemptychar = 0;
1143 if(charid != lastemptychar)
1144 msg("<warning> Drawing empty character charid=%d u=%d", charid, u);
1145 lastemptychar = charid;
1149 if(render == RENDER_FILL) {
1150 device->drawchar(device, current_gfxfont, charid, &col, &m);
1152 msg("<debug> Drawing glyph %d as shape", charid);
1154 msg("<notice> Some texts will be rendered as shape");
1157 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1158 gfxline_t*tglyph = gfxline_clone(glyph);
1159 gfxline_transform(tglyph, &m);
1160 if((render&3) != RENDER_INVISIBLE) {
1161 gfxline_t*add = gfxline_clone(tglyph);
1162 current_text_stroke = gfxline_append(current_text_stroke, add);
1164 if(render&RENDER_CLIP) {
1165 gfxline_t*add = gfxline_clone(tglyph);
1166 current_text_clip = gfxline_append(current_text_clip, add);
1167 if(!current_text_clip) {
1168 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1171 gfxline_free(tglyph);
1175 void GFXOutputDev::endString(GfxState *state)
1177 int render = state->getRender();
1178 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1180 if(current_text_stroke) {
1181 /* fillstroke and stroke text rendering objects we can process right
1182 now (as there may be texts of other rendering modes in this
1183 text object)- clipping objects have to wait until endTextObject,
1185 device->setparameter(device, "mark","TXT");
1186 if((render&3) == RENDER_FILL) {
1187 fillGfxLine(state, current_text_stroke);
1188 gfxline_free(current_text_stroke);
1189 current_text_stroke = 0;
1190 } else if((render&3) == RENDER_FILLSTROKE) {
1191 fillGfxLine(state, current_text_stroke);
1192 strokeGfxline(state, current_text_stroke,0);
1193 gfxline_free(current_text_stroke);
1194 current_text_stroke = 0;
1195 } else if((render&3) == RENDER_STROKE) {
1196 strokeGfxline(state, current_text_stroke,0);
1197 gfxline_free(current_text_stroke);
1198 current_text_stroke = 0;
1200 device->setparameter(device, "mark","");
1204 void GFXOutputDev::endTextObject(GfxState *state)
1206 int render = state->getRender();
1207 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1209 if(current_text_clip) {
1210 device->setparameter(device, "mark","TXT");
1211 clipToGfxLine(state, current_text_clip);
1212 device->setparameter(device, "mark","");
1213 gfxline_free(current_text_clip);
1214 current_text_clip = 0;
1218 /* the logic seems to be as following:
1219 first, beginType3Char is called, with the charcode and the coordinates.
1220 if this function returns true, it already knew about the char and has now drawn it.
1221 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1222 the all draw operations until endType3Char are part of the char (which in this moment is
1223 at the position first passed to beginType3Char). the char ends with endType3Char.
1225 The drawing operations between beginType3Char and endType3Char are somewhat different to
1226 the normal ones. For example, the fillcolor equals the stroke color.
1229 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1231 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1236 gfxcolor_t col={255,0,0,0};
1237 gfxmatrix_t m = {1,0,0, 0,1,0};
1239 for(t=0;t<uLen;t++) {
1240 device->drawchar(device, 0, u[t], &col, &m);
1243 /* the character itself is going to be passed using the draw functions */
1244 return gFalse; /* gTrue= is_in_cache? */
1247 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1248 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1250 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1251 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1255 void GFXOutputDev::endType3Char(GfxState *state)
1258 msg("<debug> endType3Char");
1261 void GFXOutputDev::startFrame(int width, int height)
1263 if(outer_clip_box) {
1264 device->endclip(device);
1268 device->startpage(device, width, height);
1269 this->width = width;
1270 this->height = height;
1273 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1275 this->currentpage = pageNum;
1277 int rot = doc->getPageRotate(1);
1280 gfxline_t clippath[5];
1282 white.r = white.g = white.b = white.a = 255;
1284 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1285 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1286 Use CropBox, not MediaBox, as page size
1293 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1294 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1296 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1297 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1299 this->clipmovex = -(int)x1;
1300 this->clipmovey = -(int)y1;
1302 /* apply user clip box */
1303 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1304 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1305 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1306 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1307 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1308 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1311 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1313 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);
1315 msg("<verbose> page is rotated %d degrees\n", rot);
1317 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1318 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1319 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1320 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1321 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1322 device->startclip(device, clippath); outer_clip_box = 1;
1323 device->fill(device, clippath, &white);
1327 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1329 double x1, y1, x2, y2, w;
1330 gfxline_t points[5];
1333 msg("<debug> drawlink\n");
1335 link->getRect(&x1, &y1, &x2, &y2);
1336 cvtUserToDev(x1, y1, &x, &y);
1337 points[0].type = gfx_moveTo;
1338 points[0].x = points[4].x = x + user_movex + clipmovex;
1339 points[0].y = points[4].y = y + user_movey + clipmovey;
1340 points[0].next = &points[1];
1341 cvtUserToDev(x2, y1, &x, &y);
1342 points[1].type = gfx_lineTo;
1343 points[1].x = x + user_movex + clipmovex;
1344 points[1].y = y + user_movey + clipmovey;
1345 points[1].next = &points[2];
1346 cvtUserToDev(x2, y2, &x, &y);
1347 points[2].type = gfx_lineTo;
1348 points[2].x = x + user_movex + clipmovex;
1349 points[2].y = y + user_movey + clipmovey;
1350 points[2].next = &points[3];
1351 cvtUserToDev(x1, y2, &x, &y);
1352 points[3].type = gfx_lineTo;
1353 points[3].x = x + user_movex + clipmovex;
1354 points[3].y = y + user_movey + clipmovey;
1355 points[3].next = &points[4];
1356 cvtUserToDev(x1, y1, &x, &y);
1357 points[4].type = gfx_lineTo;
1358 points[4].x = x + user_movex + clipmovex;
1359 points[4].y = y + user_movey + clipmovey;
1362 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1363 points[0].x, points[0].y,
1364 points[1].x, points[1].y,
1365 points[2].x, points[2].y,
1366 points[3].x, points[3].y);
1368 LinkAction*action=link->getAction();
1371 const char*type = "-?-";
1374 msg("<trace> drawlink action=%d\n", action->getKind());
1375 switch(action->getKind())
1379 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1380 LinkDest *dest=NULL;
1381 if (ha->getDest()==NULL)
1382 dest=catalog->findDest(ha->getNamedDest());
1383 else dest=ha->getDest();
1385 if (dest->isPageRef()){
1386 Ref pageref=dest->getPageRef();
1387 page=catalog->findPage(pageref.num,pageref.gen);
1389 else page=dest->getPageNum();
1390 sprintf(buf, "%d", page);
1397 LinkGoToR*l = (LinkGoToR*)action;
1398 GString*g = l->getFileName();
1400 s = strdup(g->getCString());
1402 /* if the GoToR link has no filename, then
1403 try to find a refernce in the *local*
1405 GString*g = l->getNamedDest();
1407 s = strdup(g->getCString());
1413 LinkNamed*l = (LinkNamed*)action;
1414 GString*name = l->getName();
1416 s = strdup(name->lowerCase()->getCString());
1417 named = name->getCString();
1420 if(strstr(s, "next") || strstr(s, "forward"))
1422 page = currentpage + 1;
1424 else if(strstr(s, "prev") || strstr(s, "back"))
1426 page = currentpage - 1;
1428 else if(strstr(s, "last") || strstr(s, "end"))
1430 if(pages && pagepos>0)
1431 page = pages[pagepos-1];
1433 else if(strstr(s, "first") || strstr(s, "top"))
1441 case actionLaunch: {
1443 LinkLaunch*l = (LinkLaunch*)action;
1444 GString * str = new GString(l->getFileName());
1445 GString * params = l->getParams();
1447 str->append(params);
1448 s = strdup(str->getCString());
1455 LinkURI*l = (LinkURI*)action;
1456 GString*g = l->getURI();
1458 url = g->getCString();
1463 case actionUnknown: {
1465 LinkUnknown*l = (LinkUnknown*)action;
1470 msg("<error> Unknown link type!\n");
1475 if(!s) s = strdup("-?-");
1477 msg("<trace> drawlink s=%s\n", s);
1479 if(!linkinfo && (page || s))
1481 msg("<notice> File contains links");
1489 for(t=1;t<=pagepos;t++) {
1490 if(pages[t]==page) {
1499 sprintf(buf, "page%d", lpage);
1500 device->drawlink(device, points, buf);
1504 device->drawlink(device, points, s);
1507 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1511 void GFXOutputDev::saveState(GfxState *state) {
1512 dbg("saveState");dbgindent+=2;
1514 msg("<trace> saveState\n");
1517 msg("<error> Too many nested states in pdf.");
1521 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1522 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1523 states[statepos].clipping = 0;
1526 void GFXOutputDev::restoreState(GfxState *state) {
1527 dbgindent-=2; dbg("restoreState");
1530 msg("<error> Invalid restoreState");
1533 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1534 states[statepos].clipping?" (end clipping)":"");
1535 if(states[statepos].softmask) {
1536 clearSoftMask(state);
1539 while(states[statepos].clipping) {
1540 device->endclip(device);
1541 states[statepos].clipping--;
1546 char* writeOutStdFont(fontentry* f)
1551 char* tmpFileName = mktmpname(namebuf1);
1553 sprintf(namebuf2, "%s.afm", tmpFileName);
1554 fi = fopen(namebuf2, "wb");
1557 fwrite(f->afm, 1, f->afmlen, fi);
1560 sprintf(namebuf2, "%s.pfb", tmpFileName);
1561 fi = fopen(namebuf2, "wb");
1564 fwrite(f->pfb, 1, f->pfblen, fi);
1567 return strdup(namebuf2);
1570 char* GFXOutputDev::searchFont(const char*name)
1575 msg("<verbose> SearchFont(%s)", name);
1577 /* see if it is a pdf standard font */
1578 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1580 if(!strcmp(name, pdf2t1map[i].pdffont))
1582 if(!pdf2t1map[i].fullfilename) {
1583 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1584 if(!pdf2t1map[i].fullfilename) {
1585 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1587 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1590 return strdup(pdf2t1map[i].fullfilename);
1593 /* else look in all font files */
1594 for(i=0;i<fontnum;i++)
1596 if(strstr(fonts[i].filename, name)) {
1597 return strdup(fonts[i].filename);
1603 void GFXOutputDev::updateLineWidth(GfxState *state)
1605 double width = state->getTransformedLineWidth();
1606 //swfoutput_setlinewidth(&device, width);
1609 void GFXOutputDev::updateLineCap(GfxState *state)
1611 int c = state->getLineCap();
1614 void GFXOutputDev::updateLineJoin(GfxState *state)
1616 int j = state->getLineJoin();
1619 void GFXOutputDev::updateFillColor(GfxState *state)
1622 double opaq = state->getFillOpacity();
1623 state->getFillRGB(&rgb);
1625 void GFXOutputDev::updateFillOpacity(GfxState *state)
1628 double opaq = state->getFillOpacity();
1629 state->getFillRGB(&rgb);
1630 dbg("update fillopaq %f", opaq);
1632 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1634 double opaq = state->getFillOpacity();
1635 dbg("update strokeopaq %f", opaq);
1637 void GFXOutputDev::updateFillOverprint(GfxState *state)
1639 double opaq = state->getFillOverprint();
1640 dbg("update filloverprint %f", opaq);
1642 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1644 double opaq = state->getStrokeOverprint();
1645 dbg("update strokeoverprint %f", opaq);
1647 void GFXOutputDev::updateTransfer(GfxState *state)
1649 dbg("update transfer");
1653 void GFXOutputDev::updateStrokeColor(GfxState *state)
1656 double opaq = state->getStrokeOpacity();
1657 state->getStrokeRGB(&rgb);
1660 void FoFiWrite(void *stream, char *data, int len)
1662 int ret = fwrite(data, len, 1, (FILE*)stream);
1665 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1667 char*tmpFileName = NULL;
1673 Object refObj, strObj;
1675 tmpFileName = mktmpname(namebuf);
1678 ret = font->getEmbeddedFontID(&embRef);
1680 msg("<verbose> Didn't get embedded font id");
1681 /* not embedded- the caller should now search the font
1682 directories for this font */
1686 f = fopen(tmpFileName, "wb");
1688 msg("<error> Couldn't create temporary Type 1 font file");
1692 /*if(font->isCIDFont()) {
1693 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1694 GString c = cidFont->getCollection();
1695 msg("<notice> Collection: %s", c.getCString());
1698 //if (font->getType() == fontType1C) {
1699 if (0) { //font->getType() == fontType1C) {
1700 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1702 msg("<error> Couldn't read embedded font file");
1705 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1707 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1708 //cvt->convertToCIDType0("test", f);
1709 //cvt->convertToType0("test", f);
1712 } else if(font->getType() == fontTrueType) {
1713 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1714 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1716 msg("<error> Couldn't read embedded font file");
1719 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1720 cvt->writeTTF(FoFiWrite, f);
1724 font->getEmbeddedFontID(&embRef);
1725 refObj.initRef(embRef.num, embRef.gen);
1726 refObj.fetch(ref, &strObj);
1728 strObj.streamReset();
1733 f4[t] = strObj.streamGetChar();
1734 f4c[t] = (char)f4[t];
1739 if(!strncmp(f4c, "true", 4)) {
1740 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1741 Change this on the fly */
1742 f4[0] = f4[2] = f4[3] = 0;
1750 while ((c = strObj.streamGetChar()) != EOF) {
1754 strObj.streamClose();
1759 return strdup(tmpFileName);
1762 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1764 char*name = getFontName(gfxFont);
1768 if(!this->config_use_fontconfig)
1771 #ifdef HAVE_FONTCONFIG
1772 FcPattern *pattern, *match;
1776 static int fcinitcalled = false;
1778 msg("<debug> searchForSuitableFont(%s)", name);
1780 // call init ony once
1781 if (!fcinitcalled) {
1782 msg("<debug> Initializing FontConfig...");
1783 fcinitcalled = true;
1785 msg("<debug> FontConfig Initialization failed. Disabling.");
1786 config_use_fontconfig = 0;
1789 msg("<debug> ...initialized FontConfig");
1792 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1793 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1794 if (gfxFont->isItalic()) // check for italic
1795 msg("<debug> FontConfig: Adding Italic Slant");
1796 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1797 if (gfxFont->isBold()) // check for bold
1798 msg("<debug> FontConfig: Adding Bold Weight");
1799 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1801 msg("<debug> FontConfig: Try to match...");
1802 // configure and match using the original font name
1803 FcConfigSubstitute(0, pattern, FcMatchPattern);
1804 FcDefaultSubstitute(pattern);
1805 match = FcFontMatch(0, pattern, &result);
1807 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1808 msg("<debug> FontConfig: family=%s", (char*)v);
1809 // if we get an exact match
1810 if (strcmp((char *)v, name) == 0) {
1811 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1812 filename = strdup((char*)v); // mem leak
1813 char *nfn = strrchr(filename, '/');
1814 if(nfn) fontname = strdup(nfn+1);
1815 else fontname = filename;
1817 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1819 // initialize patterns
1820 FcPatternDestroy(pattern);
1821 FcPatternDestroy(match);
1823 // now match against serif etc.
1824 if (gfxFont->isSerif()) {
1825 msg("<debug> FontConfig: Create Serif Family Pattern");
1826 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1827 } else if (gfxFont->isFixedWidth()) {
1828 msg("<debug> FontConfig: Create Monospace Family Pattern");
1829 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1831 msg("<debug> FontConfig: Create Sans Family Pattern");
1832 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1836 if (gfxFont->isItalic()) {
1837 msg("<debug> FontConfig: Adding Italic Slant");
1838 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1841 if (gfxFont->isBold()) {
1842 msg("<debug> FontConfig: Adding Bold Weight");
1843 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1846 msg("<debug> FontConfig: Try to match... (2)");
1847 // configure and match using serif etc
1848 FcConfigSubstitute (0, pattern, FcMatchPattern);
1849 FcDefaultSubstitute (pattern);
1850 match = FcFontMatch (0, pattern, &result);
1852 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1853 filename = strdup((char*)v); // mem leak
1854 char *nfn = strrchr(filename, '/');
1855 if(nfn) fontname = strdup(nfn+1);
1856 else fontname = filename;
1858 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1862 //printf("FONTCONFIG: pattern");
1863 //FcPatternPrint(pattern);
1864 //printf("FONTCONFIG: match");
1865 //FcPatternPrint(match);
1867 FcPatternDestroy(pattern);
1868 FcPatternDestroy(match);
1870 pdfswf_addfont(filename);
1877 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1879 const char*fontname = 0, *filename = 0;
1880 msg("<notice> substituteFont(%s)", oldname);
1882 if(!(fontname = searchForSuitableFont(gfxFont))) {
1883 fontname = "Times-Roman";
1885 filename = searchFont(fontname);
1887 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1891 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1892 msg("<fatal> Too many fonts in file.");
1896 substitutesource[substitutepos] = strdup(oldname); //mem leak
1897 substitutetarget[substitutepos] = fontname;
1898 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1901 return strdup(filename); //mem leak
1904 void unlinkfont(char* filename)
1911 if(!strncmp(&filename[l-4],".afm",4)) {
1912 memcpy(&filename[l-4],".pfb",4);
1914 memcpy(&filename[l-4],".pfa",4);
1916 memcpy(&filename[l-4],".afm",4);
1919 if(!strncmp(&filename[l-4],".pfa",4)) {
1920 memcpy(&filename[l-4],".afm",4);
1922 memcpy(&filename[l-4],".pfa",4);
1925 if(!strncmp(&filename[l-4],".pfb",4)) {
1926 memcpy(&filename[l-4],".afm",4);
1928 memcpy(&filename[l-4],".pfb",4);
1933 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1939 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu)
1942 fontlist_t*last=0,*l = this->fontlist;
1945 msg("<error> Internal Error: FontID is null");
1947 /* TODO: should this be part of the state? */
1950 if(!strcmp(l->font->id, id)) {
1951 current_gfxfont = l->font;
1953 device->addfont(device, current_gfxfont);
1958 if(!filename) return 0;
1960 /* A font size of e.g. 9 means the font will be scaled down by
1961 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1962 we have to divide 0.05 by (fontsize/1024)
1964 double quality = (1024 * 0.05) / maxSize;
1966 msg("<verbose> Loading %s...", filename);
1967 font = gfxfont_load(id, filename, 0, quality);
1969 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1972 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1974 if(this->config_remapunicode && ctu) {
1976 for(c=0;c<font->num_glyphs;c++) {
1978 int uLen = ctu->mapToUnicode(c, u, 8);
1979 if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0]))
1980 font->glyphs[c].unicode = u[0];
1986 l->filename = strdup(filename);
1988 current_gfxfont = l->font;
1994 device->addfont(device, current_gfxfont);
1998 void GFXOutputDev::updateFont(GfxState *state)
2000 GfxFont*gfxFont = state->getFont();
2006 char * fontid = getFontID(gfxFont);
2007 char * fontname = getFontName(gfxFont);
2009 double maxSize = 1.0;
2012 maxSize = this->info->getMaximumFontSize(fontid);
2016 /* first, look if we substituted this font before-
2017 this way, we don't initialize the T1 Fonts
2019 for(t=0;t<substitutepos;t++) {
2020 if(!strcmp(fontid, substitutesource[t])) {
2021 free(fontid);fontid=0;
2022 fontid = strdup(substitutetarget[t]);
2027 /* second, see if this is a font which was used before-
2028 if so, we are done */
2029 if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2034 /* if(swfoutput_queryfont(&device, fontid))
2035 swfoutput_setfont(&device, fontid, 0);
2037 msg("<debug> updateFont(%s) [cached]", fontid);
2041 // look for Type 3 font
2042 if (gfxFont->getType() == fontType3) {
2044 type3Warning = gTrue;
2045 showFontError(gfxFont, 2);
2052 /* now either load the font, or find a substitution */
2055 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2060 (gfxFont->getType() == fontType1 ||
2061 gfxFont->getType() == fontType1C ||
2062 gfxFont->getType() == fontCIDType0C ||
2063 gfxFont->getType() == fontTrueType ||
2064 gfxFont->getType() == fontCIDType2
2067 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2068 if(!fileName) showFontError(gfxFont,0);
2071 fileName = searchFont(fontname);
2072 if(!fileName) showFontError(gfxFont,0);
2075 char * fontname = getFontName(gfxFont);
2076 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2079 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2081 msg("<warning> Try specifying one or more font directories");
2083 fileName = substituteFont(gfxFont, fontid);
2086 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2087 msg("<notice> Font is now %s (%s)", fontid, fileName);
2091 msg("<error> Couldn't set font %s\n", fontid);
2097 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2098 dumpFontInfo("<verbose>", gfxFont);
2100 //swfoutput_setfont(&device, fontid, fileName);
2102 if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2103 setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU());
2107 unlinkfont(fileName);
2117 #define SQR(x) ((x)*(x))
2119 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2121 if((newwidth<2 || newheight<2) ||
2122 (width<=newwidth || height<=newheight))
2124 unsigned char*newdata;
2126 newdata= (unsigned char*)malloc(newwidth*newheight);
2128 double fx = (double)(width)/newwidth;
2129 double fy = (double)(height)/newheight;
2131 int blocksize = (int)(8192/(fx*fy));
2132 int r = 8192*256/palettesize;
2133 for(x=0;x<newwidth;x++) {
2134 double ex = px + fx;
2135 int fromx = (int)px;
2137 int xweight1 = (int)(((fromx+1)-px)*256);
2138 int xweight2 = (int)((ex-tox)*256);
2140 for(y=0;y<newheight;y++) {
2141 double ey = py + fy;
2142 int fromy = (int)py;
2144 int yweight1 = (int)(((fromy+1)-py)*256);
2145 int yweight2 = (int)((ey-toy)*256);
2148 for(xx=fromx;xx<=tox;xx++)
2149 for(yy=fromy;yy<=toy;yy++) {
2150 int b = 1-data[width*yy+xx];
2152 if(xx==fromx) weight = (weight*xweight1)/256;
2153 if(xx==tox) weight = (weight*xweight2)/256;
2154 if(yy==fromy) weight = (weight*yweight1)/256;
2155 if(yy==toy) weight = (weight*yweight2)/256;
2158 //if(a) a=(palettesize-1)*r/blocksize;
2159 newdata[y*newwidth+x] = (a*blocksize)/r;
2167 #define IMAGE_TYPE_JPEG 0
2168 #define IMAGE_TYPE_LOSSLESS 1
2170 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2171 double x1,double y1,
2172 double x2,double y2,
2173 double x3,double y3,
2174 double x4,double y4, int type)
2176 gfxcolor_t*newpic=0;
2178 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2179 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2181 gfxline_t p1,p2,p3,p4,p5;
2182 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2183 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2184 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2185 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2186 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2188 {p1.x = (int)(p1.x*20)/20.0;
2189 p1.y = (int)(p1.y*20)/20.0;
2190 p2.x = (int)(p2.x*20)/20.0;
2191 p2.y = (int)(p2.y*20)/20.0;
2192 p3.x = (int)(p3.x*20)/20.0;
2193 p3.y = (int)(p3.y*20)/20.0;
2194 p4.x = (int)(p4.x*20)/20.0;
2195 p4.y = (int)(p4.y*20)/20.0;
2196 p5.x = (int)(p5.x*20)/20.0;
2197 p5.y = (int)(p5.y*20)/20.0;
2204 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2205 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2210 img.data = (gfxcolor_t*)data;
2214 if(type == IMAGE_TYPE_JPEG)
2215 /* TODO: pass image_dpi to device instead */
2216 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2218 dev->fillbitmap(dev, &p1, &img, &m, 0);
2221 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2222 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2224 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2227 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2228 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2230 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2234 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2235 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2236 GBool inlineImg, int mask, int*maskColors,
2237 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2239 double x1,y1,x2,y2,x3,y3,x4,y4;
2240 ImageStream *imgStr;
2245 unsigned char* maskbitmap = 0;
2248 ncomps = colorMap->getNumPixelComps();
2249 bits = colorMap->getBits();
2254 unsigned char buf[8];
2255 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2257 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2258 imgMaskStr->reset();
2259 unsigned char pal[256];
2260 int n = 1 << colorMap->getBits();
2265 maskColorMap->getGray(pixBuf, &gray);
2266 pal[t] = colToByte(gray);
2268 for (y = 0; y < maskHeight; y++) {
2269 for (x = 0; x < maskWidth; x++) {
2270 imgMaskStr->getPixel(buf);
2271 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2276 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2277 imgMaskStr->reset();
2278 for (y = 0; y < maskHeight; y++) {
2279 for (x = 0; x < maskWidth; x++) {
2280 imgMaskStr->getPixel(buf);
2282 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2290 imgStr = new ImageStream(str, width, ncomps,bits);
2293 if(!width || !height || (height<=1 && width<=1))
2295 msg("<verbose> Ignoring %d by %d image", width, height);
2296 unsigned char buf[8];
2298 for (y = 0; y < height; ++y)
2299 for (x = 0; x < width; ++x) {
2300 imgStr->getPixel(buf);
2308 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2309 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2310 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2311 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2313 if(!pbminfo && !(str->getKind()==strDCT)) {
2315 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2319 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2321 if(!jpeginfo && (str->getKind()==strDCT)) {
2322 msg("<notice> file contains jpeg pictures");
2328 unsigned char buf[8];
2330 unsigned char*pic = new unsigned char[width*height];
2331 gfxcolor_t pal[256];
2333 state->getFillRGB(&rgb);
2335 memset(pal,255,sizeof(pal));
2336 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2337 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2338 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2339 pal[0].a = 255; pal[1].a = 0;
2342 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2343 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2344 for (y = 0; y < height; ++y)
2345 for (x = 0; x < width; ++x)
2347 imgStr->getPixel(buf);
2350 pic[width*y+x] = buf[0];
2353 /* the size of the drawn image is added to the identifier
2354 as the same image may require different bitmaps if displayed
2355 at different sizes (due to antialiasing): */
2358 unsigned char*pic2 = 0;
2361 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2370 height = realheight;
2374 /* make a black/white palette */
2376 float r = 255/(numpalette-1);
2378 for(t=0;t<numpalette;t++) {
2379 pal[t].r = colToByte(rgb.r);
2380 pal[t].g = colToByte(rgb.g);
2381 pal[t].b = colToByte(rgb.b);
2382 pal[t].a = (unsigned char)(t*r);
2386 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2387 for (y = 0; y < height; ++y) {
2388 for (x = 0; x < width; ++x) {
2389 pic2[width*y+x] = pal[pic[y*width+x]];
2392 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2396 if(maskbitmap) free(maskbitmap);
2402 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2403 gfxcolor_t*pic=new gfxcolor_t[width*height];
2404 for (y = 0; y < height; ++y) {
2405 for (x = 0; x < width; ++x) {
2406 imgStr->getPixel(pixBuf);
2407 colorMap->getRGB(pixBuf, &rgb);
2408 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2409 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2410 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2411 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2413 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2417 if(str->getKind()==strDCT)
2418 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2420 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2423 if(maskbitmap) free(maskbitmap);
2426 gfxcolor_t*pic=new gfxcolor_t[width*height];
2427 gfxcolor_t pal[256];
2428 int n = 1 << colorMap->getBits();
2430 for(t=0;t<256;t++) {
2432 colorMap->getRGB(pixBuf, &rgb);
2434 {/*if(maskColors && *maskColors==t) {
2435 msg("<notice> Color %d is transparent", t);
2436 if (imgData->maskColors) {
2438 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2439 if (pix[i] < imgData->maskColors[2*i] ||
2440 pix[i] > imgData->maskColors[2*i+1]) {
2455 pal[t].r = (unsigned char)(colToByte(rgb.r));
2456 pal[t].g = (unsigned char)(colToByte(rgb.g));
2457 pal[t].b = (unsigned char)(colToByte(rgb.b));
2458 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2461 for (y = 0; y < height; ++y) {
2462 for (x = 0; x < width; ++x) {
2463 imgStr->getPixel(pixBuf);
2464 pic[width*y+x] = pal[pixBuf[0]];
2466 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2470 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2474 if(maskbitmap) free(maskbitmap);
2479 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2480 int width, int height, GBool invert,
2483 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2484 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2485 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2488 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2489 int width, int height, GfxImageColorMap *colorMap,
2490 int *maskColors, GBool inlineImg)
2492 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2493 colorMap?"colorMap":"no colorMap",
2494 maskColors?"maskColors":"no maskColors",
2496 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2497 colorMap?"colorMap":"no colorMap",
2498 maskColors?"maskColors":"no maskColors",
2501 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2502 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2503 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2506 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2507 int width, int height,
2508 GfxImageColorMap *colorMap,
2509 Stream *maskStr, int maskWidth, int maskHeight,
2512 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2513 colorMap?"colorMap":"no colorMap",
2514 maskWidth, maskHeight);
2515 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2516 colorMap?"colorMap":"no colorMap",
2517 maskWidth, maskHeight);
2519 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2520 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2521 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2524 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2525 int width, int height,
2526 GfxImageColorMap *colorMap,
2528 int maskWidth, int maskHeight,
2529 GfxImageColorMap *maskColorMap)
2531 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2532 colorMap?"colorMap":"no colorMap",
2533 maskWidth, maskHeight);
2534 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2535 colorMap?"colorMap":"no colorMap",
2536 maskWidth, maskHeight);
2538 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2539 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2540 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2543 void GFXOutputDev::stroke(GfxState *state)
2547 GfxPath * path = state->getPath();
2548 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2549 strokeGfxline(state, line, 0);
2553 void GFXOutputDev::fill(GfxState *state)
2555 gfxcolor_t col = getFillColor(state);
2556 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2558 GfxPath * path = state->getPath();
2559 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2560 fillGfxLine(state, line);
2564 void GFXOutputDev::eoFill(GfxState *state)
2566 gfxcolor_t col = getFillColor(state);
2567 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2569 GfxPath * path = state->getPath();
2570 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2571 fillGfxLine(state, line);
2576 static const char* dirseparator()
2585 void addGlobalFont(const char*filename)
2588 memset(&f, 0, sizeof(fontfile_t));
2589 f.filename = filename;
2590 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2591 msg("<verbose> Adding font \"%s\".", filename);
2592 fonts[fontnum++] = f;
2594 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2598 void addGlobalLanguageDir(const char*dir)
2601 globalParams = new GlobalParams((char*)"");
2603 msg("<notice> Adding %s to language pack directories", dir);
2607 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2608 strcpy(config_file, dir);
2609 strcat(config_file, dirseparator());
2610 strcat(config_file, "add-to-xpdfrc");
2612 fi = fopen(config_file, "rb");
2614 msg("<error> Could not open %s", config_file);
2617 globalParams->parseFile(new GString(config_file), fi);
2621 void addGlobalFontDir(const char*dirname)
2623 #ifdef HAVE_DIRENT_H
2624 msg("<notice> Adding %s to font directories", dirname);
2625 lastfontdir = strdup(dirname);
2626 DIR*dir = opendir(dirname);
2628 msg("<warning> Couldn't open directory %s\n", dirname);
2633 ent = readdir (dir);
2637 char*name = ent->d_name;
2643 if(!strncasecmp(&name[l-4], ".pfa", 4))
2645 if(!strncasecmp(&name[l-4], ".pfb", 4))
2647 if(!strncasecmp(&name[l-4], ".ttf", 4))
2651 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2652 strcpy(fontname, dirname);
2653 strcat(fontname, dirseparator());
2654 strcat(fontname, name);
2655 addGlobalFont(fontname);
2660 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2664 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2670 this->pagebuflen = 1024;
2671 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2672 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2674 while(pdfpage >= this->pagebuflen)
2676 int oldlen = this->pagebuflen;
2677 this->pagebuflen+=1024;
2678 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2679 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2682 this->pages[pdfpage] = outputpage;
2683 if(pdfpage>this->pagepos)
2684 this->pagepos = pdfpage;
2690 double width,height;
2693 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2695 double xMin, yMin, xMax, yMax, x, y;
2696 double tx, ty, w, h;
2697 // transform the bbox
2698 state->transform(bbox[0], bbox[1], &x, &y);
2701 state->transform(bbox[0], bbox[3], &x, &y);
2704 } else if (x > xMax) {
2709 } else if (y > yMax) {
2712 state->transform(bbox[2], bbox[1], &x, &y);
2715 } else if (x > xMax) {
2720 } else if (y > yMax) {
2723 state->transform(bbox[2], bbox[3], &x, &y);
2726 } else if (x > xMax) {
2731 } else if (y > yMax) {
2734 tx = (int)floor(xMin);
2737 } else if (tx > width) {
2740 ty = (int)floor(yMin);
2743 } else if (ty > height) {
2746 w = (int)ceil(xMax) - tx + 1;
2747 if (tx + w > width) {
2753 h = (int)ceil(yMax) - ty + 1;
2754 if (ty + h > height) {
2768 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2769 GfxColorSpace *blendingColorSpace,
2770 GBool isolated, GBool knockout,
2773 const char*colormodename = "";
2774 BBox rect = mkBBox(state, bbox, this->width, this->height);
2776 if(blendingColorSpace) {
2777 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2779 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);
2780 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2781 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);
2783 states[statepos].createsoftmask |= forSoftMask;
2784 states[statepos].transparencygroup = !forSoftMask;
2785 states[statepos].isolated = isolated;
2787 states[statepos].olddevice = this->device;
2788 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2790 gfxdevice_record_init(this->device);
2792 /*if(!forSoftMask) { ////???
2793 state->setFillOpacity(0.0);
2798 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2801 dbg("endTransparencyGroup");
2802 msg("<verbose> endTransparencyGroup");
2804 gfxdevice_t*r = this->device;
2806 this->device = states[statepos].olddevice;
2808 if(states[statepos].createsoftmask) {
2809 states[statepos-1].softmaskrecording = r->finish(r);
2811 states[statepos-1].grouprecording = r->finish(r);
2814 states[statepos].createsoftmask = 0;
2815 states[statepos].transparencygroup = 0;
2819 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2821 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2822 "colordodge","colorburn","hardlight","softlight","difference",
2823 "exclusion","hue","saturation","color","luminosity"};
2825 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2826 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2828 if(state->getBlendMode() == gfxBlendNormal)
2829 infofeature("transparency groups");
2832 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2833 warnfeature(buffer, 0);
2836 gfxresult_t*grouprecording = states[statepos].grouprecording;
2838 if(state->getBlendMode() == gfxBlendNormal) {
2840 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2841 gfxresult_record_replay(grouprecording, &ops);
2844 grouprecording->destroy(grouprecording);
2846 states[statepos].grouprecording = 0;
2849 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2851 /* alpha = 1: retrieve mask values from alpha layer
2852 alpha = 0: retrieve mask values from luminance */
2853 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2854 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2855 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2856 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2858 infofeature("soft masks");
2860 warnfeature("soft masks from alpha channel",0);
2862 states[statepos].olddevice = this->device;
2863 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2864 gfxdevice_record_init(this->device);
2866 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2868 states[statepos].softmask = 1;
2869 states[statepos].softmask_alpha = alpha;
2872 static inline Guchar div255(int x) {
2873 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2876 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2878 if(c < min) c = min;
2879 if(c > max) c = max;
2883 void GFXOutputDev::clearSoftMask(GfxState *state)
2885 if(!states[statepos].softmask)
2887 states[statepos].softmask = 0;
2888 dbg("clearSoftMask statepos=%d", statepos);
2889 msg("<verbose> clearSoftMask");
2891 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2892 msg("<error> Error in softmask/tgroup ordering");
2896 gfxresult_t*mask = states[statepos].softmaskrecording;
2897 gfxresult_t*below = this->device->finish(this->device);
2898 this->device = states[statepos].olddevice;
2900 /* get outline of all objects below the soft mask */
2901 gfxdevice_t uniondev;
2902 gfxdevice_union_init(&uniondev, 0);
2903 gfxresult_record_replay(below, &uniondev);
2904 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2905 uniondev.finish(&uniondev);
2907 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2909 this->device->startclip(this->device, belowoutline);
2910 gfxresult_record_replay(below, this->device);
2911 gfxresult_record_replay(mask, this->device);
2912 this->device->endclip(this->device);
2913 gfxline_free(belowoutline);
2916 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2917 if(width<=0 || height<=0)
2920 gfxdevice_t belowrender;
2921 gfxdevice_render_init(&belowrender);
2922 if(states[statepos+1].isolated) {
2923 belowrender.setparameter(&belowrender, "fillwhite", "1");
2925 belowrender.setparameter(&belowrender, "antialize", "2");
2926 belowrender.startpage(&belowrender, width, height);
2927 gfxresult_record_replay(below, &belowrender);
2928 belowrender.endpage(&belowrender);
2929 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2930 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2931 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2933 gfxdevice_t maskrender;
2934 gfxdevice_render_init(&maskrender);
2935 maskrender.startpage(&maskrender, width, height);
2936 gfxresult_record_replay(mask, &maskrender);
2937 maskrender.endpage(&maskrender);
2938 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2939 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2941 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2942 msg("<fatal> Internal error in mask drawing");
2947 for(y=0;y<height;y++) {
2948 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2949 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2950 for(x=0;x<width;x++) {
2952 if(states[statepos].softmask_alpha) {
2955 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2958 l2->a = div255(alpha*l2->a);
2960 /* DON'T premultiply alpha- this is done by fillbitmap,
2961 depending on the output device */
2962 //l2->r = div255(alpha*l2->r);
2963 //l2->g = div255(alpha*l2->g);
2964 //l2->b = div255(alpha*l2->b);
2970 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2973 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2974 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2976 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2978 mask->destroy(mask);
2979 below->destroy(below);
2980 maskresult->destroy(maskresult);
2981 belowresult->destroy(belowresult);
2982 states[statepos].softmaskrecording = 0;
2989 delete globalParams;globalParams=0;
2990 Object::memCheck(stderr);