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->config_transparent=0;
277 this->do_interpretType3Chars = gTrue;
279 this->parameters = p;
281 memset(states, 0, sizeof(states));
283 /* configure device */
285 setParameter(p->name, p->value);
290 void GFXOutputDev::setParameter(const char*key, const char*value)
292 if(!strcmp(key,"rawtext")) {
293 this->do_interpretType3Chars = atoi(value)^1;
294 } else if(!strcmp(key,"breakonwarning")) {
295 this->config_break_on_warning = atoi(value);
296 } else if(!strcmp(key,"fontconfig")) {
297 this->config_use_fontconfig = atoi(value);
298 } else if(!strcmp(key,"remapunicode")) {
299 this->config_remapunicode = atoi(value);
300 } else if(!strcmp(p->name,"transparent")) {
301 this->config_transparent = atoi(p->value);
303 msg("<warning> Ignored parameter: %s=%s", key, value);
307 void GFXOutputDev::setDevice(gfxdevice_t*dev)
309 parameter_t*p = this->parameters;
311 /* pass parameters to output device */
315 this->device->setparameter(this->device, p->name, p->value);
321 void GFXOutputDev::setMove(int x,int y)
323 this->user_movex = x;
324 this->user_movey = y;
327 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
329 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
330 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
332 this->user_clipx1 = x1;
333 this->user_clipy1 = y1;
334 this->user_clipx2 = x2;
335 this->user_clipy2 = y2;
338 static char*getFontID(GfxFont*font)
340 Ref*ref = font->getID();
341 GString*gstr = font->getName();
342 char* fname = gstr==0?0:gstr->getCString();
345 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
347 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
352 static char*getFontName(GfxFont*font)
355 GString*gstr = font->getName();
356 char* fname = gstr==0?0:gstr->getCString();
360 sprintf(buf, "UFONT%d", r->num);
361 fontid = strdup(buf);
363 fontid = strdup(fname);
367 char* plus = strchr(fontid, '+');
368 if(plus && plus < &fontid[strlen(fontid)-1]) {
369 fontname = strdup(plus+1);
371 fontname = strdup(fontid);
377 static char mybuf[1024];
378 static char* gfxstate2str(GfxState *state)
382 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
389 if(state->getX1()!=0.0)
390 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
391 if(state->getY1()!=0.0)
392 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
393 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
394 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
395 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
396 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
397 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
398 state->getFillColor()->c[0], state->getFillColor()->c[1]);
399 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
400 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
401 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
402 state->getFillColor()->c[0], state->getFillColor()->c[1],
403 state->getFillColor()->c[2], state->getFillColor()->c[3],
404 state->getFillColor()->c[4], state->getFillColor()->c[5],
405 state->getFillColor()->c[6], state->getFillColor()->c[7]);
406 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
407 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
408 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
409 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
410 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
411 state->getFillRGB(&rgb);
412 if(rgb.r || rgb.g || rgb.b)
413 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
414 state->getStrokeRGB(&rgb);
415 if(rgb.r || rgb.g || rgb.b)
416 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
417 if(state->getFillColorSpace()->getNComps()>1)
418 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
419 if(state->getStrokeColorSpace()->getNComps()>1)
420 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
421 if(state->getFillPattern())
422 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
423 if(state->getStrokePattern())
424 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
426 if(state->getFillOpacity()!=1.0)
427 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
428 if(state->getStrokeOpacity()!=1.0)
429 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
431 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
436 state->getLineDash(&dash, &length, &start);
440 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
441 for(t=0;t<length;t++) {
442 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
444 bufpos+=sprintf(bufpos,"]");
447 if(state->getFlatness()!=1)
448 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
449 if(state->getLineJoin()!=0)
450 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
451 if(state->getLineJoin()!=0)
452 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
453 if(state->getLineJoin()!=0)
454 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
456 if(state->getFont() && getFontID(state->getFont()))
457 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
458 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
459 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
460 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
461 if(state->getCharSpace())
462 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
463 if(state->getWordSpace())
464 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
465 if(state->getHorizScaling()!=1.0)
466 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
467 if(state->getLeading())
468 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
470 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
471 if(state->getRender())
472 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
473 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
474 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
475 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
476 if(state->getLineX())
477 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
478 if(state->getLineY())
479 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
480 bufpos+=sprintf(bufpos," ");
484 static void dumpFontInfo(const char*loglevel, GfxFont*font);
485 static int lastdumps[1024];
486 static int lastdumppos = 0;
491 static void showFontError(GfxFont*font, int nr)
495 for(t=0;t<lastdumppos;t++)
496 if(lastdumps[t] == r->num)
500 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
501 lastdumps[lastdumppos++] = r->num;
503 msg("<warning> The following font caused problems:");
505 msg("<warning> The following font caused problems (substituting):");
507 msg("<warning> The following Type 3 Font will be rendered as graphics:");
508 dumpFontInfo("<warning>", font);
511 static void dumpFontInfo(const char*loglevel, GfxFont*font)
513 char* id = getFontID(font);
514 char* name = getFontName(font);
515 Ref* r=font->getID();
516 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
518 GString*gstr = font->getTag();
520 msg("%s| Tag: %s\n", loglevel, id);
522 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
524 GfxFontType type=font->getType();
526 case fontUnknownType:
527 msg("%s| Type: unknown\n",loglevel);
530 msg("%s| Type: 1\n",loglevel);
533 msg("%s| Type: 1C\n",loglevel);
536 msg("%s| Type: 3\n",loglevel);
539 msg("%s| Type: TrueType\n",loglevel);
542 msg("%s| Type: CIDType0\n",loglevel);
545 msg("%s| Type: CIDType0C\n",loglevel);
548 msg("%s| Type: CIDType2\n",loglevel);
553 GBool embedded = font->getEmbeddedFontID(&embRef);
555 if(font->getEmbeddedFontName()) {
556 embeddedName = font->getEmbeddedFontName()->getCString();
559 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
561 gstr = font->getExtFontFile();
563 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
565 // Get font descriptor flags.
566 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
567 if(font->isSerif()) msg("%s| is serif\n", loglevel);
568 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
569 if(font->isItalic()) msg("%s| is italic\n", loglevel);
570 if(font->isBold()) msg("%s| is bold\n", loglevel);
576 //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");}
577 //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");}
580 void dump_outline(gfxline_t*line)
583 if(line->type == gfx_moveTo) {
584 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
585 } else if(line->type == gfx_lineTo) {
586 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
587 } else if(line->type == gfx_splineTo) {
588 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
594 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
596 int num = path->getNumSubpaths();
599 double lastx=0,lasty=0,posx=0,posy=0;
602 msg("<warning> empty path");
606 gfxdrawer_target_gfxline(&draw);
608 for(t = 0; t < num; t++) {
609 GfxSubpath *subpath = path->getSubpath(t);
610 int subnum = subpath->getNumPoints();
611 double bx=0,by=0,cx=0,cy=0;
613 for(s=0;s<subnum;s++) {
616 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
621 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
622 draw.lineTo(&draw, lastx, lasty);
624 draw.moveTo(&draw, x,y);
629 } else if(subpath->getCurve(s) && cpos==0) {
633 } else if(subpath->getCurve(s) && cpos==1) {
641 draw.lineTo(&draw, x,y);
643 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
650 /* fix non-closed lines */
651 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
652 draw.lineTo(&draw, lastx, lasty);
654 gfxline_t*result = (gfxline_t*)draw.result(&draw);
656 gfxline_optimize(result);
661 GBool GFXOutputDev::useTilingPatternFill()
663 infofeature("tiled patterns");
667 GBool GFXOutputDev::useShadedFills()
669 infofeature("shaded fills");
673 GBool GFXOutputDev::useDrawForm()
675 infofeature("forms");
678 void GFXOutputDev::drawForm(Ref id)
680 msg("<error> drawForm not implemented");
682 GBool GFXOutputDev::needNonText()
686 void GFXOutputDev::endPage()
688 msg("<verbose> endPage");
691 #define STROKE_FILL 1
692 #define STROKE_CLIP 2
693 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
695 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
696 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
697 double miterLimit = state->getMiterLimit();
698 double width = state->getTransformedLineWidth();
701 double opaq = state->getStrokeOpacity();
703 state->getFillRGB(&rgb);
705 state->getStrokeRGB(&rgb);
707 col.r = colToByte(rgb.r);
708 col.g = colToByte(rgb.g);
709 col.b = colToByte(rgb.b);
710 col.a = (unsigned char)(opaq*255);
712 gfx_capType capType = gfx_capRound;
713 if(lineCap == 0) capType = gfx_capButt;
714 else if(lineCap == 1) capType = gfx_capRound;
715 else if(lineCap == 2) capType = gfx_capSquare;
717 gfx_joinType joinType = gfx_joinRound;
718 if(lineJoin == 0) joinType = gfx_joinMiter;
719 else if(lineJoin == 1) joinType = gfx_joinRound;
720 else if(lineJoin == 2) joinType = gfx_joinBevel;
723 double dashphase = 0;
725 state->getLineDash(&ldash, &dashnum, &dashphase);
729 if(dashnum && ldash) {
730 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
734 msg("<trace> %d dashes", dashnum);
735 msg("<trace> | phase: %f", dashphase);
736 for(t=0;t<dashnum;t++) {
738 msg("<trace> | d%-3d: %f", t, ldash[t]);
741 if(getLogLevel() >= LOGLEVEL_TRACE) {
745 line2 = gfxtool_dash_line(line, dash, dashphase);
748 msg("<trace> After dashing:");
751 if(getLogLevel() >= LOGLEVEL_TRACE) {
752 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
754 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
755 lineCap==0?"butt": (lineJoin==1?"round":"square"),
757 col.r,col.g,col.b,col.a
762 if(flags&STROKE_FILL) {
763 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
764 gfxline_t*gfxline = SVPtogfxline(svp);
765 if(flags&STROKE_CLIP) {
766 device->startclip(device, gfxline);
767 states[statepos].clipping++;
769 device->fill(device, gfxline, &col);
774 if(flags&STROKE_CLIP)
775 msg("<error> Stroke&clip not supported at the same time");
776 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
783 gfxcolor_t getFillColor(GfxState * state)
786 double opaq = state->getFillOpacity();
787 state->getFillRGB(&rgb);
789 col.r = colToByte(rgb.r);
790 col.g = colToByte(rgb.g);
791 col.b = colToByte(rgb.b);
792 col.a = (unsigned char)(opaq*255);
796 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
798 gfxcolor_t col = getFillColor(state);
800 if(getLogLevel() >= LOGLEVEL_TRACE) {
801 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
804 device->fill(device, line, &col);
807 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
809 if(getLogLevel() >= LOGLEVEL_TRACE) {
810 msg("<trace> clip\n");
814 device->startclip(device, line);
815 states[statepos].clipping++;
818 void GFXOutputDev::clip(GfxState *state)
820 GfxPath * path = state->getPath();
821 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
822 clipToGfxLine(state, line);
826 void GFXOutputDev::eoClip(GfxState *state)
828 GfxPath * path = state->getPath();
829 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
831 if(getLogLevel() >= LOGLEVEL_TRACE) {
832 msg("<trace> eoclip\n");
836 device->startclip(device, line);
837 states[statepos].clipping++;
840 void GFXOutputDev::clipToStrokePath(GfxState *state)
842 GfxPath * path = state->getPath();
843 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
844 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
848 void GFXOutputDev::endframe()
851 device->endclip(device);
856 void GFXOutputDev::finish()
860 device->endclip(device);
866 GFXOutputDev::~GFXOutputDev()
871 free(this->pages); this->pages = 0;
874 fontlist_t*l = this->fontlist;
876 fontlist_t*next = l->next;
878 gfxfont_free(l->font);
879 free(l->filename);l->filename=0;
885 GBool GFXOutputDev::upsideDown()
889 GBool GFXOutputDev::useDrawChar()
894 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
895 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
897 #define RENDER_FILL 0
898 #define RENDER_STROKE 1
899 #define RENDER_FILLSTROKE 2
900 #define RENDER_INVISIBLE 3
901 #define RENDER_CLIP 4
903 static char tmp_printstr[4096];
904 char* makeStringPrintable(char*str)
906 int len = strlen(str);
921 tmp_printstr[len++] = '.';
922 tmp_printstr[len++] = '.';
923 tmp_printstr[len++] = '.';
925 tmp_printstr[len] = 0;
930 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
932 const char*uniname = 0;
937 /* find out char name from unicode index
938 TODO: should be precomputed
940 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
941 if(nameToUnicodeTab[t].u == u) {
942 uniname = nameToUnicodeTab[t].name;
950 for(t=0;t<font->num_glyphs;t++) {
951 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
952 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
956 /* if we didn't find the character, maybe
957 we can find the capitalized version */
958 for(t=0;t<font->num_glyphs;t++) {
959 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
960 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
968 for(t=0;t<font->num_glyphs;t++) {
969 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
970 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
974 /* if we didn't find the character, maybe
975 we can find the capitalized version */
976 for(t=0;t<font->num_glyphs;t++) {
977 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
978 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
984 /* try to use the unicode id */
985 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
986 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
987 return font->unicode2glyph[u];
989 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
990 FIXME: do this only if we know the font is wingdings?
993 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
994 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
995 return font->unicode2glyph[u];
998 if(charnr>=0 && charnr<font->num_glyphs) {
999 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1007 void GFXOutputDev::beginString(GfxState *state, GString *s)
1009 int render = state->getRender();
1010 if(current_text_stroke) {
1011 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1014 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1015 double m11,m21,m12,m22;
1016 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
1017 state->getFontTransMat(&m11, &m12, &m21, &m22);
1018 m11 *= state->getHorizScaling();
1019 m21 *= state->getHorizScaling();
1021 this->current_font_matrix.m00 = m11 / 1024.0;
1022 this->current_font_matrix.m01 = m12 / 1024.0;
1023 this->current_font_matrix.m10 = -m21 / 1024.0;
1024 this->current_font_matrix.m11 = -m22 / 1024.0;
1025 this->current_font_matrix.tx = 0;
1026 this->current_font_matrix.ty = 0;
1028 gfxmatrix_t m = this->current_font_matrix;
1031 static gfxline_t* mkEmptyGfxShape(double x, double y)
1033 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1034 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1038 static char isValidUnicode(int c)
1040 if(c>=32 && c<0x2fffe)
1045 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1046 double dx, double dy,
1047 double originX, double originY,
1048 CharCode c, int nBytes, Unicode *_u, int uLen)
1050 int render = state->getRender();
1051 // check for invisible text -- this is used by Acrobat Capture
1053 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1057 gfxcolor_t col = getFillColor(state);
1059 Gushort *CIDToGIDMap = 0;
1060 GfxFont*font = state->getFont();
1062 if(font->getType() == fontType3 && do_interpretType3Chars) {
1063 /* type 3 chars are passed as graphics */
1064 msg("<debug> type3 char at %f/%f", x, y);
1074 /* char*fontname = getFontName(font);
1075 if(u<256 && strstr(fontname, "ingdings")) {
1076 // symbols are at 0xe000 in the unicode table
1081 if(font->isCIDFont()) {
1082 GfxCIDFont*cfont = (GfxCIDFont*)font;
1084 if(font->getType() == fontCIDType2)
1085 CIDToGIDMap = cfont->getCIDToGID();
1088 font8 = (Gfx8BitFont*)font;
1089 char**enc=font8->getEncoding();
1093 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);
1096 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);
1102 charid = getGfxCharID(current_gfxfont, c, name, u);
1104 charid = getGfxCharID(current_gfxfont, c, name, -1);
1107 /* multiple unicodes- should usually map to a ligature.
1108 if the ligature doesn't exist, we need to draw
1109 the characters one-by-one. */
1111 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1112 for(t=0;t<uLen;t++) {
1113 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1119 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1120 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1123 //useless- the font has already been passed to the output device
1124 //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) {
1125 // current_gfxfont->glyphs[charid].
1128 gfxmatrix_t m = this->current_font_matrix;
1129 state->transform(x, y, &m.tx, &m.ty);
1130 m.tx += user_movex + clipmovex;
1131 m.ty += user_movey + clipmovey;
1133 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1135 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1139 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1145 static int lastemptychar = 0;
1146 if(charid != lastemptychar)
1147 msg("<warning> Drawing empty character charid=%d u=%d", charid, u);
1148 lastemptychar = charid;
1152 if(render == RENDER_FILL) {
1153 device->drawchar(device, current_gfxfont, charid, &col, &m);
1155 msg("<debug> Drawing glyph %d as shape", charid);
1157 msg("<notice> Some texts will be rendered as shape");
1160 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1161 gfxline_t*tglyph = gfxline_clone(glyph);
1162 gfxline_transform(tglyph, &m);
1163 if((render&3) != RENDER_INVISIBLE) {
1164 gfxline_t*add = gfxline_clone(tglyph);
1165 current_text_stroke = gfxline_append(current_text_stroke, add);
1167 if(render&RENDER_CLIP) {
1168 gfxline_t*add = gfxline_clone(tglyph);
1169 current_text_clip = gfxline_append(current_text_clip, add);
1170 if(!current_text_clip) {
1171 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1174 gfxline_free(tglyph);
1178 void GFXOutputDev::endString(GfxState *state)
1180 int render = state->getRender();
1181 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1183 if(current_text_stroke) {
1184 /* fillstroke and stroke text rendering objects we can process right
1185 now (as there may be texts of other rendering modes in this
1186 text object)- clipping objects have to wait until endTextObject,
1188 device->setparameter(device, "mark","TXT");
1189 if((render&3) == RENDER_FILL) {
1190 fillGfxLine(state, current_text_stroke);
1191 gfxline_free(current_text_stroke);
1192 current_text_stroke = 0;
1193 } else if((render&3) == RENDER_FILLSTROKE) {
1194 fillGfxLine(state, current_text_stroke);
1195 strokeGfxline(state, current_text_stroke,0);
1196 gfxline_free(current_text_stroke);
1197 current_text_stroke = 0;
1198 } else if((render&3) == RENDER_STROKE) {
1199 strokeGfxline(state, current_text_stroke,0);
1200 gfxline_free(current_text_stroke);
1201 current_text_stroke = 0;
1203 device->setparameter(device, "mark","");
1207 void GFXOutputDev::endTextObject(GfxState *state)
1209 int render = state->getRender();
1210 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1212 if(current_text_clip) {
1213 device->setparameter(device, "mark","TXT");
1214 clipToGfxLine(state, current_text_clip);
1215 device->setparameter(device, "mark","");
1216 gfxline_free(current_text_clip);
1217 current_text_clip = 0;
1221 /* the logic seems to be as following:
1222 first, beginType3Char is called, with the charcode and the coordinates.
1223 if this function returns true, it already knew about the char and has now drawn it.
1224 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1225 the all draw operations until endType3Char are part of the char (which in this moment is
1226 at the position first passed to beginType3Char). the char ends with endType3Char.
1228 The drawing operations between beginType3Char and endType3Char are somewhat different to
1229 the normal ones. For example, the fillcolor equals the stroke color.
1232 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1234 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1239 gfxcolor_t col={255,0,0,0};
1240 gfxmatrix_t m = {1,0,0, 0,1,0};
1242 for(t=0;t<uLen;t++) {
1243 device->drawchar(device, 0, u[t], &col, &m);
1246 /* the character itself is going to be passed using the draw functions */
1247 return gFalse; /* gTrue= is_in_cache? */
1250 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1251 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1253 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1254 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1258 void GFXOutputDev::endType3Char(GfxState *state)
1261 msg("<debug> endType3Char");
1264 void GFXOutputDev::startFrame(int width, int height)
1266 if(outer_clip_box) {
1267 device->endclip(device);
1271 device->startpage(device, width, height);
1272 this->width = width;
1273 this->height = height;
1276 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1278 this->currentpage = pageNum;
1280 int rot = doc->getPageRotate(1);
1283 gfxline_t clippath[5];
1285 white.r = white.g = white.b = white.a = 255;
1287 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1288 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1289 Use CropBox, not MediaBox, as page size
1296 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1297 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1299 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1300 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1302 this->clipmovex = -(int)x1;
1303 this->clipmovey = -(int)y1;
1305 /* apply user clip box */
1306 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1307 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1308 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1309 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1310 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1311 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1314 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1316 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);
1318 msg("<verbose> page is rotated %d degrees\n", rot);
1320 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1321 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1322 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1323 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1324 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1325 device->startclip(device, clippath); outer_clip_box = 1;
1326 if(!config_transparent)
1327 device->fill(device, clippath, &white);
1331 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1333 double x1, y1, x2, y2, w;
1334 gfxline_t points[5];
1337 msg("<debug> drawlink\n");
1339 link->getRect(&x1, &y1, &x2, &y2);
1340 cvtUserToDev(x1, y1, &x, &y);
1341 points[0].type = gfx_moveTo;
1342 points[0].x = points[4].x = x + user_movex + clipmovex;
1343 points[0].y = points[4].y = y + user_movey + clipmovey;
1344 points[0].next = &points[1];
1345 cvtUserToDev(x2, y1, &x, &y);
1346 points[1].type = gfx_lineTo;
1347 points[1].x = x + user_movex + clipmovex;
1348 points[1].y = y + user_movey + clipmovey;
1349 points[1].next = &points[2];
1350 cvtUserToDev(x2, y2, &x, &y);
1351 points[2].type = gfx_lineTo;
1352 points[2].x = x + user_movex + clipmovex;
1353 points[2].y = y + user_movey + clipmovey;
1354 points[2].next = &points[3];
1355 cvtUserToDev(x1, y2, &x, &y);
1356 points[3].type = gfx_lineTo;
1357 points[3].x = x + user_movex + clipmovex;
1358 points[3].y = y + user_movey + clipmovey;
1359 points[3].next = &points[4];
1360 cvtUserToDev(x1, y1, &x, &y);
1361 points[4].type = gfx_lineTo;
1362 points[4].x = x + user_movex + clipmovex;
1363 points[4].y = y + user_movey + clipmovey;
1366 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1367 points[0].x, points[0].y,
1368 points[1].x, points[1].y,
1369 points[2].x, points[2].y,
1370 points[3].x, points[3].y);
1372 LinkAction*action=link->getAction();
1375 const char*type = "-?-";
1378 msg("<trace> drawlink action=%d\n", action->getKind());
1379 switch(action->getKind())
1383 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1384 LinkDest *dest=NULL;
1385 if (ha->getDest()==NULL)
1386 dest=catalog->findDest(ha->getNamedDest());
1387 else dest=ha->getDest();
1389 if (dest->isPageRef()){
1390 Ref pageref=dest->getPageRef();
1391 page=catalog->findPage(pageref.num,pageref.gen);
1393 else page=dest->getPageNum();
1394 sprintf(buf, "%d", page);
1401 LinkGoToR*l = (LinkGoToR*)action;
1402 GString*g = l->getFileName();
1404 s = strdup(g->getCString());
1406 /* if the GoToR link has no filename, then
1407 try to find a refernce in the *local*
1409 GString*g = l->getNamedDest();
1411 s = strdup(g->getCString());
1417 LinkNamed*l = (LinkNamed*)action;
1418 GString*name = l->getName();
1420 s = strdup(name->lowerCase()->getCString());
1421 named = name->getCString();
1424 if(strstr(s, "next") || strstr(s, "forward"))
1426 page = currentpage + 1;
1428 else if(strstr(s, "prev") || strstr(s, "back"))
1430 page = currentpage - 1;
1432 else if(strstr(s, "last") || strstr(s, "end"))
1434 if(pages && pagepos>0)
1435 page = pages[pagepos-1];
1437 else if(strstr(s, "first") || strstr(s, "top"))
1445 case actionLaunch: {
1447 LinkLaunch*l = (LinkLaunch*)action;
1448 GString * str = new GString(l->getFileName());
1449 GString * params = l->getParams();
1451 str->append(params);
1452 s = strdup(str->getCString());
1459 LinkURI*l = (LinkURI*)action;
1460 GString*g = l->getURI();
1462 url = g->getCString();
1467 case actionUnknown: {
1469 LinkUnknown*l = (LinkUnknown*)action;
1474 msg("<error> Unknown link type!\n");
1479 if(!s) s = strdup("-?-");
1481 msg("<trace> drawlink s=%s\n", s);
1483 if(!linkinfo && (page || s))
1485 msg("<notice> File contains links");
1493 for(t=1;t<=pagepos;t++) {
1494 if(pages[t]==page) {
1503 sprintf(buf, "page%d", lpage);
1504 device->drawlink(device, points, buf);
1508 device->drawlink(device, points, s);
1511 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1515 void GFXOutputDev::saveState(GfxState *state) {
1516 dbg("saveState");dbgindent+=2;
1518 msg("<trace> saveState\n");
1521 msg("<error> Too many nested states in pdf.");
1525 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1526 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1527 states[statepos].clipping = 0;
1530 void GFXOutputDev::restoreState(GfxState *state) {
1531 dbgindent-=2; dbg("restoreState");
1534 msg("<error> Invalid restoreState");
1537 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1538 states[statepos].clipping?" (end clipping)":"");
1539 if(states[statepos].softmask) {
1540 clearSoftMask(state);
1543 while(states[statepos].clipping) {
1544 device->endclip(device);
1545 states[statepos].clipping--;
1550 char* writeOutStdFont(fontentry* f)
1555 char* tmpFileName = mktmpname(namebuf1);
1557 sprintf(namebuf2, "%s.afm", tmpFileName);
1558 fi = fopen(namebuf2, "wb");
1561 fwrite(f->afm, 1, f->afmlen, fi);
1564 sprintf(namebuf2, "%s.pfb", tmpFileName);
1565 fi = fopen(namebuf2, "wb");
1568 fwrite(f->pfb, 1, f->pfblen, fi);
1571 return strdup(namebuf2);
1574 char* GFXOutputDev::searchFont(const char*name)
1579 msg("<verbose> SearchFont(%s)", name);
1581 /* see if it is a pdf standard font */
1582 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1584 if(!strcmp(name, pdf2t1map[i].pdffont))
1586 if(!pdf2t1map[i].fullfilename) {
1587 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1588 if(!pdf2t1map[i].fullfilename) {
1589 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1591 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1594 return strdup(pdf2t1map[i].fullfilename);
1597 /* else look in all font files */
1598 for(i=0;i<fontnum;i++)
1600 if(strstr(fonts[i].filename, name)) {
1601 return strdup(fonts[i].filename);
1607 void GFXOutputDev::updateLineWidth(GfxState *state)
1609 double width = state->getTransformedLineWidth();
1610 //swfoutput_setlinewidth(&device, width);
1613 void GFXOutputDev::updateLineCap(GfxState *state)
1615 int c = state->getLineCap();
1618 void GFXOutputDev::updateLineJoin(GfxState *state)
1620 int j = state->getLineJoin();
1623 void GFXOutputDev::updateFillColor(GfxState *state)
1626 double opaq = state->getFillOpacity();
1627 state->getFillRGB(&rgb);
1629 void GFXOutputDev::updateFillOpacity(GfxState *state)
1632 double opaq = state->getFillOpacity();
1633 state->getFillRGB(&rgb);
1634 dbg("update fillopaq %f", opaq);
1636 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1638 double opaq = state->getFillOpacity();
1639 dbg("update strokeopaq %f", opaq);
1641 void GFXOutputDev::updateFillOverprint(GfxState *state)
1643 double opaq = state->getFillOverprint();
1644 dbg("update filloverprint %f", opaq);
1646 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1648 double opaq = state->getStrokeOverprint();
1649 dbg("update strokeoverprint %f", opaq);
1651 void GFXOutputDev::updateTransfer(GfxState *state)
1653 dbg("update transfer");
1657 void GFXOutputDev::updateStrokeColor(GfxState *state)
1660 double opaq = state->getStrokeOpacity();
1661 state->getStrokeRGB(&rgb);
1664 void FoFiWrite(void *stream, char *data, int len)
1666 int ret = fwrite(data, len, 1, (FILE*)stream);
1669 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1671 char*tmpFileName = NULL;
1677 Object refObj, strObj;
1679 tmpFileName = mktmpname(namebuf);
1682 ret = font->getEmbeddedFontID(&embRef);
1684 msg("<verbose> Didn't get embedded font id");
1685 /* not embedded- the caller should now search the font
1686 directories for this font */
1690 f = fopen(tmpFileName, "wb");
1692 msg("<error> Couldn't create temporary Type 1 font file");
1696 /*if(font->isCIDFont()) {
1697 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1698 GString c = cidFont->getCollection();
1699 msg("<notice> Collection: %s", c.getCString());
1702 //if (font->getType() == fontType1C) {
1703 if (0) { //font->getType() == fontType1C) {
1704 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1706 msg("<error> Couldn't read embedded font file");
1709 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1711 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1712 //cvt->convertToCIDType0("test", f);
1713 //cvt->convertToType0("test", f);
1716 } else if(font->getType() == fontTrueType) {
1717 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1718 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1720 msg("<error> Couldn't read embedded font file");
1723 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1724 cvt->writeTTF(FoFiWrite, f);
1728 font->getEmbeddedFontID(&embRef);
1729 refObj.initRef(embRef.num, embRef.gen);
1730 refObj.fetch(ref, &strObj);
1732 strObj.streamReset();
1737 f4[t] = strObj.streamGetChar();
1738 f4c[t] = (char)f4[t];
1743 if(!strncmp(f4c, "true", 4)) {
1744 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1745 Change this on the fly */
1746 f4[0] = f4[2] = f4[3] = 0;
1754 while ((c = strObj.streamGetChar()) != EOF) {
1758 strObj.streamClose();
1763 return strdup(tmpFileName);
1766 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1768 char*name = getFontName(gfxFont);
1772 if(!this->config_use_fontconfig)
1775 #ifdef HAVE_FONTCONFIG
1776 FcPattern *pattern, *match;
1780 static int fcinitcalled = false;
1782 msg("<debug> searchForSuitableFont(%s)", name);
1784 // call init ony once
1785 if (!fcinitcalled) {
1786 msg("<debug> Initializing FontConfig...");
1787 fcinitcalled = true;
1789 msg("<debug> FontConfig Initialization failed. Disabling.");
1790 config_use_fontconfig = 0;
1793 msg("<debug> ...initialized FontConfig");
1796 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1797 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1798 if (gfxFont->isItalic()) // check for italic
1799 msg("<debug> FontConfig: Adding Italic Slant");
1800 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1801 if (gfxFont->isBold()) // check for bold
1802 msg("<debug> FontConfig: Adding Bold Weight");
1803 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1805 msg("<debug> FontConfig: Try to match...");
1806 // configure and match using the original font name
1807 FcConfigSubstitute(0, pattern, FcMatchPattern);
1808 FcDefaultSubstitute(pattern);
1809 match = FcFontMatch(0, pattern, &result);
1811 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1812 msg("<debug> FontConfig: family=%s", (char*)v);
1813 // if we get an exact match
1814 if (strcmp((char *)v, name) == 0) {
1815 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1816 filename = strdup((char*)v); // mem leak
1817 char *nfn = strrchr(filename, '/');
1818 if(nfn) fontname = strdup(nfn+1);
1819 else fontname = filename;
1821 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1823 // initialize patterns
1824 FcPatternDestroy(pattern);
1825 FcPatternDestroy(match);
1827 // now match against serif etc.
1828 if (gfxFont->isSerif()) {
1829 msg("<debug> FontConfig: Create Serif Family Pattern");
1830 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1831 } else if (gfxFont->isFixedWidth()) {
1832 msg("<debug> FontConfig: Create Monospace Family Pattern");
1833 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1835 msg("<debug> FontConfig: Create Sans Family Pattern");
1836 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1840 if (gfxFont->isItalic()) {
1841 msg("<debug> FontConfig: Adding Italic Slant");
1842 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1845 if (gfxFont->isBold()) {
1846 msg("<debug> FontConfig: Adding Bold Weight");
1847 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1850 msg("<debug> FontConfig: Try to match... (2)");
1851 // configure and match using serif etc
1852 FcConfigSubstitute (0, pattern, FcMatchPattern);
1853 FcDefaultSubstitute (pattern);
1854 match = FcFontMatch (0, pattern, &result);
1856 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1857 filename = strdup((char*)v); // mem leak
1858 char *nfn = strrchr(filename, '/');
1859 if(nfn) fontname = strdup(nfn+1);
1860 else fontname = filename;
1862 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1866 //printf("FONTCONFIG: pattern");
1867 //FcPatternPrint(pattern);
1868 //printf("FONTCONFIG: match");
1869 //FcPatternPrint(match);
1871 FcPatternDestroy(pattern);
1872 FcPatternDestroy(match);
1874 pdfswf_addfont(filename);
1881 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1883 const char*fontname = 0, *filename = 0;
1884 msg("<notice> substituteFont(%s)", oldname);
1886 if(!(fontname = searchForSuitableFont(gfxFont))) {
1887 fontname = "Times-Roman";
1889 filename = searchFont(fontname);
1891 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1895 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1896 msg("<fatal> Too many fonts in file.");
1900 substitutesource[substitutepos] = strdup(oldname); //mem leak
1901 substitutetarget[substitutepos] = fontname;
1902 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1905 return strdup(filename); //mem leak
1908 void unlinkfont(char* filename)
1915 if(!strncmp(&filename[l-4],".afm",4)) {
1916 memcpy(&filename[l-4],".pfb",4);
1918 memcpy(&filename[l-4],".pfa",4);
1920 memcpy(&filename[l-4],".afm",4);
1923 if(!strncmp(&filename[l-4],".pfa",4)) {
1924 memcpy(&filename[l-4],".afm",4);
1926 memcpy(&filename[l-4],".pfa",4);
1929 if(!strncmp(&filename[l-4],".pfb",4)) {
1930 memcpy(&filename[l-4],".afm",4);
1932 memcpy(&filename[l-4],".pfb",4);
1937 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1943 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu)
1946 fontlist_t*last=0,*l = this->fontlist;
1949 msg("<error> Internal Error: FontID is null");
1951 /* TODO: should this be part of the state? */
1954 if(!strcmp(l->font->id, id)) {
1955 current_gfxfont = l->font;
1957 device->addfont(device, current_gfxfont);
1962 if(!filename) return 0;
1964 /* A font size of e.g. 9 means the font will be scaled down by
1965 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1966 we have to divide 0.05 by (fontsize/1024)
1968 double quality = (1024 * 0.05) / maxSize;
1970 msg("<verbose> Loading %s...", filename);
1971 font = gfxfont_load(id, filename, 0, quality);
1973 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1976 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1978 if(this->config_remapunicode && ctu) {
1980 for(c=0;c<font->num_glyphs;c++) {
1982 int uLen = ctu->mapToUnicode(c, u, 8);
1983 if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0]))
1984 font->glyphs[c].unicode = u[0];
1990 l->filename = strdup(filename);
1992 current_gfxfont = l->font;
1998 device->addfont(device, current_gfxfont);
2002 void GFXOutputDev::updateFont(GfxState *state)
2004 GfxFont*gfxFont = state->getFont();
2010 char * fontid = getFontID(gfxFont);
2011 char * fontname = getFontName(gfxFont);
2013 double maxSize = 1.0;
2016 maxSize = this->info->getMaximumFontSize(fontid);
2020 /* first, look if we substituted this font before-
2021 this way, we don't initialize the T1 Fonts
2023 for(t=0;t<substitutepos;t++) {
2024 if(!strcmp(fontid, substitutesource[t])) {
2025 free(fontid);fontid=0;
2026 fontid = strdup(substitutetarget[t]);
2031 /* second, see if this is a font which was used before-
2032 if so, we are done */
2033 if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2038 /* if(swfoutput_queryfont(&device, fontid))
2039 swfoutput_setfont(&device, fontid, 0);
2041 msg("<debug> updateFont(%s) [cached]", fontid);
2045 // look for Type 3 font
2046 if (gfxFont->getType() == fontType3) {
2048 type3Warning = gTrue;
2049 showFontError(gfxFont, 2);
2056 /* now either load the font, or find a substitution */
2059 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2064 (gfxFont->getType() == fontType1 ||
2065 gfxFont->getType() == fontType1C ||
2066 gfxFont->getType() == fontCIDType0C ||
2067 gfxFont->getType() == fontTrueType ||
2068 gfxFont->getType() == fontCIDType2
2071 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2072 if(!fileName) showFontError(gfxFont,0);
2075 fileName = searchFont(fontname);
2076 if(!fileName) showFontError(gfxFont,0);
2079 char * fontname = getFontName(gfxFont);
2080 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2083 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2085 msg("<warning> Try specifying one or more font directories");
2087 fileName = substituteFont(gfxFont, fontid);
2090 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2091 msg("<notice> Font is now %s (%s)", fontid, fileName);
2095 msg("<error> Couldn't set font %s\n", fontid);
2101 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2102 dumpFontInfo("<verbose>", gfxFont);
2104 //swfoutput_setfont(&device, fontid, fileName);
2106 if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2107 setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU());
2111 unlinkfont(fileName);
2121 #define SQR(x) ((x)*(x))
2123 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2125 if((newwidth<2 || newheight<2) ||
2126 (width<=newwidth || height<=newheight))
2128 unsigned char*newdata;
2130 newdata= (unsigned char*)malloc(newwidth*newheight);
2132 double fx = (double)(width)/newwidth;
2133 double fy = (double)(height)/newheight;
2135 int blocksize = (int)(8192/(fx*fy));
2136 int r = 8192*256/palettesize;
2137 for(x=0;x<newwidth;x++) {
2138 double ex = px + fx;
2139 int fromx = (int)px;
2141 int xweight1 = (int)(((fromx+1)-px)*256);
2142 int xweight2 = (int)((ex-tox)*256);
2144 for(y=0;y<newheight;y++) {
2145 double ey = py + fy;
2146 int fromy = (int)py;
2148 int yweight1 = (int)(((fromy+1)-py)*256);
2149 int yweight2 = (int)((ey-toy)*256);
2152 for(xx=fromx;xx<=tox;xx++)
2153 for(yy=fromy;yy<=toy;yy++) {
2154 int b = 1-data[width*yy+xx];
2156 if(xx==fromx) weight = (weight*xweight1)/256;
2157 if(xx==tox) weight = (weight*xweight2)/256;
2158 if(yy==fromy) weight = (weight*yweight1)/256;
2159 if(yy==toy) weight = (weight*yweight2)/256;
2162 //if(a) a=(palettesize-1)*r/blocksize;
2163 newdata[y*newwidth+x] = (a*blocksize)/r;
2171 #define IMAGE_TYPE_JPEG 0
2172 #define IMAGE_TYPE_LOSSLESS 1
2174 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2175 double x1,double y1,
2176 double x2,double y2,
2177 double x3,double y3,
2178 double x4,double y4, int type)
2180 gfxcolor_t*newpic=0;
2182 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2183 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2185 gfxline_t p1,p2,p3,p4,p5;
2186 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2187 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2188 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2189 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2190 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2192 {p1.x = (int)(p1.x*20)/20.0;
2193 p1.y = (int)(p1.y*20)/20.0;
2194 p2.x = (int)(p2.x*20)/20.0;
2195 p2.y = (int)(p2.y*20)/20.0;
2196 p3.x = (int)(p3.x*20)/20.0;
2197 p3.y = (int)(p3.y*20)/20.0;
2198 p4.x = (int)(p4.x*20)/20.0;
2199 p4.y = (int)(p4.y*20)/20.0;
2200 p5.x = (int)(p5.x*20)/20.0;
2201 p5.y = (int)(p5.y*20)/20.0;
2208 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2209 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2214 img.data = (gfxcolor_t*)data;
2218 if(type == IMAGE_TYPE_JPEG)
2219 /* TODO: pass image_dpi to device instead */
2220 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2222 dev->fillbitmap(dev, &p1, &img, &m, 0);
2225 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2226 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2228 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2231 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2232 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2234 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2238 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2239 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2240 GBool inlineImg, int mask, int*maskColors,
2241 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2243 double x1,y1,x2,y2,x3,y3,x4,y4;
2244 ImageStream *imgStr;
2249 unsigned char* maskbitmap = 0;
2252 ncomps = colorMap->getNumPixelComps();
2253 bits = colorMap->getBits();
2258 unsigned char buf[8];
2259 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2261 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2262 imgMaskStr->reset();
2263 unsigned char pal[256];
2264 int n = 1 << colorMap->getBits();
2269 maskColorMap->getGray(pixBuf, &gray);
2270 pal[t] = colToByte(gray);
2272 for (y = 0; y < maskHeight; y++) {
2273 for (x = 0; x < maskWidth; x++) {
2274 imgMaskStr->getPixel(buf);
2275 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2280 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2281 imgMaskStr->reset();
2282 for (y = 0; y < maskHeight; y++) {
2283 for (x = 0; x < maskWidth; x++) {
2284 imgMaskStr->getPixel(buf);
2286 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2294 imgStr = new ImageStream(str, width, ncomps,bits);
2297 if(!width || !height || (height<=1 && width<=1))
2299 msg("<verbose> Ignoring %d by %d image", width, height);
2300 unsigned char buf[8];
2302 for (y = 0; y < height; ++y)
2303 for (x = 0; x < width; ++x) {
2304 imgStr->getPixel(buf);
2312 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2313 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2314 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2315 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2317 if(!pbminfo && !(str->getKind()==strDCT)) {
2319 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2323 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2325 if(!jpeginfo && (str->getKind()==strDCT)) {
2326 msg("<notice> file contains jpeg pictures");
2332 unsigned char buf[8];
2334 unsigned char*pic = new unsigned char[width*height];
2335 gfxcolor_t pal[256];
2337 state->getFillRGB(&rgb);
2339 memset(pal,255,sizeof(pal));
2340 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2341 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2342 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2343 pal[0].a = 255; pal[1].a = 0;
2346 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2347 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2348 for (y = 0; y < height; ++y)
2349 for (x = 0; x < width; ++x)
2351 imgStr->getPixel(buf);
2354 pic[width*y+x] = buf[0];
2357 /* the size of the drawn image is added to the identifier
2358 as the same image may require different bitmaps if displayed
2359 at different sizes (due to antialiasing): */
2362 unsigned char*pic2 = 0;
2365 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2374 height = realheight;
2378 /* make a black/white palette */
2380 float r = 255/(numpalette-1);
2382 for(t=0;t<numpalette;t++) {
2383 pal[t].r = colToByte(rgb.r);
2384 pal[t].g = colToByte(rgb.g);
2385 pal[t].b = colToByte(rgb.b);
2386 pal[t].a = (unsigned char)(t*r);
2390 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2391 for (y = 0; y < height; ++y) {
2392 for (x = 0; x < width; ++x) {
2393 pic2[width*y+x] = pal[pic[y*width+x]];
2396 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2400 if(maskbitmap) free(maskbitmap);
2406 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2407 gfxcolor_t*pic=new gfxcolor_t[width*height];
2408 for (y = 0; y < height; ++y) {
2409 for (x = 0; x < width; ++x) {
2410 imgStr->getPixel(pixBuf);
2411 colorMap->getRGB(pixBuf, &rgb);
2412 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2413 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2414 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2415 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2417 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2421 if(str->getKind()==strDCT)
2422 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2424 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2427 if(maskbitmap) free(maskbitmap);
2430 gfxcolor_t*pic=new gfxcolor_t[width*height];
2431 gfxcolor_t pal[256];
2432 int n = 1 << colorMap->getBits();
2434 for(t=0;t<256;t++) {
2436 colorMap->getRGB(pixBuf, &rgb);
2438 {/*if(maskColors && *maskColors==t) {
2439 msg("<notice> Color %d is transparent", t);
2440 if (imgData->maskColors) {
2442 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2443 if (pix[i] < imgData->maskColors[2*i] ||
2444 pix[i] > imgData->maskColors[2*i+1]) {
2459 pal[t].r = (unsigned char)(colToByte(rgb.r));
2460 pal[t].g = (unsigned char)(colToByte(rgb.g));
2461 pal[t].b = (unsigned char)(colToByte(rgb.b));
2462 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2465 for (y = 0; y < height; ++y) {
2466 for (x = 0; x < width; ++x) {
2467 imgStr->getPixel(pixBuf);
2468 pic[width*y+x] = pal[pixBuf[0]];
2470 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2474 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2478 if(maskbitmap) free(maskbitmap);
2483 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2484 int width, int height, GBool invert,
2487 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2488 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2489 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2492 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2493 int width, int height, GfxImageColorMap *colorMap,
2494 int *maskColors, GBool inlineImg)
2496 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2497 colorMap?"colorMap":"no colorMap",
2498 maskColors?"maskColors":"no maskColors",
2500 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2501 colorMap?"colorMap":"no colorMap",
2502 maskColors?"maskColors":"no maskColors",
2505 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2506 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2507 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2510 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2511 int width, int height,
2512 GfxImageColorMap *colorMap,
2513 Stream *maskStr, int maskWidth, int maskHeight,
2516 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2517 colorMap?"colorMap":"no colorMap",
2518 maskWidth, maskHeight);
2519 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2520 colorMap?"colorMap":"no colorMap",
2521 maskWidth, maskHeight);
2523 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2524 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2525 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2528 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2529 int width, int height,
2530 GfxImageColorMap *colorMap,
2532 int maskWidth, int maskHeight,
2533 GfxImageColorMap *maskColorMap)
2535 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2536 colorMap?"colorMap":"no colorMap",
2537 maskWidth, maskHeight);
2538 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2539 colorMap?"colorMap":"no colorMap",
2540 maskWidth, maskHeight);
2542 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2543 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2544 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2547 void GFXOutputDev::stroke(GfxState *state)
2551 GfxPath * path = state->getPath();
2552 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2553 strokeGfxline(state, line, 0);
2557 void GFXOutputDev::fill(GfxState *state)
2559 gfxcolor_t col = getFillColor(state);
2560 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2562 GfxPath * path = state->getPath();
2563 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2564 fillGfxLine(state, line);
2568 void GFXOutputDev::eoFill(GfxState *state)
2570 gfxcolor_t col = getFillColor(state);
2571 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2573 GfxPath * path = state->getPath();
2574 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2575 fillGfxLine(state, line);
2580 static const char* dirseparator()
2589 void addGlobalFont(const char*filename)
2592 memset(&f, 0, sizeof(fontfile_t));
2593 f.filename = filename;
2594 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2595 msg("<verbose> Adding font \"%s\".", filename);
2596 fonts[fontnum++] = f;
2598 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2602 void addGlobalLanguageDir(const char*dir)
2605 globalParams = new GlobalParams((char*)"");
2607 msg("<notice> Adding %s to language pack directories", dir);
2611 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2612 strcpy(config_file, dir);
2613 strcat(config_file, dirseparator());
2614 strcat(config_file, "add-to-xpdfrc");
2616 fi = fopen(config_file, "rb");
2618 msg("<error> Could not open %s", config_file);
2621 globalParams->parseFile(new GString(config_file), fi);
2625 void addGlobalFontDir(const char*dirname)
2627 #ifdef HAVE_DIRENT_H
2628 msg("<notice> Adding %s to font directories", dirname);
2629 lastfontdir = strdup(dirname);
2630 DIR*dir = opendir(dirname);
2632 msg("<warning> Couldn't open directory %s\n", dirname);
2637 ent = readdir (dir);
2641 char*name = ent->d_name;
2647 if(!strncasecmp(&name[l-4], ".pfa", 4))
2649 if(!strncasecmp(&name[l-4], ".pfb", 4))
2651 if(!strncasecmp(&name[l-4], ".ttf", 4))
2655 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2656 strcpy(fontname, dirname);
2657 strcat(fontname, dirseparator());
2658 strcat(fontname, name);
2659 addGlobalFont(fontname);
2664 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2668 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2674 this->pagebuflen = 1024;
2675 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2676 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2678 while(pdfpage >= this->pagebuflen)
2680 int oldlen = this->pagebuflen;
2681 this->pagebuflen+=1024;
2682 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2683 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2686 this->pages[pdfpage] = outputpage;
2687 if(pdfpage>this->pagepos)
2688 this->pagepos = pdfpage;
2694 double width,height;
2697 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2699 double xMin, yMin, xMax, yMax, x, y;
2700 double tx, ty, w, h;
2701 // transform the bbox
2702 state->transform(bbox[0], bbox[1], &x, &y);
2705 state->transform(bbox[0], bbox[3], &x, &y);
2708 } else if (x > xMax) {
2713 } else if (y > yMax) {
2716 state->transform(bbox[2], bbox[1], &x, &y);
2719 } else if (x > xMax) {
2724 } else if (y > yMax) {
2727 state->transform(bbox[2], bbox[3], &x, &y);
2730 } else if (x > xMax) {
2735 } else if (y > yMax) {
2738 tx = (int)floor(xMin);
2741 } else if (tx > width) {
2744 ty = (int)floor(yMin);
2747 } else if (ty > height) {
2750 w = (int)ceil(xMax) - tx + 1;
2751 if (tx + w > width) {
2757 h = (int)ceil(yMax) - ty + 1;
2758 if (ty + h > height) {
2772 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2773 GfxColorSpace *blendingColorSpace,
2774 GBool isolated, GBool knockout,
2777 const char*colormodename = "";
2778 BBox rect = mkBBox(state, bbox, this->width, this->height);
2780 if(blendingColorSpace) {
2781 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2783 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);
2784 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2785 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);
2787 states[statepos].createsoftmask |= forSoftMask;
2788 states[statepos].transparencygroup = !forSoftMask;
2789 states[statepos].isolated = isolated;
2791 states[statepos].olddevice = this->device;
2792 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2794 gfxdevice_record_init(this->device);
2796 /*if(!forSoftMask) { ////???
2797 state->setFillOpacity(0.0);
2802 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2805 dbg("endTransparencyGroup");
2806 msg("<verbose> endTransparencyGroup");
2808 gfxdevice_t*r = this->device;
2810 this->device = states[statepos].olddevice;
2812 if(states[statepos].createsoftmask) {
2813 states[statepos-1].softmaskrecording = r->finish(r);
2815 states[statepos-1].grouprecording = r->finish(r);
2818 states[statepos].createsoftmask = 0;
2819 states[statepos].transparencygroup = 0;
2823 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2825 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2826 "colordodge","colorburn","hardlight","softlight","difference",
2827 "exclusion","hue","saturation","color","luminosity"};
2829 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2830 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2832 if(state->getBlendMode() == gfxBlendNormal)
2833 infofeature("transparency groups");
2836 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2837 warnfeature(buffer, 0);
2840 gfxresult_t*grouprecording = states[statepos].grouprecording;
2842 if(state->getBlendMode() == gfxBlendNormal) {
2844 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2845 gfxresult_record_replay(grouprecording, &ops);
2848 grouprecording->destroy(grouprecording);
2850 states[statepos].grouprecording = 0;
2853 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2855 /* alpha = 1: retrieve mask values from alpha layer
2856 alpha = 0: retrieve mask values from luminance */
2857 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2858 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2859 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2860 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2862 infofeature("soft masks");
2864 warnfeature("soft masks from alpha channel",0);
2866 states[statepos].olddevice = this->device;
2867 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2868 gfxdevice_record_init(this->device);
2870 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2872 states[statepos].softmask = 1;
2873 states[statepos].softmask_alpha = alpha;
2876 static inline Guchar div255(int x) {
2877 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2880 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2882 if(c < min) c = min;
2883 if(c > max) c = max;
2887 void GFXOutputDev::clearSoftMask(GfxState *state)
2889 if(!states[statepos].softmask)
2891 states[statepos].softmask = 0;
2892 dbg("clearSoftMask statepos=%d", statepos);
2893 msg("<verbose> clearSoftMask");
2895 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2896 msg("<error> Error in softmask/tgroup ordering");
2900 gfxresult_t*mask = states[statepos].softmaskrecording;
2901 gfxresult_t*below = this->device->finish(this->device);
2902 this->device = states[statepos].olddevice;
2904 /* get outline of all objects below the soft mask */
2905 gfxdevice_t uniondev;
2906 gfxdevice_union_init(&uniondev, 0);
2907 gfxresult_record_replay(below, &uniondev);
2908 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2909 uniondev.finish(&uniondev);
2911 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2913 this->device->startclip(this->device, belowoutline);
2914 gfxresult_record_replay(below, this->device);
2915 gfxresult_record_replay(mask, this->device);
2916 this->device->endclip(this->device);
2917 gfxline_free(belowoutline);
2920 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2921 if(width<=0 || height<=0)
2924 gfxdevice_t belowrender;
2925 gfxdevice_render_init(&belowrender);
2926 if(states[statepos+1].isolated) {
2927 belowrender.setparameter(&belowrender, "fillwhite", "1");
2929 belowrender.setparameter(&belowrender, "antialize", "2");
2930 belowrender.startpage(&belowrender, width, height);
2931 gfxresult_record_replay(below, &belowrender);
2932 belowrender.endpage(&belowrender);
2933 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2934 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2935 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2937 gfxdevice_t maskrender;
2938 gfxdevice_render_init(&maskrender);
2939 maskrender.startpage(&maskrender, width, height);
2940 gfxresult_record_replay(mask, &maskrender);
2941 maskrender.endpage(&maskrender);
2942 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2943 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2945 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2946 msg("<fatal> Internal error in mask drawing");
2951 for(y=0;y<height;y++) {
2952 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2953 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2954 for(x=0;x<width;x++) {
2956 if(states[statepos].softmask_alpha) {
2959 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2962 l2->a = div255(alpha*l2->a);
2964 /* DON'T premultiply alpha- this is done by fillbitmap,
2965 depending on the output device */
2966 //l2->r = div255(alpha*l2->r);
2967 //l2->g = div255(alpha*l2->g);
2968 //l2->b = div255(alpha*l2->b);
2974 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2977 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2978 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2980 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2982 mask->destroy(mask);
2983 below->destroy(below);
2984 maskresult->destroy(maskresult);
2985 belowresult->destroy(belowresult);
2986 states[statepos].softmaskrecording = 0;
2993 delete globalParams;globalParams=0;
2994 Object::memCheck(stderr);