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 = 1;
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 warnfeature(char*feature,char fully)
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;
156 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
159 GFXOutputState::GFXOutputState() {
161 this->textRender = 0;
162 this->createsoftmask = 0;
163 this->transparencygroup = 0;
165 this->grouprecording = 0;
168 GBool GFXOutputDev::interpretType3Chars()
173 typedef struct _drawnchar
191 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
192 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
197 free(chars);chars = 0;
204 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
208 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
211 chars[num_chars].x = x;
212 chars[num_chars].y = y;
213 chars[num_chars].color = color;
214 chars[num_chars].charid = charid;
218 static char*getFontID(GfxFont*font);
220 GFXOutputDev::GFXOutputDev(parameter_t*p)
223 this->textmodeinfo = 0;
227 this->type3active = 0;
230 this->substitutepos = 0;
231 this->type3Warning = 0;
232 this->user_movex = 0;
233 this->user_movey = 0;
236 this->user_clipx1 = 0;
237 this->user_clipy1 = 0;
238 this->user_clipx2 = 0;
239 this->user_clipy2 = 0;
240 this->current_text_stroke = 0;
241 this->current_text_clip = 0;
243 this->outer_clip_box = 0;
245 this->pagebuflen = 0;
248 this->forceType0Fonts=1;
249 this->config_use_fontconfig=1;
251 this->parameters = p;
253 /* configure device */
255 if(!strcmp(p->name,"forceType0Fonts")) {
256 this->forceType0Fonts = atoi(p->value);
257 } else if(!strcmp(p->name,"fontconfig")) {
258 this->config_use_fontconfig = atoi(p->value);
264 void GFXOutputDev::setDevice(gfxdevice_t*dev)
266 parameter_t*p = this->parameters;
268 /* TODO: get rid of this */
272 this->device->setparameter(this->device, p->name, p->value);
278 void GFXOutputDev::setMove(int x,int y)
280 this->user_movex = x;
281 this->user_movey = y;
284 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
286 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
287 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
289 this->user_clipx1 = x1;
290 this->user_clipy1 = y1;
291 this->user_clipx2 = x2;
292 this->user_clipy2 = y2;
295 static char*getFontID(GfxFont*font)
297 Ref*ref = font->getID();
298 GString*gstr = font->getName();
299 char* fname = gstr==0?0:gstr->getCString();
302 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
304 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
309 static char*getFontName(GfxFont*font)
312 GString*gstr = font->getName();
313 char* fname = gstr==0?0:gstr->getCString();
317 sprintf(buf, "UFONT%d", r->num);
318 fontid = strdup(buf);
320 fontid = strdup(fname);
324 char* plus = strchr(fontid, '+');
325 if(plus && plus < &fontid[strlen(fontid)-1]) {
326 fontname = strdup(plus+1);
328 fontname = strdup(fontid);
334 static char mybuf[1024];
335 static char* gfxstate2str(GfxState *state)
339 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
346 if(state->getX1()!=0.0)
347 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
348 if(state->getY1()!=0.0)
349 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
350 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
351 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
352 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
353 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
354 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
355 state->getFillColor()->c[0], state->getFillColor()->c[1]);
356 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
357 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
358 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
359 state->getFillColor()->c[0], state->getFillColor()->c[1],
360 state->getFillColor()->c[2], state->getFillColor()->c[3],
361 state->getFillColor()->c[4], state->getFillColor()->c[5],
362 state->getFillColor()->c[6], state->getFillColor()->c[7]);
363 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
364 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
365 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
366 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
367 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
368 state->getFillRGB(&rgb);
369 if(rgb.r || rgb.g || rgb.b)
370 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
371 state->getStrokeRGB(&rgb);
372 if(rgb.r || rgb.g || rgb.b)
373 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
374 if(state->getFillColorSpace()->getNComps()>1)
375 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
376 if(state->getStrokeColorSpace()->getNComps()>1)
377 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
378 if(state->getFillPattern())
379 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
380 if(state->getStrokePattern())
381 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
383 if(state->getFillOpacity()!=1.0)
384 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
385 if(state->getStrokeOpacity()!=1.0)
386 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
388 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
393 state->getLineDash(&dash, &length, &start);
397 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
398 for(t=0;t<length;t++) {
399 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
401 bufpos+=sprintf(bufpos,"]");
404 if(state->getFlatness()!=1)
405 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
406 if(state->getLineJoin()!=0)
407 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
408 if(state->getLineJoin()!=0)
409 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
410 if(state->getLineJoin()!=0)
411 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
413 if(state->getFont() && getFontID(state->getFont()))
414 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
415 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
416 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
417 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
418 if(state->getCharSpace())
419 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
420 if(state->getWordSpace())
421 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
422 if(state->getHorizScaling()!=1.0)
423 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
424 if(state->getLeading())
425 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
427 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
428 if(state->getRender())
429 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
430 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
431 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
432 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
433 if(state->getLineX())
434 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
435 if(state->getLineY())
436 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
437 bufpos+=sprintf(bufpos," ");
441 static void dumpFontInfo(char*loglevel, GfxFont*font);
442 static int lastdumps[1024];
443 static int lastdumppos = 0;
448 static void showFontError(GfxFont*font, int nr)
452 for(t=0;t<lastdumppos;t++)
453 if(lastdumps[t] == r->num)
457 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
458 lastdumps[lastdumppos++] = r->num;
460 msg("<warning> The following font caused problems:");
462 msg("<warning> The following font caused problems (substituting):");
464 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
465 dumpFontInfo("<warning>", font);
468 static void dumpFontInfo(char*loglevel, GfxFont*font)
470 char* id = getFontID(font);
471 char* name = getFontName(font);
472 Ref* r=font->getID();
473 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
475 GString*gstr = font->getTag();
477 msg("%s| Tag: %s\n", loglevel, id);
479 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
481 GfxFontType type=font->getType();
483 case fontUnknownType:
484 msg("%s| Type: unknown\n",loglevel);
487 msg("%s| Type: 1\n",loglevel);
490 msg("%s| Type: 1C\n",loglevel);
493 msg("%s| Type: 3\n",loglevel);
496 msg("%s| Type: TrueType\n",loglevel);
499 msg("%s| Type: CIDType0\n",loglevel);
502 msg("%s| Type: CIDType0C\n",loglevel);
505 msg("%s| Type: CIDType2\n",loglevel);
510 GBool embedded = font->getEmbeddedFontID(&embRef);
512 if(font->getEmbeddedFontName()) {
513 embeddedName = font->getEmbeddedFontName()->getCString();
516 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
518 gstr = font->getExtFontFile();
520 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
522 // Get font descriptor flags.
523 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
524 if(font->isSerif()) msg("%s| is serif\n", loglevel);
525 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
526 if(font->isItalic()) msg("%s| is italic\n", loglevel);
527 if(font->isBold()) msg("%s| is bold\n", loglevel);
533 //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");}
534 //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");}
537 void dump_outline(gfxline_t*line)
540 if(line->type == gfx_moveTo) {
541 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
542 } else if(line->type == gfx_lineTo) {
543 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
544 } else if(line->type == gfx_splineTo) {
545 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
551 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
553 int num = path->getNumSubpaths();
556 double lastx=0,lasty=0,posx=0,posy=0;
559 msg("<warning> empty path");
563 gfxdrawer_target_gfxline(&draw);
565 for(t = 0; t < num; t++) {
566 GfxSubpath *subpath = path->getSubpath(t);
567 int subnum = subpath->getNumPoints();
568 double bx=0,by=0,cx=0,cy=0;
570 for(s=0;s<subnum;s++) {
573 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
578 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
579 draw.lineTo(&draw, lastx, lasty);
581 draw.moveTo(&draw, x,y);
586 } else if(subpath->getCurve(s) && cpos==0) {
590 } else if(subpath->getCurve(s) && cpos==1) {
598 draw.lineTo(&draw, x,y);
600 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
607 /* fix non-closed lines */
608 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
609 draw.lineTo(&draw, lastx, lasty);
611 gfxline_t*result = (gfxline_t*)draw.result(&draw);
613 gfxline_optimize(result);
618 /*----------------------------------------------------------------------------
619 * Primitive Graphic routines
620 *----------------------------------------------------------------------------*/
622 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
624 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
625 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
626 double miterLimit = state->getMiterLimit();
627 double width = state->getTransformedLineWidth();
630 double opaq = state->getStrokeOpacity();
632 state->getFillRGB(&rgb);
634 state->getStrokeRGB(&rgb);
636 col.r = colToByte(rgb.r);
637 col.g = colToByte(rgb.g);
638 col.b = colToByte(rgb.b);
639 col.a = (unsigned char)(opaq*255);
641 gfx_capType capType = gfx_capRound;
642 if(lineCap == 0) capType = gfx_capButt;
643 else if(lineCap == 1) capType = gfx_capRound;
644 else if(lineCap == 2) capType = gfx_capSquare;
646 gfx_joinType joinType = gfx_joinRound;
647 if(lineJoin == 0) joinType = gfx_joinMiter;
648 else if(lineJoin == 1) joinType = gfx_joinRound;
649 else if(lineJoin == 2) joinType = gfx_joinBevel;
652 double dashphase = 0;
654 state->getLineDash(&ldash, &dashnum, &dashphase);
658 if(dashnum && ldash) {
659 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
663 msg("<trace> %d dashes", dashnum);
664 msg("<trace> | phase: %f", dashphase);
665 for(t=0;t<dashnum;t++) {
667 msg("<trace> | d%-3d: %f", t, ldash[t]);
670 if(getLogLevel() >= LOGLEVEL_TRACE) {
674 line2 = gfxtool_dash_line(line, dash, dashphase);
677 msg("<trace> After dashing:");
680 if(getLogLevel() >= LOGLEVEL_TRACE) {
681 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
683 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
684 lineCap==0?"butt": (lineJoin==1?"round":"square"),
686 col.r,col.g,col.b,col.a
691 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
692 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
698 gfxcolor_t getFillColor(GfxState * state)
701 double opaq = state->getFillOpacity();
702 state->getFillRGB(&rgb);
704 col.r = colToByte(rgb.r);
705 col.g = colToByte(rgb.g);
706 col.b = colToByte(rgb.b);
707 col.a = (unsigned char)(opaq*255);
711 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
713 gfxcolor_t col = getFillColor(state);
715 if(getLogLevel() >= LOGLEVEL_TRACE) {
716 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
719 device->fill(device, line, &col);
722 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
724 if(getLogLevel() >= LOGLEVEL_TRACE) {
725 msg("<trace> clip\n");
729 device->startclip(device, line);
730 states[statepos].clipping++;
733 void GFXOutputDev::clip(GfxState *state)
735 GfxPath * path = state->getPath();
736 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
737 clipToGfxLine(state, line);
741 void GFXOutputDev::eoClip(GfxState *state)
743 GfxPath * path = state->getPath();
744 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
746 if(getLogLevel() >= LOGLEVEL_TRACE) {
747 msg("<trace> eoclip\n");
751 device->startclip(device, line);
752 states[statepos].clipping++;
756 void GFXOutputDev::endframe()
759 device->endclip(device);
763 device->endpage(device);
766 void GFXOutputDev::finish()
770 device->endclip(device);
776 GFXOutputDev::~GFXOutputDev()
781 free(this->pages); this->pages = 0;
784 fontlist_t*l = this->fontlist;
786 fontlist_t*next = l->next;
788 gfxfont_free(l->font);
789 free(l->filename);l->filename=0;
795 GBool GFXOutputDev::upsideDown()
799 GBool GFXOutputDev::useDrawChar()
804 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
805 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
807 #define RENDER_FILL 0
808 #define RENDER_STROKE 1
809 #define RENDER_FILLSTROKE 2
810 #define RENDER_INVISIBLE 3
811 #define RENDER_CLIP 4
813 static char tmp_printstr[4096];
814 char* makeStringPrintable(char*str)
816 int len = strlen(str);
831 tmp_printstr[len++] = '.';
832 tmp_printstr[len++] = '.';
833 tmp_printstr[len++] = '.';
835 tmp_printstr[len] = 0;
840 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
845 /* find out char name from unicode index
846 TODO: should be precomputed
848 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
849 if(nameToUnicodeTab[t].u == u) {
850 uniname = nameToUnicodeTab[t].name;
858 for(t=0;t<font->num_glyphs;t++) {
859 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
860 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
864 /* if we didn't find the character, maybe
865 we can find the capitalized version */
866 for(t=0;t<font->num_glyphs;t++) {
867 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
868 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
876 for(t=0;t<font->num_glyphs;t++) {
877 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
878 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
882 /* if we didn't find the character, maybe
883 we can find the capitalized version */
884 for(t=0;t<font->num_glyphs;t++) {
885 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
886 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
892 /* try to use the unicode id */
893 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
894 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
895 return font->unicode2glyph[u];
898 if(charnr>=0 && charnr<font->num_glyphs) {
899 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
907 void GFXOutputDev::beginString(GfxState *state, GString *s)
909 int render = state->getRender();
910 if(current_text_stroke) {
911 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
914 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
915 double m11,m21,m12,m22;
916 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
917 state->getFontTransMat(&m11, &m12, &m21, &m22);
918 m11 *= state->getHorizScaling();
919 m21 *= state->getHorizScaling();
921 this->current_font_matrix.m00 = m11 / 1024.0;
922 this->current_font_matrix.m01 = m12 / 1024.0;
923 this->current_font_matrix.m10 = -m21 / 1024.0;
924 this->current_font_matrix.m11 = -m22 / 1024.0;
925 this->current_font_matrix.tx = 0;
926 this->current_font_matrix.ty = 0;
928 gfxmatrix_t m = this->current_font_matrix;
930 /*if(render != 3 && render != 0)
931 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
932 states[statepos].textRender = render;
935 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
936 double dx, double dy,
937 double originX, double originY,
938 CharCode c, int nBytes, Unicode *_u, int uLen)
940 int render = state->getRender();
941 // check for invisible text -- this is used by Acrobat Capture
943 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
947 if(states[statepos].textRender != render)
948 msg("<error> Internal error: drawChar.render!=beginString.render");
950 gfxcolor_t col = getFillColor(state);
952 Gushort *CIDToGIDMap = 0;
953 GfxFont*font = state->getFont();
955 if(font->getType() == fontType3) {
956 /* type 3 chars are passed as graphics */
957 msg("<debug> type3 char at %f/%f", x, y);
967 if(font->isCIDFont()) {
968 GfxCIDFont*cfont = (GfxCIDFont*)font;
970 if(font->getType() == fontCIDType2)
971 CIDToGIDMap = cfont->getCIDToGID();
974 font8 = (Gfx8BitFont*)font;
975 char**enc=font8->getEncoding();
979 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);
982 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);
988 charid = getGfxCharID(current_gfxfont, c, name, u);
990 charid = getGfxCharID(current_gfxfont, c, name, -1);
993 /* multiple unicodes- should usually map to a ligature.
994 if the ligature doesn't exist, we need to draw
995 the characters one-by-one. */
997 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
998 for(t=0;t<uLen;t++) {
999 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1005 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1006 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1010 gfxmatrix_t m = this->current_font_matrix;
1011 state->transform(x, y, &m.tx, &m.ty);
1012 m.tx += user_movex + clipmovex;
1013 m.ty += user_movey + clipmovey;
1015 if(render == RENDER_FILL) {
1016 device->drawchar(device, current_gfxfont, charid, &col, &m);
1018 msg("<debug> Drawing glyph %d as shape", charid);
1020 msg("<notice> Some texts will be rendered as shape");
1023 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1024 gfxline_t*tglyph = gfxline_clone(glyph);
1025 gfxline_transform(tglyph, &m);
1026 if((render&3) != RENDER_INVISIBLE) {
1027 gfxline_t*add = gfxline_clone(tglyph);
1028 current_text_stroke = gfxline_append(current_text_stroke, add);
1030 if(render&RENDER_CLIP) {
1031 gfxline_t*add = gfxline_clone(tglyph);
1032 current_text_clip = gfxline_append(current_text_clip, add);
1034 gfxline_free(tglyph);
1038 void GFXOutputDev::endString(GfxState *state)
1040 int render = state->getRender();
1041 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1042 if(states[statepos].textRender != render)
1043 msg("<error> Internal error: drawChar.render!=beginString.render");
1045 if(current_text_stroke) {
1046 /* fillstroke and stroke text rendering objects we can process right
1047 now (as there may be texts of other rendering modes in this
1048 text object)- clipping objects have to wait until endTextObject,
1050 device->setparameter(device, "mark","TXT");
1051 if((render&3) == RENDER_FILL) {
1052 fillGfxLine(state, current_text_stroke);
1053 gfxline_free(current_text_stroke);
1054 current_text_stroke = 0;
1055 } else if((render&3) == RENDER_FILLSTROKE) {
1056 fillGfxLine(state, current_text_stroke);
1057 strokeGfxline(state, current_text_stroke);
1058 gfxline_free(current_text_stroke);
1059 current_text_stroke = 0;
1060 } else if((render&3) == RENDER_STROKE) {
1061 strokeGfxline(state, current_text_stroke);
1062 gfxline_free(current_text_stroke);
1063 current_text_stroke = 0;
1065 device->setparameter(device, "mark","");
1069 void GFXOutputDev::endTextObject(GfxState *state)
1071 int render = state->getRender();
1072 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1073 if(states[statepos].textRender != render)
1074 msg("<error> Internal error: drawChar.render!=beginString.render");
1076 if(current_text_clip) {
1077 device->setparameter(device, "mark","TXT");
1078 clipToGfxLine(state, current_text_clip);
1079 device->setparameter(device, "mark","");
1080 gfxline_free(current_text_clip);
1081 current_text_clip = 0;
1085 /* the logic seems to be as following:
1086 first, beginType3Char is called, with the charcode and the coordinates.
1087 if this function returns true, it already knew about the char and has now drawn it.
1088 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1089 the all draw operations until endType3Char are part of the char (which in this moment is
1090 at the position first passed to beginType3Char). the char ends with endType3Char.
1092 The drawing operations between beginType3Char and endType3Char are somewhat different to
1093 the normal ones. For example, the fillcolor equals the stroke color.
1096 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1098 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1100 /* the character itself is going to be passed using the draw functions */
1101 return gFalse; /* gTrue= is_in_cache? */
1104 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1105 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1107 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1108 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1112 void GFXOutputDev::endType3Char(GfxState *state)
1115 msg("<debug> endType3Char");
1118 void GFXOutputDev::startFrame(int width, int height)
1120 if(outer_clip_box) {
1121 device->endclip(device);
1125 device->startpage(device, width, height);
1126 this->width = width;
1127 this->height = height;
1130 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1132 this->currentpage = pageNum;
1134 int rot = doc->getPageRotate(1);
1137 gfxline_t clippath[5];
1139 white.r = white.g = white.b = white.a = 255;
1141 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1142 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1143 Use CropBox, not MediaBox, as page size
1150 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1151 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1153 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1154 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1156 this->clipmovex = -(int)x1;
1157 this->clipmovey = -(int)y1;
1159 /* apply user clip box */
1160 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1161 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1162 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1163 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1164 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1165 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1168 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1170 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);
1172 msg("<verbose> page is rotated %d degrees\n", rot);
1174 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1175 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1176 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1177 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1178 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1179 device->startclip(device, clippath); outer_clip_box = 1;
1180 device->fill(device, clippath, &white);
1183 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1185 double x1, y1, x2, y2, w;
1186 gfxline_t points[5];
1189 msg("<debug> drawlink\n");
1191 link->getRect(&x1, &y1, &x2, &y2);
1192 cvtUserToDev(x1, y1, &x, &y);
1193 points[0].type = gfx_moveTo;
1194 points[0].x = points[4].x = x + user_movex + clipmovex;
1195 points[0].y = points[4].y = y + user_movey + clipmovey;
1196 points[0].next = &points[1];
1197 cvtUserToDev(x2, y1, &x, &y);
1198 points[1].type = gfx_lineTo;
1199 points[1].x = x + user_movex + clipmovex;
1200 points[1].y = y + user_movey + clipmovey;
1201 points[1].next = &points[2];
1202 cvtUserToDev(x2, y2, &x, &y);
1203 points[2].type = gfx_lineTo;
1204 points[2].x = x + user_movex + clipmovex;
1205 points[2].y = y + user_movey + clipmovey;
1206 points[2].next = &points[3];
1207 cvtUserToDev(x1, y2, &x, &y);
1208 points[3].type = gfx_lineTo;
1209 points[3].x = x + user_movex + clipmovex;
1210 points[3].y = y + user_movey + clipmovey;
1211 points[3].next = &points[4];
1212 cvtUserToDev(x1, y1, &x, &y);
1213 points[4].type = gfx_lineTo;
1214 points[4].x = x + user_movex + clipmovex;
1215 points[4].y = y + user_movey + clipmovey;
1218 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1219 points[0].x, points[0].y,
1220 points[1].x, points[1].y,
1221 points[2].x, points[2].y,
1222 points[3].x, points[3].y);
1224 LinkAction*action=link->getAction();
1230 msg("<trace> drawlink action=%d\n", action->getKind());
1231 switch(action->getKind())
1235 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1236 LinkDest *dest=NULL;
1237 if (ha->getDest()==NULL)
1238 dest=catalog->findDest(ha->getNamedDest());
1239 else dest=ha->getDest();
1241 if (dest->isPageRef()){
1242 Ref pageref=dest->getPageRef();
1243 page=catalog->findPage(pageref.num,pageref.gen);
1245 else page=dest->getPageNum();
1246 sprintf(buf, "%d", page);
1253 LinkGoToR*l = (LinkGoToR*)action;
1254 GString*g = l->getFileName();
1256 s = strdup(g->getCString());
1258 /* if the GoToR link has no filename, then
1259 try to find a refernce in the *local*
1261 GString*g = l->getNamedDest();
1263 s = strdup(g->getCString());
1269 LinkNamed*l = (LinkNamed*)action;
1270 GString*name = l->getName();
1272 s = strdup(name->lowerCase()->getCString());
1273 named = name->getCString();
1276 if(strstr(s, "next") || strstr(s, "forward"))
1278 page = currentpage + 1;
1280 else if(strstr(s, "prev") || strstr(s, "back"))
1282 page = currentpage - 1;
1284 else if(strstr(s, "last") || strstr(s, "end"))
1286 if(pages && pagepos>0)
1287 page = pages[pagepos-1];
1289 else if(strstr(s, "first") || strstr(s, "top"))
1297 case actionLaunch: {
1299 LinkLaunch*l = (LinkLaunch*)action;
1300 GString * str = new GString(l->getFileName());
1301 GString * params = l->getParams();
1303 str->append(params);
1304 s = strdup(str->getCString());
1311 LinkURI*l = (LinkURI*)action;
1312 GString*g = l->getURI();
1314 url = g->getCString();
1319 case actionUnknown: {
1321 LinkUnknown*l = (LinkUnknown*)action;
1326 msg("<error> Unknown link type!\n");
1331 if(!s) s = strdup("-?-");
1333 msg("<trace> drawlink s=%s\n", s);
1335 if(!linkinfo && (page || s))
1337 msg("<notice> File contains links");
1345 for(t=1;t<=pagepos;t++) {
1346 if(pages[t]==page) {
1355 sprintf(buf, "page%d", lpage);
1356 device->drawlink(device, points, buf);
1360 device->drawlink(device, points, s);
1363 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1367 void GFXOutputDev::saveState(GfxState *state) {
1368 dbg("saveState");dbgindent+=2;
1370 msg("<trace> saveState\n");
1373 msg("<error> Too many nested states in pdf.");
1377 states[statepos].textRender = states[statepos-1].textRender;
1378 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1379 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1380 states[statepos].clipping = 0;
1383 void GFXOutputDev::restoreState(GfxState *state) {
1384 dbgindent-=2; dbg("restoreState");
1387 msg("<error> Invalid restoreState");
1390 msg("<trace> restoreState");
1391 if(states[statepos].softmask) {
1392 clearSoftMask(state);
1395 while(states[statepos].clipping) {
1396 device->endclip(device);
1397 states[statepos].clipping--;
1402 char* GFXOutputDev::searchFont(char*name)
1406 int is_standard_font = 0;
1408 msg("<verbose> SearchFont(%s)", name);
1410 /* see if it is a pdf standard font */
1411 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1413 if(!strcmp(name, pdf2t1map[i].pdffont))
1415 name = pdf2t1map[i].filename;
1416 is_standard_font = 1;
1420 /* look in all font files */
1421 for(i=0;i<fontnum;i++)
1423 if(strstr(fonts[i].filename, name))
1425 if(!fonts[i].used) {
1428 if(!is_standard_font)
1429 msg("<notice> Using %s for %s", fonts[i].filename, name);
1431 return strdup(fonts[i].filename);
1437 void GFXOutputDev::updateLineWidth(GfxState *state)
1439 double width = state->getTransformedLineWidth();
1440 //swfoutput_setlinewidth(&device, width);
1443 void GFXOutputDev::updateLineCap(GfxState *state)
1445 int c = state->getLineCap();
1448 void GFXOutputDev::updateLineJoin(GfxState *state)
1450 int j = state->getLineJoin();
1453 void GFXOutputDev::updateFillColor(GfxState *state)
1456 double opaq = state->getFillOpacity();
1457 state->getFillRGB(&rgb);
1459 void GFXOutputDev::updateFillOpacity(GfxState *state)
1462 double opaq = state->getFillOpacity();
1463 state->getFillRGB(&rgb);
1464 dbg("update fillopaq %f", opaq);
1466 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1468 double opaq = state->getFillOpacity();
1469 dbg("update strokeopaq %f", opaq);
1471 void GFXOutputDev::updateFillOverprint(GfxState *state)
1473 double opaq = state->getFillOverprint();
1474 dbg("update filloverprint %f", opaq);
1476 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1478 double opaq = state->getStrokeOverprint();
1479 dbg("update strokeoverprint %f", opaq);
1481 void GFXOutputDev::updateTransfer(GfxState *state)
1483 dbg("update transfer");
1487 void GFXOutputDev::updateStrokeColor(GfxState *state)
1490 double opaq = state->getStrokeOpacity();
1491 state->getStrokeRGB(&rgb);
1494 void FoFiWrite(void *stream, char *data, int len)
1496 int ret = fwrite(data, len, 1, (FILE*)stream);
1499 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1501 char*tmpFileName = NULL;
1507 Object refObj, strObj;
1509 tmpFileName = mktmpname(namebuf);
1512 ret = font->getEmbeddedFontID(&embRef);
1514 msg("<verbose> Didn't get embedded font id");
1515 /* not embedded- the caller should now search the font
1516 directories for this font */
1520 f = fopen(tmpFileName, "wb");
1522 msg("<error> Couldn't create temporary Type 1 font file");
1526 /*if(font->isCIDFont()) {
1527 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1528 GString c = cidFont->getCollection();
1529 msg("<notice> Collection: %s", c.getCString());
1532 //if (font->getType() == fontType1C) {
1533 if (0) { //font->getType() == fontType1C) {
1534 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1536 msg("<error> Couldn't read embedded font file");
1539 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1541 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1542 //cvt->convertToCIDType0("test", f);
1543 //cvt->convertToType0("test", f);
1546 } else if(font->getType() == fontTrueType) {
1547 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1548 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1550 msg("<error> Couldn't read embedded font file");
1553 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1554 cvt->writeTTF(FoFiWrite, f);
1558 font->getEmbeddedFontID(&embRef);
1559 refObj.initRef(embRef.num, embRef.gen);
1560 refObj.fetch(ref, &strObj);
1562 strObj.streamReset();
1567 f4[t] = strObj.streamGetChar();
1568 f4c[t] = (char)f4[t];
1573 if(!strncmp(f4c, "true", 4)) {
1574 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1575 Change this on the fly */
1576 f4[0] = f4[2] = f4[3] = 0;
1584 while ((c = strObj.streamGetChar()) != EOF) {
1588 strObj.streamClose();
1593 return strdup(tmpFileName);
1596 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1598 char*name = getFontName(gfxFont);
1602 if(!this->config_use_fontconfig)
1605 #ifdef HAVE_FONTCONFIG
1606 FcPattern *pattern, *match;
1610 static int fcinitcalled = false;
1612 msg("<debug> searchForSuitableFont(%s)", name);
1614 // call init ony once
1615 if (!fcinitcalled) {
1616 msg("<debug> Initializing FontConfig...");
1617 fcinitcalled = true;
1619 msg("<debug> FontConfig Initialization failed. Disabling.");
1620 config_use_fontconfig = 0;
1623 msg("<debug> ...initialized FontConfig");
1626 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1627 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1628 if (gfxFont->isItalic()) // check for italic
1629 msg("<debug> FontConfig: Adding Italic Slant");
1630 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1631 if (gfxFont->isBold()) // check for bold
1632 msg("<debug> FontConfig: Adding Bold Weight");
1633 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1635 msg("<debug> FontConfig: Try to match...");
1636 // configure and match using the original font name
1637 FcConfigSubstitute(0, pattern, FcMatchPattern);
1638 FcDefaultSubstitute(pattern);
1639 match = FcFontMatch(0, pattern, &result);
1641 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1642 msg("<debug> FontConfig: family=%s", (char*)v);
1643 // if we get an exact match
1644 if (strcmp((char *)v, name) == 0) {
1645 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1646 filename = strdup((char*)v); // mem leak
1647 char *nfn = strrchr(filename, '/');
1648 if(nfn) fontname = strdup(nfn+1);
1649 else fontname = filename;
1651 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1653 // initialize patterns
1654 FcPatternDestroy(pattern);
1655 FcPatternDestroy(match);
1657 // now match against serif etc.
1658 if (gfxFont->isSerif()) {
1659 msg("<debug> FontConfig: Create Serif Family Pattern");
1660 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1661 } else if (gfxFont->isFixedWidth()) {
1662 msg("<debug> FontConfig: Create Monospace Family Pattern");
1663 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1665 msg("<debug> FontConfig: Create Sans Family Pattern");
1666 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1670 if (gfxFont->isItalic()) {
1671 msg("<debug> FontConfig: Adding Italic Slant");
1672 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1675 if (gfxFont->isBold()) {
1676 msg("<debug> FontConfig: Adding Bold Weight");
1677 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1680 msg("<debug> FontConfig: Try to match... (2)");
1681 // configure and match using serif etc
1682 FcConfigSubstitute (0, pattern, FcMatchPattern);
1683 FcDefaultSubstitute (pattern);
1684 match = FcFontMatch (0, pattern, &result);
1686 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1687 filename = strdup((char*)v); // mem leak
1688 char *nfn = strrchr(filename, '/');
1689 if(nfn) fontname = strdup(nfn+1);
1690 else fontname = filename;
1692 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1696 //printf("FONTCONFIG: pattern");
1697 //FcPatternPrint(pattern);
1698 //printf("FONTCONFIG: match");
1699 //FcPatternPrint(match);
1701 FcPatternDestroy(pattern);
1702 FcPatternDestroy(match);
1704 pdfswf_addfont(filename);
1711 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1713 char*fontname = 0, *filename = 0;
1714 msg("<notice> substituteFont(%s)", oldname);
1716 if(!(fontname = searchForSuitableFont(gfxFont))) {
1717 fontname = "Times-Roman";
1719 filename = searchFont(fontname);
1721 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1725 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1726 msg("<fatal> Too many fonts in file.");
1730 substitutesource[substitutepos] = strdup(oldname); //mem leak
1731 substitutetarget[substitutepos] = fontname;
1732 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1735 return strdup(filename); //mem leak
1738 void unlinkfont(char* filename)
1745 if(!strncmp(&filename[l-4],".afm",4)) {
1746 memcpy(&filename[l-4],".pfb",4);
1748 memcpy(&filename[l-4],".pfa",4);
1750 memcpy(&filename[l-4],".afm",4);
1753 if(!strncmp(&filename[l-4],".pfa",4)) {
1754 memcpy(&filename[l-4],".afm",4);
1756 memcpy(&filename[l-4],".pfa",4);
1759 if(!strncmp(&filename[l-4],".pfb",4)) {
1760 memcpy(&filename[l-4],".afm",4);
1762 memcpy(&filename[l-4],".pfb",4);
1767 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1773 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1776 fontlist_t*last=0,*l = this->fontlist;
1779 msg("<error> Internal Error: FontID is null");
1781 /* TODO: should this be part of the state? */
1784 if(!strcmp(l->font->id, id)) {
1785 current_gfxfont = l->font;
1787 device->addfont(device, current_gfxfont);
1792 if(!filename) return 0;
1794 /* A font size of e.g. 9 means the font will be scaled down by
1795 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1796 we have to divide 0.05 by (fontsize/1024)
1798 double quality = (1024 * 0.05) / maxSize;
1800 msg("<verbose> Loading %s...", filename);
1801 font = gfxfont_load(id, filename, quality);
1803 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1806 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1810 l->filename = strdup(filename);
1812 current_gfxfont = l->font;
1818 device->addfont(device, current_gfxfont);
1822 void GFXOutputDev::updateFont(GfxState *state)
1824 GfxFont*gfxFont = state->getFont();
1830 char * fontid = getFontID(gfxFont);
1831 char * fontname = getFontName(gfxFont);
1833 double maxSize = 1.0;
1836 maxSize = this->info->getMaximumFontSize(fontid);
1840 /* first, look if we substituted this font before-
1841 this way, we don't initialize the T1 Fonts
1843 for(t=0;t<substitutepos;t++) {
1844 if(!strcmp(fontid, substitutesource[t])) {
1845 free(fontid);fontid=0;
1846 fontid = strdup(substitutetarget[t]);
1851 /* second, see if this is a font which was used before-
1852 if so, we are done */
1853 if(setGfxFont(fontid, fontname, 0, 0)) {
1858 /* if(swfoutput_queryfont(&device, fontid))
1859 swfoutput_setfont(&device, fontid, 0);
1861 msg("<debug> updateFont(%s) [cached]", fontid);
1865 // look for Type 3 font
1866 if (gfxFont->getType() == fontType3) {
1868 type3Warning = gTrue;
1869 showFontError(gfxFont, 2);
1876 /* now either load the font, or find a substitution */
1879 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1884 (gfxFont->getType() == fontType1 ||
1885 gfxFont->getType() == fontType1C ||
1886 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1887 gfxFont->getType() == fontTrueType ||
1888 gfxFont->getType() == fontCIDType2
1891 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1892 if(!fileName) showFontError(gfxFont,0);
1895 fileName = searchFont(fontname);
1896 if(!fileName) showFontError(gfxFont,0);
1899 char * fontname = getFontName(gfxFont);
1900 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1903 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1905 msg("<warning> Try specifying one or more font directories");
1907 fileName = substituteFont(gfxFont, fontid);
1910 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1911 msg("<notice> Font is now %s (%s)", fontid, fileName);
1915 msg("<error> Couldn't set font %s\n", fontid);
1921 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1922 dumpFontInfo("<verbose>", gfxFont);
1924 //swfoutput_setfont(&device, fontid, fileName);
1926 if(!setGfxFont(fontid, fontname, 0, 0)) {
1927 setGfxFont(fontid, fontname, fileName, maxSize);
1931 unlinkfont(fileName);
1941 #define SQR(x) ((x)*(x))
1943 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1945 if((newwidth<2 || newheight<2) ||
1946 (width<=newwidth || height<=newheight))
1948 unsigned char*newdata;
1950 newdata= (unsigned char*)malloc(newwidth*newheight);
1952 double fx = (double)(width)/newwidth;
1953 double fy = (double)(height)/newheight;
1955 int blocksize = (int)(8192/(fx*fy));
1956 int r = 8192*256/palettesize;
1957 for(x=0;x<newwidth;x++) {
1958 double ex = px + fx;
1959 int fromx = (int)px;
1961 int xweight1 = (int)(((fromx+1)-px)*256);
1962 int xweight2 = (int)((ex-tox)*256);
1964 for(y=0;y<newheight;y++) {
1965 double ey = py + fy;
1966 int fromy = (int)py;
1968 int yweight1 = (int)(((fromy+1)-py)*256);
1969 int yweight2 = (int)((ey-toy)*256);
1972 for(xx=fromx;xx<=tox;xx++)
1973 for(yy=fromy;yy<=toy;yy++) {
1974 int b = 1-data[width*yy+xx];
1976 if(xx==fromx) weight = (weight*xweight1)/256;
1977 if(xx==tox) weight = (weight*xweight2)/256;
1978 if(yy==fromy) weight = (weight*yweight1)/256;
1979 if(yy==toy) weight = (weight*yweight2)/256;
1982 //if(a) a=(palettesize-1)*r/blocksize;
1983 newdata[y*newwidth+x] = (a*blocksize)/r;
1991 #define IMAGE_TYPE_JPEG 0
1992 #define IMAGE_TYPE_LOSSLESS 1
1994 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1995 double x1,double y1,
1996 double x2,double y2,
1997 double x3,double y3,
1998 double x4,double y4, int type)
2000 gfxcolor_t*newpic=0;
2002 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2003 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2005 gfxline_t p1,p2,p3,p4,p5;
2006 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2007 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2008 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2009 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2010 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2012 {p1.x = (int)(p1.x*20)/20.0;
2013 p1.y = (int)(p1.y*20)/20.0;
2014 p2.x = (int)(p2.x*20)/20.0;
2015 p2.y = (int)(p2.y*20)/20.0;
2016 p3.x = (int)(p3.x*20)/20.0;
2017 p3.y = (int)(p3.y*20)/20.0;
2018 p4.x = (int)(p4.x*20)/20.0;
2019 p4.y = (int)(p4.y*20)/20.0;
2020 p5.x = (int)(p5.x*20)/20.0;
2021 p5.y = (int)(p5.y*20)/20.0;
2028 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2029 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2034 img.data = (gfxcolor_t*)data;
2038 if(type == IMAGE_TYPE_JPEG)
2039 /* TODO: pass image_dpi to device instead */
2040 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2042 gfxline_show(&p1,stdout);
2044 printf("%.2f %.2f %.2f\n", m.m00, m.m10, m.tx);
2045 printf("%.2f %.2f %.2f\n", m.m01, m.m11, m.ty);
2046 dev->fillbitmap(dev, &p1, &img, &m, 0);
2049 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2050 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2052 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2055 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2056 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2058 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2062 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2063 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2064 GBool inlineImg, int mask, int*maskColors,
2065 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2067 double x1,y1,x2,y2,x3,y3,x4,y4;
2068 ImageStream *imgStr;
2073 unsigned char* maskbitmap = 0;
2076 ncomps = colorMap->getNumPixelComps();
2077 bits = colorMap->getBits();
2082 unsigned char buf[8];
2083 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2085 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2086 imgMaskStr->reset();
2087 unsigned char pal[256];
2088 int n = 1 << colorMap->getBits();
2093 maskColorMap->getGray(pixBuf, &gray);
2094 pal[t] = colToByte(gray);
2096 for (y = 0; y < maskHeight; y++) {
2097 for (x = 0; x < maskWidth; x++) {
2098 imgMaskStr->getPixel(buf);
2099 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2104 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2105 imgMaskStr->reset();
2106 for (y = 0; y < maskHeight; y++) {
2107 for (x = 0; x < maskWidth; x++) {
2108 imgMaskStr->getPixel(buf);
2110 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2118 imgStr = new ImageStream(str, width, ncomps,bits);
2121 if(!width || !height || (height<=1 && width<=1))
2123 msg("<verbose> Ignoring %d by %d image", width, height);
2124 unsigned char buf[8];
2126 for (y = 0; y < height; ++y)
2127 for (x = 0; x < width; ++x) {
2128 imgStr->getPixel(buf);
2136 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2137 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2138 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2139 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2141 if(!pbminfo && !(str->getKind()==strDCT)) {
2143 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2147 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2149 if(!jpeginfo && (str->getKind()==strDCT)) {
2150 msg("<notice> file contains jpeg pictures");
2156 unsigned char buf[8];
2158 unsigned char*pic = new unsigned char[width*height];
2159 gfxcolor_t pal[256];
2161 state->getFillRGB(&rgb);
2163 memset(pal,255,sizeof(pal));
2164 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2165 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2166 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2167 pal[0].a = 255; pal[1].a = 0;
2170 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2171 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2172 for (y = 0; y < height; ++y)
2173 for (x = 0; x < width; ++x)
2175 imgStr->getPixel(buf);
2178 pic[width*y+x] = buf[0];
2181 /* the size of the drawn image is added to the identifier
2182 as the same image may require different bitmaps if displayed
2183 at different sizes (due to antialiasing): */
2186 unsigned char*pic2 = 0;
2189 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2198 height = realheight;
2202 /* make a black/white palette */
2204 float r = 255/(numpalette-1);
2206 for(t=0;t<numpalette;t++) {
2207 pal[t].r = colToByte(rgb.r);
2208 pal[t].g = colToByte(rgb.g);
2209 pal[t].b = colToByte(rgb.b);
2210 pal[t].a = (unsigned char)(t*r);
2214 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2215 for (y = 0; y < height; ++y) {
2216 for (x = 0; x < width; ++x) {
2217 pic2[width*y+x] = pal[pic[y*width+x]];
2220 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2224 if(maskbitmap) free(maskbitmap);
2230 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2231 gfxcolor_t*pic=new gfxcolor_t[width*height];
2232 for (y = 0; y < height; ++y) {
2233 for (x = 0; x < width; ++x) {
2234 imgStr->getPixel(pixBuf);
2235 colorMap->getRGB(pixBuf, &rgb);
2236 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2237 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2238 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2239 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2241 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2245 if(str->getKind()==strDCT)
2246 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2248 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2251 if(maskbitmap) free(maskbitmap);
2254 gfxcolor_t*pic=new gfxcolor_t[width*height];
2255 gfxcolor_t pal[256];
2256 int n = 1 << colorMap->getBits();
2258 for(t=0;t<256;t++) {
2260 colorMap->getRGB(pixBuf, &rgb);
2262 {/*if(maskColors && *maskColors==t) {
2263 msg("<notice> Color %d is transparent", t);
2264 if (imgData->maskColors) {
2266 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2267 if (pix[i] < imgData->maskColors[2*i] ||
2268 pix[i] > imgData->maskColors[2*i+1]) {
2283 pal[t].r = (unsigned char)(colToByte(rgb.r));
2284 pal[t].g = (unsigned char)(colToByte(rgb.g));
2285 pal[t].b = (unsigned char)(colToByte(rgb.b));
2286 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2289 for (y = 0; y < height; ++y) {
2290 for (x = 0; x < width; ++x) {
2291 imgStr->getPixel(pixBuf);
2292 pic[width*y+x] = pal[pixBuf[0]];
2294 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2298 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2302 if(maskbitmap) free(maskbitmap);
2307 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2308 int width, int height, GBool invert,
2311 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2312 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2313 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2316 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2317 int width, int height, GfxImageColorMap *colorMap,
2318 int *maskColors, GBool inlineImg)
2320 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2321 colorMap?"colorMap":"no colorMap",
2322 maskColors?"maskColors":"no maskColors",
2324 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2325 colorMap?"colorMap":"no colorMap",
2326 maskColors?"maskColors":"no maskColors",
2329 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2330 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2331 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2334 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2335 int width, int height,
2336 GfxImageColorMap *colorMap,
2337 Stream *maskStr, int maskWidth, int maskHeight,
2340 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2341 colorMap?"colorMap":"no colorMap",
2342 maskWidth, maskHeight);
2343 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2344 colorMap?"colorMap":"no colorMap",
2345 maskWidth, maskHeight);
2347 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2348 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2349 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2352 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2353 int width, int height,
2354 GfxImageColorMap *colorMap,
2356 int maskWidth, int maskHeight,
2357 GfxImageColorMap *maskColorMap)
2359 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2360 colorMap?"colorMap":"no colorMap",
2361 maskWidth, maskHeight);
2362 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2363 colorMap?"colorMap":"no colorMap",
2364 maskWidth, maskHeight);
2366 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2367 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2368 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2371 void GFXOutputDev::stroke(GfxState *state)
2375 GfxPath * path = state->getPath();
2376 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2377 strokeGfxline(state, line);
2381 void GFXOutputDev::fill(GfxState *state)
2385 GfxPath * path = state->getPath();
2386 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2387 fillGfxLine(state, line);
2391 void GFXOutputDev::eoFill(GfxState *state)
2395 GfxPath * path = state->getPath();
2396 gfxcolor_t col = getFillColor(state);
2398 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2400 if(getLogLevel() >= LOGLEVEL_TRACE) {
2401 msg("<trace> eofill\n");
2405 device->fill(device, line, &col);
2410 static char* dirseparator()
2419 void addGlobalFont(char*filename)
2422 memset(&f, 0, sizeof(fontfile_t));
2423 f.filename = filename;
2424 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2425 msg("<verbose> Adding font \"%s\".", filename);
2426 fonts[fontnum++] = f;
2428 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2432 void addGlobalLanguageDir(char*dir)
2435 globalParams = new GlobalParams("");
2437 msg("<notice> Adding %s to language pack directories", dir);
2441 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2442 strcpy(config_file, dir);
2443 strcat(config_file, dirseparator());
2444 strcat(config_file, "add-to-xpdfrc");
2446 fi = fopen(config_file, "rb");
2448 msg("<error> Could not open %s", config_file);
2451 globalParams->parseFile(new GString(config_file), fi);
2455 void addGlobalFontDir(char*dirname)
2457 #ifdef HAVE_DIRENT_H
2458 msg("<notice> Adding %s to font directories", dirname);
2459 lastfontdir = strdup(dirname);
2460 DIR*dir = opendir(dirname);
2462 msg("<warning> Couldn't open directory %s\n", dirname);
2467 ent = readdir (dir);
2471 char*name = ent->d_name;
2477 if(!strncasecmp(&name[l-4], ".pfa", 4))
2479 if(!strncasecmp(&name[l-4], ".pfb", 4))
2481 if(!strncasecmp(&name[l-4], ".ttf", 4))
2485 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2486 strcpy(fontname, dirname);
2487 strcat(fontname, dirseparator());
2488 strcat(fontname, name);
2489 addGlobalFont(fontname);
2494 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2498 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2504 this->pagebuflen = 1024;
2505 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2506 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2508 while(pdfpage >= this->pagebuflen)
2510 int oldlen = this->pagebuflen;
2511 this->pagebuflen+=1024;
2512 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2513 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2516 this->pages[pdfpage] = outputpage;
2517 if(pdfpage>this->pagepos)
2518 this->pagepos = pdfpage;
2524 double width,height;
2527 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2529 double xMin, yMin, xMax, yMax, x, y;
2530 double tx, ty, w, h;
2531 // transform the bbox
2532 state->transform(bbox[0], bbox[1], &x, &y);
2535 state->transform(bbox[0], bbox[3], &x, &y);
2538 } else if (x > xMax) {
2543 } else if (y > yMax) {
2546 state->transform(bbox[2], bbox[1], &x, &y);
2549 } else if (x > xMax) {
2554 } else if (y > yMax) {
2557 state->transform(bbox[2], bbox[3], &x, &y);
2560 } else if (x > xMax) {
2565 } else if (y > yMax) {
2568 tx = (int)floor(xMin);
2571 } else if (tx > width) {
2574 ty = (int)floor(yMin);
2577 } else if (ty > height) {
2580 w = (int)ceil(xMax) - tx + 1;
2581 if (tx + w > width) {
2587 h = (int)ceil(yMax) - ty + 1;
2588 if (ty + h > height) {
2602 #if xpdfUpdateVersion >= 16
2603 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2604 GfxColorSpace *blendingColorSpace,
2605 GBool isolated, GBool knockout,
2608 char*colormodename = "";
2609 BBox rect = mkBBox(state, bbox, this->width, this->height);
2611 if(blendingColorSpace) {
2612 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2614 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);
2615 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2616 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);
2617 states[statepos].createsoftmask |= forSoftMask;
2618 states[statepos].transparencygroup = !forSoftMask;
2619 states[statepos].olddevice = this->device;
2621 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2623 gfxdevice_record_init(this->device);
2625 /*if(!forSoftMask) { ////???
2626 state->setFillOpacity(0.0);
2628 warnfeature("transparency groups",1);
2632 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2635 dbg("endTransparencyGroup");
2636 msg("<verbose> endTransparencyGroup");
2638 gfxdevice_t*r = this->device;
2640 this->device = states[statepos].olddevice;
2642 if(states[statepos].createsoftmask) {
2643 states[statepos-1].softmaskrecording = r->finish(r);
2645 states[statepos-1].grouprecording = r->finish(r);
2648 states[statepos].createsoftmask = 0;
2649 states[statepos].transparencygroup = 0;
2653 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2655 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2656 "colordodge","colorburn","hardlight","softlight","difference",
2657 "exclusion","hue","saturation","color","luminosity"};
2659 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2660 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2662 gfxresult_t*grouprecording = states[statepos].grouprecording;
2664 if(state->getBlendMode() == gfxBlendNormal) {
2666 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2667 gfxresult_record_replay(grouprecording, &ops);
2670 grouprecording->destroy(grouprecording);
2672 states[statepos].grouprecording = 0;
2675 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2677 /* alpha = 1: retrieve mask values from alpha layer
2678 alpha = 0: retrieve mask values from luminance */
2679 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2680 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2681 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2682 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2683 warnfeature("soft masks",0);
2685 states[statepos].olddevice = this->device;
2686 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2687 gfxdevice_record_init(this->device);
2689 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2691 states[statepos].softmask = 1;
2694 void GFXOutputDev::clearSoftMask(GfxState *state)
2696 if(!states[statepos].softmask)
2698 states[statepos].softmask = 0;
2699 dbg("clearSoftMask statepos=%d", statepos);
2700 msg("<verbose> clearSoftMask");
2702 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2703 msg("<error> Error in softmask/tgroup ordering");
2707 gfxresult_t*mask = states[statepos].softmaskrecording;
2708 gfxresult_t*below = this->device->finish(this->device);
2709 this->device = states[statepos].olddevice;
2711 /* get outline of all objects below the soft mask */
2712 gfxdevice_t uniondev;
2713 gfxdevice_union_init(&uniondev, 0);
2714 gfxresult_record_replay(below, &uniondev);
2715 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2716 uniondev.finish(&uniondev);
2718 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2720 this->device->startclip(this->device, belowoutline);
2721 gfxresult_record_replay(below, this->device);
2722 gfxresult_record_replay(mask, this->device);
2723 this->device->endclip(this->device);
2724 gfxline_free(belowoutline);
2727 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2729 gfxdevice_t belowrender;
2730 gfxdevice_render_init(&belowrender);
2731 belowrender.setparameter(&belowrender, "antialize", "2");
2732 belowrender.startpage(&belowrender, width, height);
2733 gfxresult_record_replay(below, &belowrender);
2734 belowrender.endpage(&belowrender);
2735 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2736 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2738 gfxdevice_t maskrender;
2739 gfxdevice_render_init(&maskrender);
2740 maskrender.startpage(&maskrender, width, height);
2741 gfxresult_record_replay(mask, &maskrender);
2742 maskrender.endpage(&maskrender);
2743 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2744 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2746 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2747 msg("<fatal> Internal error in mask drawing");
2752 for(y=0;y<height;y++) {
2753 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2754 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2755 for(x=0;x<width;x++) {
2757 l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2759 /* premultiply alpha... do we need this? (depends on output device)
2760 l2->r = (l2->a*l2->r) >> 8;
2761 l2->g = (l2->a*l2->g) >> 8;
2762 l2->b = (l2->a*l2->b) >> 8;
2769 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2772 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2773 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2775 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2777 mask->destroy(mask);
2778 below->destroy(below);
2779 maskresult->destroy(maskresult);
2780 belowresult->destroy(belowresult);
2781 states[statepos].softmaskrecording = 0;
2789 delete globalParams;globalParams=0;
2790 Object::memCheck(stderr);