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"
76 typedef struct _fontfile
83 static fontfile_t fonts[2048];
84 static int fontnum = 0;
88 static char* lastfontdir = 0;
94 {"Times-Roman", "n021003l"},
95 {"Times-Italic", "n021023l"},
96 {"Times-Bold", "n021004l"},
97 {"Times-BoldItalic", "n021024l"},
98 {"Helvetica", "n019003l"},
99 {"Helvetica-Oblique", "n019023l"},
100 {"Helvetica-Bold", "n019004l"},
101 {"Helvetica-BoldOblique", "n019024l"},
102 {"Courier", "n022003l"},
103 {"Courier-Oblique", "n022023l"},
104 {"Courier-Bold", "n022004l"},
105 {"Courier-BoldOblique", "n022024l"},
106 {"Symbol", "s050000l"},
107 {"ZapfDingbats", "d050000l"}};
109 static int verbose = 0;
110 static int dbgindent = 0;
111 static void dbg(char*format, ...)
118 va_start(arglist, format);
119 vsprintf(buf, format, arglist);
122 while(l && buf[l-1]=='\n') {
127 int indent = dbgindent;
137 typedef struct _feature
140 struct _feature*next;
142 feature_t*featurewarnings = 0;
144 static void showfeature(char*feature,char fully, char warn)
146 feature_t*f = featurewarnings;
148 if(!strcmp(feature, f->string))
152 f = (feature_t*)malloc(sizeof(feature_t));
153 f->string = strdup(feature);
154 f->next = featurewarnings;
157 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
159 msg("<info> File contains %s",feature);
162 static void warnfeature(char*feature,char fully)
164 showfeature(feature,fully,1);
166 static void infofeature(char*feature)
168 showfeature(feature,0,0);
171 GFXOutputState::GFXOutputState() {
173 this->textRender = 0;
174 this->createsoftmask = 0;
175 this->transparencygroup = 0;
177 this->grouprecording = 0;
180 GBool GFXOutputDev::interpretType3Chars()
185 typedef struct _drawnchar
203 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
204 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
209 free(chars);chars = 0;
216 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
220 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
223 chars[num_chars].x = x;
224 chars[num_chars].y = y;
225 chars[num_chars].color = color;
226 chars[num_chars].charid = charid;
230 static char*getFontID(GfxFont*font);
232 GFXOutputDev::GFXOutputDev(parameter_t*p)
235 this->textmodeinfo = 0;
238 this->type3active = 0;
241 this->substitutepos = 0;
242 this->type3Warning = 0;
243 this->user_movex = 0;
244 this->user_movey = 0;
247 this->user_clipx1 = 0;
248 this->user_clipy1 = 0;
249 this->user_clipx2 = 0;
250 this->user_clipy2 = 0;
251 this->current_text_stroke = 0;
252 this->current_text_clip = 0;
254 this->outer_clip_box = 0;
256 this->pagebuflen = 0;
259 this->forceType0Fonts=1;
260 this->config_use_fontconfig=1;
262 this->parameters = p;
264 /* configure device */
266 if(!strcmp(p->name,"forceType0Fonts")) {
267 this->forceType0Fonts = atoi(p->value);
268 } else if(!strcmp(p->name,"fontconfig")) {
269 this->config_use_fontconfig = atoi(p->value);
275 void GFXOutputDev::setDevice(gfxdevice_t*dev)
277 parameter_t*p = this->parameters;
279 /* TODO: get rid of this */
283 this->device->setparameter(this->device, p->name, p->value);
289 void GFXOutputDev::setMove(int x,int y)
291 this->user_movex = x;
292 this->user_movey = y;
295 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
297 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
298 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
300 this->user_clipx1 = x1;
301 this->user_clipy1 = y1;
302 this->user_clipx2 = x2;
303 this->user_clipy2 = y2;
306 static char*getFontID(GfxFont*font)
308 Ref*ref = font->getID();
309 GString*gstr = font->getName();
310 char* fname = gstr==0?0:gstr->getCString();
313 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
315 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
320 static char*getFontName(GfxFont*font)
323 GString*gstr = font->getName();
324 char* fname = gstr==0?0:gstr->getCString();
328 sprintf(buf, "UFONT%d", r->num);
329 fontid = strdup(buf);
331 fontid = strdup(fname);
335 char* plus = strchr(fontid, '+');
336 if(plus && plus < &fontid[strlen(fontid)-1]) {
337 fontname = strdup(plus+1);
339 fontname = strdup(fontid);
345 static char mybuf[1024];
346 static char* gfxstate2str(GfxState *state)
350 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
357 if(state->getX1()!=0.0)
358 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
359 if(state->getY1()!=0.0)
360 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
361 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
362 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
363 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
364 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
365 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
366 state->getFillColor()->c[0], state->getFillColor()->c[1]);
367 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
368 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
369 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
370 state->getFillColor()->c[0], state->getFillColor()->c[1],
371 state->getFillColor()->c[2], state->getFillColor()->c[3],
372 state->getFillColor()->c[4], state->getFillColor()->c[5],
373 state->getFillColor()->c[6], state->getFillColor()->c[7]);
374 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
375 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
376 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
377 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
378 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
379 state->getFillRGB(&rgb);
380 if(rgb.r || rgb.g || rgb.b)
381 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
382 state->getStrokeRGB(&rgb);
383 if(rgb.r || rgb.g || rgb.b)
384 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
385 if(state->getFillColorSpace()->getNComps()>1)
386 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
387 if(state->getStrokeColorSpace()->getNComps()>1)
388 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
389 if(state->getFillPattern())
390 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
391 if(state->getStrokePattern())
392 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
394 if(state->getFillOpacity()!=1.0)
395 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
396 if(state->getStrokeOpacity()!=1.0)
397 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
399 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
404 state->getLineDash(&dash, &length, &start);
408 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
409 for(t=0;t<length;t++) {
410 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
412 bufpos+=sprintf(bufpos,"]");
415 if(state->getFlatness()!=1)
416 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
417 if(state->getLineJoin()!=0)
418 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
419 if(state->getLineJoin()!=0)
420 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
421 if(state->getLineJoin()!=0)
422 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
424 if(state->getFont() && getFontID(state->getFont()))
425 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
426 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
427 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
428 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
429 if(state->getCharSpace())
430 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
431 if(state->getWordSpace())
432 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
433 if(state->getHorizScaling()!=1.0)
434 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
435 if(state->getLeading())
436 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
438 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
439 if(state->getRender())
440 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
441 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
442 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
443 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
444 if(state->getLineX())
445 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
446 if(state->getLineY())
447 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
448 bufpos+=sprintf(bufpos," ");
452 static void dumpFontInfo(char*loglevel, GfxFont*font);
453 static int lastdumps[1024];
454 static int lastdumppos = 0;
459 static void showFontError(GfxFont*font, int nr)
463 for(t=0;t<lastdumppos;t++)
464 if(lastdumps[t] == r->num)
468 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
469 lastdumps[lastdumppos++] = r->num;
471 msg("<warning> The following font caused problems:");
473 msg("<warning> The following font caused problems (substituting):");
475 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
476 dumpFontInfo("<warning>", font);
479 static void dumpFontInfo(char*loglevel, GfxFont*font)
481 char* id = getFontID(font);
482 char* name = getFontName(font);
483 Ref* r=font->getID();
484 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
486 GString*gstr = font->getTag();
488 msg("%s| Tag: %s\n", loglevel, id);
490 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
492 GfxFontType type=font->getType();
494 case fontUnknownType:
495 msg("%s| Type: unknown\n",loglevel);
498 msg("%s| Type: 1\n",loglevel);
501 msg("%s| Type: 1C\n",loglevel);
504 msg("%s| Type: 3\n",loglevel);
507 msg("%s| Type: TrueType\n",loglevel);
510 msg("%s| Type: CIDType0\n",loglevel);
513 msg("%s| Type: CIDType0C\n",loglevel);
516 msg("%s| Type: CIDType2\n",loglevel);
521 GBool embedded = font->getEmbeddedFontID(&embRef);
523 if(font->getEmbeddedFontName()) {
524 embeddedName = font->getEmbeddedFontName()->getCString();
527 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
529 gstr = font->getExtFontFile();
531 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
533 // Get font descriptor flags.
534 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
535 if(font->isSerif()) msg("%s| is serif\n", loglevel);
536 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
537 if(font->isItalic()) msg("%s| is italic\n", loglevel);
538 if(font->isBold()) msg("%s| is bold\n", loglevel);
544 //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");}
545 //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");}
548 void dump_outline(gfxline_t*line)
551 if(line->type == gfx_moveTo) {
552 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
553 } else if(line->type == gfx_lineTo) {
554 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
555 } else if(line->type == gfx_splineTo) {
556 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
562 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
564 int num = path->getNumSubpaths();
567 double lastx=0,lasty=0,posx=0,posy=0;
570 msg("<warning> empty path");
574 gfxdrawer_target_gfxline(&draw);
576 for(t = 0; t < num; t++) {
577 GfxSubpath *subpath = path->getSubpath(t);
578 int subnum = subpath->getNumPoints();
579 double bx=0,by=0,cx=0,cy=0;
581 for(s=0;s<subnum;s++) {
584 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
589 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
590 draw.lineTo(&draw, lastx, lasty);
592 draw.moveTo(&draw, x,y);
597 } else if(subpath->getCurve(s) && cpos==0) {
601 } else if(subpath->getCurve(s) && cpos==1) {
609 draw.lineTo(&draw, x,y);
611 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
618 /* fix non-closed lines */
619 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
620 draw.lineTo(&draw, lastx, lasty);
622 gfxline_t*result = (gfxline_t*)draw.result(&draw);
624 gfxline_optimize(result);
629 GBool GFXOutputDev::useTilingPatternFill()
631 warnfeature("tiled patterns", 1);
635 GBool GFXOutputDev::useShadedFills()
637 warnfeature("shaded fills", 1);
641 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
643 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
644 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
645 double miterLimit = state->getMiterLimit();
646 double width = state->getTransformedLineWidth();
649 double opaq = state->getStrokeOpacity();
651 state->getFillRGB(&rgb);
653 state->getStrokeRGB(&rgb);
655 col.r = colToByte(rgb.r);
656 col.g = colToByte(rgb.g);
657 col.b = colToByte(rgb.b);
658 col.a = (unsigned char)(opaq*255);
660 gfx_capType capType = gfx_capRound;
661 if(lineCap == 0) capType = gfx_capButt;
662 else if(lineCap == 1) capType = gfx_capRound;
663 else if(lineCap == 2) capType = gfx_capSquare;
665 gfx_joinType joinType = gfx_joinRound;
666 if(lineJoin == 0) joinType = gfx_joinMiter;
667 else if(lineJoin == 1) joinType = gfx_joinRound;
668 else if(lineJoin == 2) joinType = gfx_joinBevel;
671 double dashphase = 0;
673 state->getLineDash(&ldash, &dashnum, &dashphase);
677 if(dashnum && ldash) {
678 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
682 msg("<trace> %d dashes", dashnum);
683 msg("<trace> | phase: %f", dashphase);
684 for(t=0;t<dashnum;t++) {
686 msg("<trace> | d%-3d: %f", t, ldash[t]);
689 if(getLogLevel() >= LOGLEVEL_TRACE) {
693 line2 = gfxtool_dash_line(line, dash, dashphase);
696 msg("<trace> After dashing:");
699 if(getLogLevel() >= LOGLEVEL_TRACE) {
700 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
702 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
703 lineCap==0?"butt": (lineJoin==1?"round":"square"),
705 col.r,col.g,col.b,col.a
710 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
711 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
717 gfxcolor_t getFillColor(GfxState * state)
720 double opaq = state->getFillOpacity();
721 state->getFillRGB(&rgb);
723 col.r = colToByte(rgb.r);
724 col.g = colToByte(rgb.g);
725 col.b = colToByte(rgb.b);
726 col.a = (unsigned char)(opaq*255);
730 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
732 gfxcolor_t col = getFillColor(state);
734 if(getLogLevel() >= LOGLEVEL_TRACE) {
735 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
738 device->fill(device, line, &col);
741 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
743 if(getLogLevel() >= LOGLEVEL_TRACE) {
744 msg("<trace> clip\n");
748 device->startclip(device, line);
749 states[statepos].clipping++;
752 void GFXOutputDev::clip(GfxState *state)
754 GfxPath * path = state->getPath();
755 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
756 clipToGfxLine(state, line);
760 void GFXOutputDev::eoClip(GfxState *state)
762 GfxPath * path = state->getPath();
763 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
765 if(getLogLevel() >= LOGLEVEL_TRACE) {
766 msg("<trace> eoclip\n");
770 device->startclip(device, line);
771 states[statepos].clipping++;
775 void GFXOutputDev::endframe()
778 device->endclip(device);
782 device->endpage(device);
785 void GFXOutputDev::finish()
789 device->endclip(device);
795 GFXOutputDev::~GFXOutputDev()
800 free(this->pages); this->pages = 0;
803 fontlist_t*l = this->fontlist;
805 fontlist_t*next = l->next;
807 gfxfont_free(l->font);
808 free(l->filename);l->filename=0;
814 GBool GFXOutputDev::upsideDown()
818 GBool GFXOutputDev::useDrawChar()
823 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
824 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
826 #define RENDER_FILL 0
827 #define RENDER_STROKE 1
828 #define RENDER_FILLSTROKE 2
829 #define RENDER_INVISIBLE 3
830 #define RENDER_CLIP 4
832 static char tmp_printstr[4096];
833 char* makeStringPrintable(char*str)
835 int len = strlen(str);
850 tmp_printstr[len++] = '.';
851 tmp_printstr[len++] = '.';
852 tmp_printstr[len++] = '.';
854 tmp_printstr[len] = 0;
859 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
864 /* find out char name from unicode index
865 TODO: should be precomputed
867 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
868 if(nameToUnicodeTab[t].u == u) {
869 uniname = nameToUnicodeTab[t].name;
877 for(t=0;t<font->num_glyphs;t++) {
878 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
879 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
883 /* if we didn't find the character, maybe
884 we can find the capitalized version */
885 for(t=0;t<font->num_glyphs;t++) {
886 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
887 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
895 for(t=0;t<font->num_glyphs;t++) {
896 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
897 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
901 /* if we didn't find the character, maybe
902 we can find the capitalized version */
903 for(t=0;t<font->num_glyphs;t++) {
904 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
905 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
911 /* try to use the unicode id */
912 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
913 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
914 return font->unicode2glyph[u];
917 if(charnr>=0 && charnr<font->num_glyphs) {
918 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
926 void GFXOutputDev::beginString(GfxState *state, GString *s)
928 int render = state->getRender();
929 if(current_text_stroke) {
930 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
933 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
934 double m11,m21,m12,m22;
935 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
936 state->getFontTransMat(&m11, &m12, &m21, &m22);
937 m11 *= state->getHorizScaling();
938 m21 *= state->getHorizScaling();
940 this->current_font_matrix.m00 = m11 / 1024.0;
941 this->current_font_matrix.m01 = m12 / 1024.0;
942 this->current_font_matrix.m10 = -m21 / 1024.0;
943 this->current_font_matrix.m11 = -m22 / 1024.0;
944 this->current_font_matrix.tx = 0;
945 this->current_font_matrix.ty = 0;
947 gfxmatrix_t m = this->current_font_matrix;
949 states[statepos].textRender = render;
952 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
953 double dx, double dy,
954 double originX, double originY,
955 CharCode c, int nBytes, Unicode *_u, int uLen)
957 int render = state->getRender();
958 // check for invisible text -- this is used by Acrobat Capture
960 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
964 if(states[statepos].textRender != render)
965 msg("<error> Internal error: drawChar.render!=beginString.render");
967 gfxcolor_t col = getFillColor(state);
969 Gushort *CIDToGIDMap = 0;
970 GfxFont*font = state->getFont();
972 if(font->getType() == fontType3) {
973 /* type 3 chars are passed as graphics */
974 msg("<debug> type3 char at %f/%f", x, y);
984 if(font->isCIDFont()) {
985 GfxCIDFont*cfont = (GfxCIDFont*)font;
987 if(font->getType() == fontCIDType2)
988 CIDToGIDMap = cfont->getCIDToGID();
991 font8 = (Gfx8BitFont*)font;
992 char**enc=font8->getEncoding();
996 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);
999 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);
1005 charid = getGfxCharID(current_gfxfont, c, name, u);
1007 charid = getGfxCharID(current_gfxfont, c, name, -1);
1010 /* multiple unicodes- should usually map to a ligature.
1011 if the ligature doesn't exist, we need to draw
1012 the characters one-by-one. */
1014 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1015 for(t=0;t<uLen;t++) {
1016 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1022 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1023 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1027 gfxmatrix_t m = this->current_font_matrix;
1028 state->transform(x, y, &m.tx, &m.ty);
1029 m.tx += user_movex + clipmovex;
1030 m.ty += user_movey + clipmovey;
1032 if(render == RENDER_FILL) {
1033 device->drawchar(device, current_gfxfont, charid, &col, &m);
1035 msg("<debug> Drawing glyph %d as shape", charid);
1037 msg("<notice> Some texts will be rendered as shape");
1040 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1041 gfxline_t*tglyph = gfxline_clone(glyph);
1042 gfxline_transform(tglyph, &m);
1043 if((render&3) != RENDER_INVISIBLE) {
1044 gfxline_t*add = gfxline_clone(tglyph);
1045 current_text_stroke = gfxline_append(current_text_stroke, add);
1047 if(render&RENDER_CLIP) {
1048 gfxline_t*add = gfxline_clone(tglyph);
1049 current_text_clip = gfxline_append(current_text_clip, add);
1051 gfxline_free(tglyph);
1055 void GFXOutputDev::endString(GfxState *state)
1057 int render = state->getRender();
1058 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1059 if(states[statepos].textRender != render)
1060 msg("<error> Internal error: drawChar.render!=beginString.render");
1062 if(current_text_stroke) {
1063 /* fillstroke and stroke text rendering objects we can process right
1064 now (as there may be texts of other rendering modes in this
1065 text object)- clipping objects have to wait until endTextObject,
1067 device->setparameter(device, "mark","TXT");
1068 if((render&3) == RENDER_FILL) {
1069 fillGfxLine(state, current_text_stroke);
1070 gfxline_free(current_text_stroke);
1071 current_text_stroke = 0;
1072 } else if((render&3) == RENDER_FILLSTROKE) {
1073 fillGfxLine(state, current_text_stroke);
1074 strokeGfxline(state, current_text_stroke);
1075 gfxline_free(current_text_stroke);
1076 current_text_stroke = 0;
1077 } else if((render&3) == RENDER_STROKE) {
1078 strokeGfxline(state, current_text_stroke);
1079 gfxline_free(current_text_stroke);
1080 current_text_stroke = 0;
1082 device->setparameter(device, "mark","");
1086 void GFXOutputDev::endTextObject(GfxState *state)
1088 int render = state->getRender();
1089 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1090 if(states[statepos].textRender != render)
1091 msg("<error> Internal error: drawChar.render!=beginString.render");
1093 if(current_text_clip) {
1094 device->setparameter(device, "mark","TXT");
1095 clipToGfxLine(state, current_text_clip);
1096 device->setparameter(device, "mark","");
1097 gfxline_free(current_text_clip);
1098 current_text_clip = 0;
1102 /* the logic seems to be as following:
1103 first, beginType3Char is called, with the charcode and the coordinates.
1104 if this function returns true, it already knew about the char and has now drawn it.
1105 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1106 the all draw operations until endType3Char are part of the char (which in this moment is
1107 at the position first passed to beginType3Char). the char ends with endType3Char.
1109 The drawing operations between beginType3Char and endType3Char are somewhat different to
1110 the normal ones. For example, the fillcolor equals the stroke color.
1113 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1115 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1117 /* the character itself is going to be passed using the draw functions */
1118 return gFalse; /* gTrue= is_in_cache? */
1121 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1122 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1124 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1125 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1129 void GFXOutputDev::endType3Char(GfxState *state)
1132 msg("<debug> endType3Char");
1135 void GFXOutputDev::startFrame(int width, int height)
1137 if(outer_clip_box) {
1138 device->endclip(device);
1142 device->startpage(device, width, height);
1143 this->width = width;
1144 this->height = height;
1147 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1149 this->currentpage = pageNum;
1151 int rot = doc->getPageRotate(1);
1154 gfxline_t clippath[5];
1156 white.r = white.g = white.b = white.a = 255;
1158 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1159 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1160 Use CropBox, not MediaBox, as page size
1167 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1168 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1170 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1171 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1173 this->clipmovex = -(int)x1;
1174 this->clipmovey = -(int)y1;
1176 /* apply user clip box */
1177 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1178 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1179 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1180 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1181 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1182 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1185 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1187 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);
1189 msg("<verbose> page is rotated %d degrees\n", rot);
1191 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1192 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1193 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1194 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1195 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1196 device->startclip(device, clippath); outer_clip_box = 1;
1197 device->fill(device, clippath, &white);
1200 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1202 double x1, y1, x2, y2, w;
1203 gfxline_t points[5];
1206 msg("<debug> drawlink\n");
1208 link->getRect(&x1, &y1, &x2, &y2);
1209 cvtUserToDev(x1, y1, &x, &y);
1210 points[0].type = gfx_moveTo;
1211 points[0].x = points[4].x = x + user_movex + clipmovex;
1212 points[0].y = points[4].y = y + user_movey + clipmovey;
1213 points[0].next = &points[1];
1214 cvtUserToDev(x2, y1, &x, &y);
1215 points[1].type = gfx_lineTo;
1216 points[1].x = x + user_movex + clipmovex;
1217 points[1].y = y + user_movey + clipmovey;
1218 points[1].next = &points[2];
1219 cvtUserToDev(x2, y2, &x, &y);
1220 points[2].type = gfx_lineTo;
1221 points[2].x = x + user_movex + clipmovex;
1222 points[2].y = y + user_movey + clipmovey;
1223 points[2].next = &points[3];
1224 cvtUserToDev(x1, y2, &x, &y);
1225 points[3].type = gfx_lineTo;
1226 points[3].x = x + user_movex + clipmovex;
1227 points[3].y = y + user_movey + clipmovey;
1228 points[3].next = &points[4];
1229 cvtUserToDev(x1, y1, &x, &y);
1230 points[4].type = gfx_lineTo;
1231 points[4].x = x + user_movex + clipmovex;
1232 points[4].y = y + user_movey + clipmovey;
1235 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1236 points[0].x, points[0].y,
1237 points[1].x, points[1].y,
1238 points[2].x, points[2].y,
1239 points[3].x, points[3].y);
1241 LinkAction*action=link->getAction();
1247 msg("<trace> drawlink action=%d\n", action->getKind());
1248 switch(action->getKind())
1252 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1253 LinkDest *dest=NULL;
1254 if (ha->getDest()==NULL)
1255 dest=catalog->findDest(ha->getNamedDest());
1256 else dest=ha->getDest();
1258 if (dest->isPageRef()){
1259 Ref pageref=dest->getPageRef();
1260 page=catalog->findPage(pageref.num,pageref.gen);
1262 else page=dest->getPageNum();
1263 sprintf(buf, "%d", page);
1270 LinkGoToR*l = (LinkGoToR*)action;
1271 GString*g = l->getFileName();
1273 s = strdup(g->getCString());
1275 /* if the GoToR link has no filename, then
1276 try to find a refernce in the *local*
1278 GString*g = l->getNamedDest();
1280 s = strdup(g->getCString());
1286 LinkNamed*l = (LinkNamed*)action;
1287 GString*name = l->getName();
1289 s = strdup(name->lowerCase()->getCString());
1290 named = name->getCString();
1293 if(strstr(s, "next") || strstr(s, "forward"))
1295 page = currentpage + 1;
1297 else if(strstr(s, "prev") || strstr(s, "back"))
1299 page = currentpage - 1;
1301 else if(strstr(s, "last") || strstr(s, "end"))
1303 if(pages && pagepos>0)
1304 page = pages[pagepos-1];
1306 else if(strstr(s, "first") || strstr(s, "top"))
1314 case actionLaunch: {
1316 LinkLaunch*l = (LinkLaunch*)action;
1317 GString * str = new GString(l->getFileName());
1318 GString * params = l->getParams();
1320 str->append(params);
1321 s = strdup(str->getCString());
1328 LinkURI*l = (LinkURI*)action;
1329 GString*g = l->getURI();
1331 url = g->getCString();
1336 case actionUnknown: {
1338 LinkUnknown*l = (LinkUnknown*)action;
1343 msg("<error> Unknown link type!\n");
1348 if(!s) s = strdup("-?-");
1350 msg("<trace> drawlink s=%s\n", s);
1352 if(!linkinfo && (page || s))
1354 msg("<notice> File contains links");
1362 for(t=1;t<=pagepos;t++) {
1363 if(pages[t]==page) {
1372 sprintf(buf, "page%d", lpage);
1373 device->drawlink(device, points, buf);
1377 device->drawlink(device, points, s);
1380 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1384 void GFXOutputDev::saveState(GfxState *state) {
1385 dbg("saveState");dbgindent+=2;
1387 msg("<trace> saveState\n");
1390 msg("<error> Too many nested states in pdf.");
1394 states[statepos].textRender = states[statepos-1].textRender;
1395 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1396 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1397 states[statepos].clipping = 0;
1400 void GFXOutputDev::restoreState(GfxState *state) {
1401 dbgindent-=2; dbg("restoreState");
1404 msg("<error> Invalid restoreState");
1407 msg("<trace> restoreState");
1408 if(states[statepos].softmask) {
1409 clearSoftMask(state);
1412 while(states[statepos].clipping) {
1413 device->endclip(device);
1414 states[statepos].clipping--;
1419 char* GFXOutputDev::searchFont(char*name)
1423 int is_standard_font = 0;
1425 msg("<verbose> SearchFont(%s)", name);
1427 /* see if it is a pdf standard font */
1428 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1430 if(!strcmp(name, pdf2t1map[i].pdffont))
1432 name = pdf2t1map[i].filename;
1433 is_standard_font = 1;
1437 /* look in all font files */
1438 for(i=0;i<fontnum;i++)
1440 if(strstr(fonts[i].filename, name))
1442 if(!fonts[i].used) {
1445 if(!is_standard_font)
1446 msg("<notice> Using %s for %s", fonts[i].filename, name);
1448 return strdup(fonts[i].filename);
1454 void GFXOutputDev::updateLineWidth(GfxState *state)
1456 double width = state->getTransformedLineWidth();
1457 //swfoutput_setlinewidth(&device, width);
1460 void GFXOutputDev::updateLineCap(GfxState *state)
1462 int c = state->getLineCap();
1465 void GFXOutputDev::updateLineJoin(GfxState *state)
1467 int j = state->getLineJoin();
1470 void GFXOutputDev::updateFillColor(GfxState *state)
1473 double opaq = state->getFillOpacity();
1474 state->getFillRGB(&rgb);
1476 void GFXOutputDev::updateFillOpacity(GfxState *state)
1479 double opaq = state->getFillOpacity();
1480 state->getFillRGB(&rgb);
1481 dbg("update fillopaq %f", opaq);
1483 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1485 double opaq = state->getFillOpacity();
1486 dbg("update strokeopaq %f", opaq);
1488 void GFXOutputDev::updateFillOverprint(GfxState *state)
1490 double opaq = state->getFillOverprint();
1491 dbg("update filloverprint %f", opaq);
1493 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1495 double opaq = state->getStrokeOverprint();
1496 dbg("update strokeoverprint %f", opaq);
1498 void GFXOutputDev::updateTransfer(GfxState *state)
1500 dbg("update transfer");
1504 void GFXOutputDev::updateStrokeColor(GfxState *state)
1507 double opaq = state->getStrokeOpacity();
1508 state->getStrokeRGB(&rgb);
1511 void FoFiWrite(void *stream, char *data, int len)
1513 int ret = fwrite(data, len, 1, (FILE*)stream);
1516 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1518 char*tmpFileName = NULL;
1524 Object refObj, strObj;
1526 tmpFileName = mktmpname(namebuf);
1529 ret = font->getEmbeddedFontID(&embRef);
1531 msg("<verbose> Didn't get embedded font id");
1532 /* not embedded- the caller should now search the font
1533 directories for this font */
1537 f = fopen(tmpFileName, "wb");
1539 msg("<error> Couldn't create temporary Type 1 font file");
1543 /*if(font->isCIDFont()) {
1544 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1545 GString c = cidFont->getCollection();
1546 msg("<notice> Collection: %s", c.getCString());
1549 //if (font->getType() == fontType1C) {
1550 if (0) { //font->getType() == fontType1C) {
1551 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1553 msg("<error> Couldn't read embedded font file");
1556 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1558 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1559 //cvt->convertToCIDType0("test", f);
1560 //cvt->convertToType0("test", f);
1563 } else if(font->getType() == fontTrueType) {
1564 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1565 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1567 msg("<error> Couldn't read embedded font file");
1570 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1571 cvt->writeTTF(FoFiWrite, f);
1575 font->getEmbeddedFontID(&embRef);
1576 refObj.initRef(embRef.num, embRef.gen);
1577 refObj.fetch(ref, &strObj);
1579 strObj.streamReset();
1584 f4[t] = strObj.streamGetChar();
1585 f4c[t] = (char)f4[t];
1590 if(!strncmp(f4c, "true", 4)) {
1591 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1592 Change this on the fly */
1593 f4[0] = f4[2] = f4[3] = 0;
1601 while ((c = strObj.streamGetChar()) != EOF) {
1605 strObj.streamClose();
1610 return strdup(tmpFileName);
1613 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1615 char*name = getFontName(gfxFont);
1619 if(!this->config_use_fontconfig)
1622 #ifdef HAVE_FONTCONFIG
1623 FcPattern *pattern, *match;
1627 static int fcinitcalled = false;
1629 msg("<debug> searchForSuitableFont(%s)", name);
1631 // call init ony once
1632 if (!fcinitcalled) {
1633 msg("<debug> Initializing FontConfig...");
1634 fcinitcalled = true;
1636 msg("<debug> FontConfig Initialization failed. Disabling.");
1637 config_use_fontconfig = 0;
1640 msg("<debug> ...initialized FontConfig");
1643 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1644 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1645 if (gfxFont->isItalic()) // check for italic
1646 msg("<debug> FontConfig: Adding Italic Slant");
1647 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1648 if (gfxFont->isBold()) // check for bold
1649 msg("<debug> FontConfig: Adding Bold Weight");
1650 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1652 msg("<debug> FontConfig: Try to match...");
1653 // configure and match using the original font name
1654 FcConfigSubstitute(0, pattern, FcMatchPattern);
1655 FcDefaultSubstitute(pattern);
1656 match = FcFontMatch(0, pattern, &result);
1658 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1659 msg("<debug> FontConfig: family=%s", (char*)v);
1660 // if we get an exact match
1661 if (strcmp((char *)v, name) == 0) {
1662 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1663 filename = strdup((char*)v); // mem leak
1664 char *nfn = strrchr(filename, '/');
1665 if(nfn) fontname = strdup(nfn+1);
1666 else fontname = filename;
1668 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1670 // initialize patterns
1671 FcPatternDestroy(pattern);
1672 FcPatternDestroy(match);
1674 // now match against serif etc.
1675 if (gfxFont->isSerif()) {
1676 msg("<debug> FontConfig: Create Serif Family Pattern");
1677 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1678 } else if (gfxFont->isFixedWidth()) {
1679 msg("<debug> FontConfig: Create Monospace Family Pattern");
1680 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1682 msg("<debug> FontConfig: Create Sans Family Pattern");
1683 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1687 if (gfxFont->isItalic()) {
1688 msg("<debug> FontConfig: Adding Italic Slant");
1689 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1692 if (gfxFont->isBold()) {
1693 msg("<debug> FontConfig: Adding Bold Weight");
1694 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1697 msg("<debug> FontConfig: Try to match... (2)");
1698 // configure and match using serif etc
1699 FcConfigSubstitute (0, pattern, FcMatchPattern);
1700 FcDefaultSubstitute (pattern);
1701 match = FcFontMatch (0, pattern, &result);
1703 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1704 filename = strdup((char*)v); // mem leak
1705 char *nfn = strrchr(filename, '/');
1706 if(nfn) fontname = strdup(nfn+1);
1707 else fontname = filename;
1709 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1713 //printf("FONTCONFIG: pattern");
1714 //FcPatternPrint(pattern);
1715 //printf("FONTCONFIG: match");
1716 //FcPatternPrint(match);
1718 FcPatternDestroy(pattern);
1719 FcPatternDestroy(match);
1721 pdfswf_addfont(filename);
1728 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1730 char*fontname = 0, *filename = 0;
1731 msg("<notice> substituteFont(%s)", oldname);
1733 if(!(fontname = searchForSuitableFont(gfxFont))) {
1734 fontname = "Times-Roman";
1736 filename = searchFont(fontname);
1738 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1742 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1743 msg("<fatal> Too many fonts in file.");
1747 substitutesource[substitutepos] = strdup(oldname); //mem leak
1748 substitutetarget[substitutepos] = fontname;
1749 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1752 return strdup(filename); //mem leak
1755 void unlinkfont(char* filename)
1762 if(!strncmp(&filename[l-4],".afm",4)) {
1763 memcpy(&filename[l-4],".pfb",4);
1765 memcpy(&filename[l-4],".pfa",4);
1767 memcpy(&filename[l-4],".afm",4);
1770 if(!strncmp(&filename[l-4],".pfa",4)) {
1771 memcpy(&filename[l-4],".afm",4);
1773 memcpy(&filename[l-4],".pfa",4);
1776 if(!strncmp(&filename[l-4],".pfb",4)) {
1777 memcpy(&filename[l-4],".afm",4);
1779 memcpy(&filename[l-4],".pfb",4);
1784 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1790 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1793 fontlist_t*last=0,*l = this->fontlist;
1796 msg("<error> Internal Error: FontID is null");
1798 /* TODO: should this be part of the state? */
1801 if(!strcmp(l->font->id, id)) {
1802 current_gfxfont = l->font;
1804 device->addfont(device, current_gfxfont);
1809 if(!filename) return 0;
1811 /* A font size of e.g. 9 means the font will be scaled down by
1812 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1813 we have to divide 0.05 by (fontsize/1024)
1815 double quality = (1024 * 0.05) / maxSize;
1817 msg("<verbose> Loading %s...", filename);
1818 font = gfxfont_load(id, filename, quality);
1820 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1823 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1827 l->filename = strdup(filename);
1829 current_gfxfont = l->font;
1835 device->addfont(device, current_gfxfont);
1839 void GFXOutputDev::updateFont(GfxState *state)
1841 GfxFont*gfxFont = state->getFont();
1847 char * fontid = getFontID(gfxFont);
1848 char * fontname = getFontName(gfxFont);
1850 double maxSize = 1.0;
1853 maxSize = this->info->getMaximumFontSize(fontid);
1857 /* first, look if we substituted this font before-
1858 this way, we don't initialize the T1 Fonts
1860 for(t=0;t<substitutepos;t++) {
1861 if(!strcmp(fontid, substitutesource[t])) {
1862 free(fontid);fontid=0;
1863 fontid = strdup(substitutetarget[t]);
1868 /* second, see if this is a font which was used before-
1869 if so, we are done */
1870 if(setGfxFont(fontid, fontname, 0, 0)) {
1875 /* if(swfoutput_queryfont(&device, fontid))
1876 swfoutput_setfont(&device, fontid, 0);
1878 msg("<debug> updateFont(%s) [cached]", fontid);
1882 // look for Type 3 font
1883 if (gfxFont->getType() == fontType3) {
1885 type3Warning = gTrue;
1886 showFontError(gfxFont, 2);
1893 /* now either load the font, or find a substitution */
1896 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1901 (gfxFont->getType() == fontType1 ||
1902 gfxFont->getType() == fontType1C ||
1903 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1904 gfxFont->getType() == fontTrueType ||
1905 gfxFont->getType() == fontCIDType2
1908 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1909 if(!fileName) showFontError(gfxFont,0);
1912 fileName = searchFont(fontname);
1913 if(!fileName) showFontError(gfxFont,0);
1916 char * fontname = getFontName(gfxFont);
1917 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1920 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1922 msg("<warning> Try specifying one or more font directories");
1924 fileName = substituteFont(gfxFont, fontid);
1927 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1928 msg("<notice> Font is now %s (%s)", fontid, fileName);
1932 msg("<error> Couldn't set font %s\n", fontid);
1938 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1939 dumpFontInfo("<verbose>", gfxFont);
1941 //swfoutput_setfont(&device, fontid, fileName);
1943 if(!setGfxFont(fontid, fontname, 0, 0)) {
1944 setGfxFont(fontid, fontname, fileName, maxSize);
1948 unlinkfont(fileName);
1958 #define SQR(x) ((x)*(x))
1960 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1962 if((newwidth<2 || newheight<2) ||
1963 (width<=newwidth || height<=newheight))
1965 unsigned char*newdata;
1967 newdata= (unsigned char*)malloc(newwidth*newheight);
1969 double fx = (double)(width)/newwidth;
1970 double fy = (double)(height)/newheight;
1972 int blocksize = (int)(8192/(fx*fy));
1973 int r = 8192*256/palettesize;
1974 for(x=0;x<newwidth;x++) {
1975 double ex = px + fx;
1976 int fromx = (int)px;
1978 int xweight1 = (int)(((fromx+1)-px)*256);
1979 int xweight2 = (int)((ex-tox)*256);
1981 for(y=0;y<newheight;y++) {
1982 double ey = py + fy;
1983 int fromy = (int)py;
1985 int yweight1 = (int)(((fromy+1)-py)*256);
1986 int yweight2 = (int)((ey-toy)*256);
1989 for(xx=fromx;xx<=tox;xx++)
1990 for(yy=fromy;yy<=toy;yy++) {
1991 int b = 1-data[width*yy+xx];
1993 if(xx==fromx) weight = (weight*xweight1)/256;
1994 if(xx==tox) weight = (weight*xweight2)/256;
1995 if(yy==fromy) weight = (weight*yweight1)/256;
1996 if(yy==toy) weight = (weight*yweight2)/256;
1999 //if(a) a=(palettesize-1)*r/blocksize;
2000 newdata[y*newwidth+x] = (a*blocksize)/r;
2008 #define IMAGE_TYPE_JPEG 0
2009 #define IMAGE_TYPE_LOSSLESS 1
2011 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2012 double x1,double y1,
2013 double x2,double y2,
2014 double x3,double y3,
2015 double x4,double y4, int type)
2017 gfxcolor_t*newpic=0;
2019 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2020 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2022 gfxline_t p1,p2,p3,p4,p5;
2023 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2024 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2025 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2026 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2027 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2029 {p1.x = (int)(p1.x*20)/20.0;
2030 p1.y = (int)(p1.y*20)/20.0;
2031 p2.x = (int)(p2.x*20)/20.0;
2032 p2.y = (int)(p2.y*20)/20.0;
2033 p3.x = (int)(p3.x*20)/20.0;
2034 p3.y = (int)(p3.y*20)/20.0;
2035 p4.x = (int)(p4.x*20)/20.0;
2036 p4.y = (int)(p4.y*20)/20.0;
2037 p5.x = (int)(p5.x*20)/20.0;
2038 p5.y = (int)(p5.y*20)/20.0;
2045 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2046 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2051 img.data = (gfxcolor_t*)data;
2055 if(type == IMAGE_TYPE_JPEG)
2056 /* TODO: pass image_dpi to device instead */
2057 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2059 dev->fillbitmap(dev, &p1, &img, &m, 0);
2062 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2063 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2065 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2068 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2069 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2071 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2075 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2076 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2077 GBool inlineImg, int mask, int*maskColors,
2078 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2080 double x1,y1,x2,y2,x3,y3,x4,y4;
2081 ImageStream *imgStr;
2086 unsigned char* maskbitmap = 0;
2089 ncomps = colorMap->getNumPixelComps();
2090 bits = colorMap->getBits();
2095 unsigned char buf[8];
2096 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2098 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2099 imgMaskStr->reset();
2100 unsigned char pal[256];
2101 int n = 1 << colorMap->getBits();
2106 maskColorMap->getGray(pixBuf, &gray);
2107 pal[t] = colToByte(gray);
2109 for (y = 0; y < maskHeight; y++) {
2110 for (x = 0; x < maskWidth; x++) {
2111 imgMaskStr->getPixel(buf);
2112 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2117 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2118 imgMaskStr->reset();
2119 for (y = 0; y < maskHeight; y++) {
2120 for (x = 0; x < maskWidth; x++) {
2121 imgMaskStr->getPixel(buf);
2123 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2131 imgStr = new ImageStream(str, width, ncomps,bits);
2134 if(!width || !height || (height<=1 && width<=1))
2136 msg("<verbose> Ignoring %d by %d image", width, height);
2137 unsigned char buf[8];
2139 for (y = 0; y < height; ++y)
2140 for (x = 0; x < width; ++x) {
2141 imgStr->getPixel(buf);
2149 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2150 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2151 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2152 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2154 if(!pbminfo && !(str->getKind()==strDCT)) {
2156 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2160 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2162 if(!jpeginfo && (str->getKind()==strDCT)) {
2163 msg("<notice> file contains jpeg pictures");
2169 unsigned char buf[8];
2171 unsigned char*pic = new unsigned char[width*height];
2172 gfxcolor_t pal[256];
2174 state->getFillRGB(&rgb);
2176 memset(pal,255,sizeof(pal));
2177 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2178 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2179 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2180 pal[0].a = 255; pal[1].a = 0;
2183 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2184 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2185 for (y = 0; y < height; ++y)
2186 for (x = 0; x < width; ++x)
2188 imgStr->getPixel(buf);
2191 pic[width*y+x] = buf[0];
2194 /* the size of the drawn image is added to the identifier
2195 as the same image may require different bitmaps if displayed
2196 at different sizes (due to antialiasing): */
2199 unsigned char*pic2 = 0;
2202 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2211 height = realheight;
2215 /* make a black/white palette */
2217 float r = 255/(numpalette-1);
2219 for(t=0;t<numpalette;t++) {
2220 pal[t].r = colToByte(rgb.r);
2221 pal[t].g = colToByte(rgb.g);
2222 pal[t].b = colToByte(rgb.b);
2223 pal[t].a = (unsigned char)(t*r);
2227 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2228 for (y = 0; y < height; ++y) {
2229 for (x = 0; x < width; ++x) {
2230 pic2[width*y+x] = pal[pic[y*width+x]];
2233 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2237 if(maskbitmap) free(maskbitmap);
2243 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2244 gfxcolor_t*pic=new gfxcolor_t[width*height];
2245 for (y = 0; y < height; ++y) {
2246 for (x = 0; x < width; ++x) {
2247 imgStr->getPixel(pixBuf);
2248 colorMap->getRGB(pixBuf, &rgb);
2249 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2250 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2251 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2252 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2254 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2258 if(str->getKind()==strDCT)
2259 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2261 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2264 if(maskbitmap) free(maskbitmap);
2267 gfxcolor_t*pic=new gfxcolor_t[width*height];
2268 gfxcolor_t pal[256];
2269 int n = 1 << colorMap->getBits();
2271 for(t=0;t<256;t++) {
2273 colorMap->getRGB(pixBuf, &rgb);
2275 {/*if(maskColors && *maskColors==t) {
2276 msg("<notice> Color %d is transparent", t);
2277 if (imgData->maskColors) {
2279 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2280 if (pix[i] < imgData->maskColors[2*i] ||
2281 pix[i] > imgData->maskColors[2*i+1]) {
2296 pal[t].r = (unsigned char)(colToByte(rgb.r));
2297 pal[t].g = (unsigned char)(colToByte(rgb.g));
2298 pal[t].b = (unsigned char)(colToByte(rgb.b));
2299 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2302 for (y = 0; y < height; ++y) {
2303 for (x = 0; x < width; ++x) {
2304 imgStr->getPixel(pixBuf);
2305 pic[width*y+x] = pal[pixBuf[0]];
2307 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2311 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2315 if(maskbitmap) free(maskbitmap);
2320 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2321 int width, int height, GBool invert,
2324 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2325 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2326 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2329 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2330 int width, int height, GfxImageColorMap *colorMap,
2331 int *maskColors, GBool inlineImg)
2333 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2334 colorMap?"colorMap":"no colorMap",
2335 maskColors?"maskColors":"no maskColors",
2337 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2338 colorMap?"colorMap":"no colorMap",
2339 maskColors?"maskColors":"no maskColors",
2342 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2343 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2344 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2347 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2348 int width, int height,
2349 GfxImageColorMap *colorMap,
2350 Stream *maskStr, int maskWidth, int maskHeight,
2353 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2354 colorMap?"colorMap":"no colorMap",
2355 maskWidth, maskHeight);
2356 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2357 colorMap?"colorMap":"no colorMap",
2358 maskWidth, maskHeight);
2360 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2361 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2362 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2365 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2366 int width, int height,
2367 GfxImageColorMap *colorMap,
2369 int maskWidth, int maskHeight,
2370 GfxImageColorMap *maskColorMap)
2372 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2373 colorMap?"colorMap":"no colorMap",
2374 maskWidth, maskHeight);
2375 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2376 colorMap?"colorMap":"no colorMap",
2377 maskWidth, maskHeight);
2379 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2380 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2381 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2384 void GFXOutputDev::stroke(GfxState *state)
2388 GfxPath * path = state->getPath();
2389 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2390 strokeGfxline(state, line);
2394 void GFXOutputDev::fill(GfxState *state)
2398 GfxPath * path = state->getPath();
2399 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2400 fillGfxLine(state, line);
2404 void GFXOutputDev::eoFill(GfxState *state)
2408 GfxPath * path = state->getPath();
2409 gfxcolor_t col = getFillColor(state);
2411 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2413 if(getLogLevel() >= LOGLEVEL_TRACE) {
2414 msg("<trace> eofill\n");
2418 device->fill(device, line, &col);
2423 static char* dirseparator()
2432 void addGlobalFont(char*filename)
2435 memset(&f, 0, sizeof(fontfile_t));
2436 f.filename = filename;
2437 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2438 msg("<verbose> Adding font \"%s\".", filename);
2439 fonts[fontnum++] = f;
2441 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2445 void addGlobalLanguageDir(char*dir)
2448 globalParams = new GlobalParams("");
2450 msg("<notice> Adding %s to language pack directories", dir);
2454 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2455 strcpy(config_file, dir);
2456 strcat(config_file, dirseparator());
2457 strcat(config_file, "add-to-xpdfrc");
2459 fi = fopen(config_file, "rb");
2461 msg("<error> Could not open %s", config_file);
2464 globalParams->parseFile(new GString(config_file), fi);
2468 void addGlobalFontDir(char*dirname)
2470 #ifdef HAVE_DIRENT_H
2471 msg("<notice> Adding %s to font directories", dirname);
2472 lastfontdir = strdup(dirname);
2473 DIR*dir = opendir(dirname);
2475 msg("<warning> Couldn't open directory %s\n", dirname);
2480 ent = readdir (dir);
2484 char*name = ent->d_name;
2490 if(!strncasecmp(&name[l-4], ".pfa", 4))
2492 if(!strncasecmp(&name[l-4], ".pfb", 4))
2494 if(!strncasecmp(&name[l-4], ".ttf", 4))
2498 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2499 strcpy(fontname, dirname);
2500 strcat(fontname, dirseparator());
2501 strcat(fontname, name);
2502 addGlobalFont(fontname);
2507 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2511 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2517 this->pagebuflen = 1024;
2518 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2519 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2521 while(pdfpage >= this->pagebuflen)
2523 int oldlen = this->pagebuflen;
2524 this->pagebuflen+=1024;
2525 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2526 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2529 this->pages[pdfpage] = outputpage;
2530 if(pdfpage>this->pagepos)
2531 this->pagepos = pdfpage;
2537 double width,height;
2540 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2542 double xMin, yMin, xMax, yMax, x, y;
2543 double tx, ty, w, h;
2544 // transform the bbox
2545 state->transform(bbox[0], bbox[1], &x, &y);
2548 state->transform(bbox[0], bbox[3], &x, &y);
2551 } else if (x > xMax) {
2556 } else if (y > yMax) {
2559 state->transform(bbox[2], bbox[1], &x, &y);
2562 } else if (x > xMax) {
2567 } else if (y > yMax) {
2570 state->transform(bbox[2], bbox[3], &x, &y);
2573 } else if (x > xMax) {
2578 } else if (y > yMax) {
2581 tx = (int)floor(xMin);
2584 } else if (tx > width) {
2587 ty = (int)floor(yMin);
2590 } else if (ty > height) {
2593 w = (int)ceil(xMax) - tx + 1;
2594 if (tx + w > width) {
2600 h = (int)ceil(yMax) - ty + 1;
2601 if (ty + h > height) {
2615 #if xpdfUpdateVersion >= 16
2616 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2617 GfxColorSpace *blendingColorSpace,
2618 GBool isolated, GBool knockout,
2621 char*colormodename = "";
2622 BBox rect = mkBBox(state, bbox, this->width, this->height);
2624 if(blendingColorSpace) {
2625 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2627 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);
2628 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2629 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);
2630 states[statepos].createsoftmask |= forSoftMask;
2631 states[statepos].transparencygroup = !forSoftMask;
2632 states[statepos].olddevice = this->device;
2634 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2636 gfxdevice_record_init(this->device);
2638 /*if(!forSoftMask) { ////???
2639 state->setFillOpacity(0.0);
2644 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2647 dbg("endTransparencyGroup");
2648 msg("<verbose> endTransparencyGroup");
2650 gfxdevice_t*r = this->device;
2652 this->device = states[statepos].olddevice;
2654 if(states[statepos].createsoftmask) {
2655 states[statepos-1].softmaskrecording = r->finish(r);
2657 states[statepos-1].grouprecording = r->finish(r);
2660 states[statepos].createsoftmask = 0;
2661 states[statepos].transparencygroup = 0;
2665 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2667 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2668 "colordodge","colorburn","hardlight","softlight","difference",
2669 "exclusion","hue","saturation","color","luminosity"};
2671 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2672 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2674 if(state->getBlendMode() == gfxBlendNormal)
2675 infofeature("transparency groups");
2678 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2679 warnfeature("transparency groups", 0);
2682 gfxresult_t*grouprecording = states[statepos].grouprecording;
2684 if(state->getBlendMode() == gfxBlendNormal) {
2686 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2687 gfxresult_record_replay(grouprecording, &ops);
2690 grouprecording->destroy(grouprecording);
2692 states[statepos].grouprecording = 0;
2695 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2697 /* alpha = 1: retrieve mask values from alpha layer
2698 alpha = 0: retrieve mask values from luminance */
2699 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2700 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2701 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2702 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2704 infofeature("soft masks");
2706 warnfeature("soft masks from alpha channel",0);
2708 states[statepos].olddevice = this->device;
2709 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2710 gfxdevice_record_init(this->device);
2712 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2714 states[statepos].softmask = 1;
2717 void GFXOutputDev::clearSoftMask(GfxState *state)
2719 if(!states[statepos].softmask)
2721 states[statepos].softmask = 0;
2722 dbg("clearSoftMask statepos=%d", statepos);
2723 msg("<verbose> clearSoftMask");
2725 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2726 msg("<error> Error in softmask/tgroup ordering");
2730 gfxresult_t*mask = states[statepos].softmaskrecording;
2731 gfxresult_t*below = this->device->finish(this->device);
2732 this->device = states[statepos].olddevice;
2734 /* get outline of all objects below the soft mask */
2735 gfxdevice_t uniondev;
2736 gfxdevice_union_init(&uniondev, 0);
2737 gfxresult_record_replay(below, &uniondev);
2738 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2739 uniondev.finish(&uniondev);
2741 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2743 this->device->startclip(this->device, belowoutline);
2744 gfxresult_record_replay(below, this->device);
2745 gfxresult_record_replay(mask, this->device);
2746 this->device->endclip(this->device);
2747 gfxline_free(belowoutline);
2750 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2752 gfxdevice_t belowrender;
2753 gfxdevice_render_init(&belowrender);
2754 belowrender.setparameter(&belowrender, "antialize", "2");
2755 belowrender.startpage(&belowrender, width, height);
2756 gfxresult_record_replay(below, &belowrender);
2757 belowrender.endpage(&belowrender);
2758 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2759 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2761 gfxdevice_t maskrender;
2762 gfxdevice_render_init(&maskrender);
2763 maskrender.startpage(&maskrender, width, height);
2764 gfxresult_record_replay(mask, &maskrender);
2765 maskrender.endpage(&maskrender);
2766 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2767 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2769 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2770 msg("<fatal> Internal error in mask drawing");
2775 for(y=0;y<height;y++) {
2776 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2777 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2778 for(x=0;x<width;x++) {
2780 l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2782 /* premultiply alpha... do we need this? (depends on output device)
2783 l2->r = (l2->a*l2->r) >> 8;
2784 l2->g = (l2->a*l2->g) >> 8;
2785 l2->b = (l2->a*l2->b) >> 8;
2792 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2795 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2796 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2798 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2800 mask->destroy(mask);
2801 below->destroy(below);
2802 maskresult->destroy(maskresult);
2803 belowresult->destroy(belowresult);
2804 states[statepos].softmaskrecording = 0;
2813 delete globalParams;globalParams=0;
2814 Object::memCheck(stderr);