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 void GFXOutputDev::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 ":"");
158 if(this->config_break_on_warning) {
159 msg("<fatal> Aborting conversion due to unsupported feature");
163 msg("<notice> File contains %s",feature);
166 void GFXOutputDev::warnfeature(char*feature,char fully)
168 showfeature(feature,fully,1);
170 void GFXOutputDev::infofeature(char*feature)
172 showfeature(feature,0,0);
175 GFXOutputState::GFXOutputState() {
177 this->textRender = 0;
178 this->createsoftmask = 0;
179 this->transparencygroup = 0;
181 this->grouprecording = 0;
184 GBool GFXOutputDev::interpretType3Chars()
189 typedef struct _drawnchar
207 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
208 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
213 free(chars);chars = 0;
220 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
224 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
227 chars[num_chars].x = x;
228 chars[num_chars].y = y;
229 chars[num_chars].color = color;
230 chars[num_chars].charid = charid;
234 static char*getFontID(GfxFont*font);
236 GFXOutputDev::GFXOutputDev(parameter_t*p)
239 this->textmodeinfo = 0;
242 this->type3active = 0;
245 this->substitutepos = 0;
246 this->type3Warning = 0;
247 this->user_movex = 0;
248 this->user_movey = 0;
251 this->user_clipx1 = 0;
252 this->user_clipy1 = 0;
253 this->user_clipx2 = 0;
254 this->user_clipy2 = 0;
255 this->current_text_stroke = 0;
256 this->current_text_clip = 0;
258 this->outer_clip_box = 0;
260 this->pagebuflen = 0;
262 this->config_use_fontconfig=1;
263 this->config_break_on_warning=0;
265 this->parameters = p;
267 /* configure device */
269 if(!strcmp(p->name,"fontconfig")) {
270 this->config_use_fontconfig = atoi(p->value);
271 } else if(!strcmp(p->name,"breakonwarning")) {
272 this->config_break_on_warning = atoi(p->value);
278 void GFXOutputDev::setDevice(gfxdevice_t*dev)
280 parameter_t*p = this->parameters;
282 /* pass parameters to output device */
286 this->device->setparameter(this->device, p->name, p->value);
292 void GFXOutputDev::setMove(int x,int y)
294 this->user_movex = x;
295 this->user_movey = y;
298 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
300 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
301 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
303 this->user_clipx1 = x1;
304 this->user_clipy1 = y1;
305 this->user_clipx2 = x2;
306 this->user_clipy2 = y2;
309 static char*getFontID(GfxFont*font)
311 Ref*ref = font->getID();
312 GString*gstr = font->getName();
313 char* fname = gstr==0?0:gstr->getCString();
316 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
318 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
323 static char*getFontName(GfxFont*font)
326 GString*gstr = font->getName();
327 char* fname = gstr==0?0:gstr->getCString();
331 sprintf(buf, "UFONT%d", r->num);
332 fontid = strdup(buf);
334 fontid = strdup(fname);
338 char* plus = strchr(fontid, '+');
339 if(plus && plus < &fontid[strlen(fontid)-1]) {
340 fontname = strdup(plus+1);
342 fontname = strdup(fontid);
348 static char mybuf[1024];
349 static char* gfxstate2str(GfxState *state)
353 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
360 if(state->getX1()!=0.0)
361 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
362 if(state->getY1()!=0.0)
363 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
364 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
365 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
366 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
367 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
368 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
369 state->getFillColor()->c[0], state->getFillColor()->c[1]);
370 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
371 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
372 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
373 state->getFillColor()->c[0], state->getFillColor()->c[1],
374 state->getFillColor()->c[2], state->getFillColor()->c[3],
375 state->getFillColor()->c[4], state->getFillColor()->c[5],
376 state->getFillColor()->c[6], state->getFillColor()->c[7]);
377 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
378 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
379 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
380 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
381 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
382 state->getFillRGB(&rgb);
383 if(rgb.r || rgb.g || rgb.b)
384 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
385 state->getStrokeRGB(&rgb);
386 if(rgb.r || rgb.g || rgb.b)
387 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
388 if(state->getFillColorSpace()->getNComps()>1)
389 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
390 if(state->getStrokeColorSpace()->getNComps()>1)
391 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
392 if(state->getFillPattern())
393 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
394 if(state->getStrokePattern())
395 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
397 if(state->getFillOpacity()!=1.0)
398 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
399 if(state->getStrokeOpacity()!=1.0)
400 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
402 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
407 state->getLineDash(&dash, &length, &start);
411 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
412 for(t=0;t<length;t++) {
413 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
415 bufpos+=sprintf(bufpos,"]");
418 if(state->getFlatness()!=1)
419 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
420 if(state->getLineJoin()!=0)
421 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
422 if(state->getLineJoin()!=0)
423 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
424 if(state->getLineJoin()!=0)
425 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
427 if(state->getFont() && getFontID(state->getFont()))
428 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
429 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
430 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
431 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
432 if(state->getCharSpace())
433 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
434 if(state->getWordSpace())
435 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
436 if(state->getHorizScaling()!=1.0)
437 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
438 if(state->getLeading())
439 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
441 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
442 if(state->getRender())
443 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
444 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
445 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
446 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
447 if(state->getLineX())
448 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
449 if(state->getLineY())
450 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
451 bufpos+=sprintf(bufpos," ");
455 static void dumpFontInfo(char*loglevel, GfxFont*font);
456 static int lastdumps[1024];
457 static int lastdumppos = 0;
462 static void showFontError(GfxFont*font, int nr)
466 for(t=0;t<lastdumppos;t++)
467 if(lastdumps[t] == r->num)
471 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
472 lastdumps[lastdumppos++] = r->num;
474 msg("<warning> The following font caused problems:");
476 msg("<warning> The following font caused problems (substituting):");
478 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
479 dumpFontInfo("<warning>", font);
482 static void dumpFontInfo(char*loglevel, GfxFont*font)
484 char* id = getFontID(font);
485 char* name = getFontName(font);
486 Ref* r=font->getID();
487 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
489 GString*gstr = font->getTag();
491 msg("%s| Tag: %s\n", loglevel, id);
493 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
495 GfxFontType type=font->getType();
497 case fontUnknownType:
498 msg("%s| Type: unknown\n",loglevel);
501 msg("%s| Type: 1\n",loglevel);
504 msg("%s| Type: 1C\n",loglevel);
507 msg("%s| Type: 3\n",loglevel);
510 msg("%s| Type: TrueType\n",loglevel);
513 msg("%s| Type: CIDType0\n",loglevel);
516 msg("%s| Type: CIDType0C\n",loglevel);
519 msg("%s| Type: CIDType2\n",loglevel);
524 GBool embedded = font->getEmbeddedFontID(&embRef);
526 if(font->getEmbeddedFontName()) {
527 embeddedName = font->getEmbeddedFontName()->getCString();
530 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
532 gstr = font->getExtFontFile();
534 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
536 // Get font descriptor flags.
537 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
538 if(font->isSerif()) msg("%s| is serif\n", loglevel);
539 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
540 if(font->isItalic()) msg("%s| is italic\n", loglevel);
541 if(font->isBold()) msg("%s| is bold\n", loglevel);
547 //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");}
548 //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");}
551 void dump_outline(gfxline_t*line)
554 if(line->type == gfx_moveTo) {
555 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
556 } else if(line->type == gfx_lineTo) {
557 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
558 } else if(line->type == gfx_splineTo) {
559 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
565 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
567 int num = path->getNumSubpaths();
570 double lastx=0,lasty=0,posx=0,posy=0;
573 msg("<warning> empty path");
577 gfxdrawer_target_gfxline(&draw);
579 for(t = 0; t < num; t++) {
580 GfxSubpath *subpath = path->getSubpath(t);
581 int subnum = subpath->getNumPoints();
582 double bx=0,by=0,cx=0,cy=0;
584 for(s=0;s<subnum;s++) {
587 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
592 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
593 draw.lineTo(&draw, lastx, lasty);
595 draw.moveTo(&draw, x,y);
600 } else if(subpath->getCurve(s) && cpos==0) {
604 } else if(subpath->getCurve(s) && cpos==1) {
612 draw.lineTo(&draw, x,y);
614 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
621 /* fix non-closed lines */
622 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
623 draw.lineTo(&draw, lastx, lasty);
625 gfxline_t*result = (gfxline_t*)draw.result(&draw);
627 gfxline_optimize(result);
632 GBool GFXOutputDev::useTilingPatternFill()
634 infofeature("tiled patterns");
638 GBool GFXOutputDev::useShadedFills()
640 infofeature("shaded fills");
644 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
646 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
647 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
648 double miterLimit = state->getMiterLimit();
649 double width = state->getTransformedLineWidth();
652 double opaq = state->getStrokeOpacity();
654 state->getFillRGB(&rgb);
656 state->getStrokeRGB(&rgb);
658 col.r = colToByte(rgb.r);
659 col.g = colToByte(rgb.g);
660 col.b = colToByte(rgb.b);
661 col.a = (unsigned char)(opaq*255);
663 gfx_capType capType = gfx_capRound;
664 if(lineCap == 0) capType = gfx_capButt;
665 else if(lineCap == 1) capType = gfx_capRound;
666 else if(lineCap == 2) capType = gfx_capSquare;
668 gfx_joinType joinType = gfx_joinRound;
669 if(lineJoin == 0) joinType = gfx_joinMiter;
670 else if(lineJoin == 1) joinType = gfx_joinRound;
671 else if(lineJoin == 2) joinType = gfx_joinBevel;
674 double dashphase = 0;
676 state->getLineDash(&ldash, &dashnum, &dashphase);
680 if(dashnum && ldash) {
681 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
685 msg("<trace> %d dashes", dashnum);
686 msg("<trace> | phase: %f", dashphase);
687 for(t=0;t<dashnum;t++) {
689 msg("<trace> | d%-3d: %f", t, ldash[t]);
692 if(getLogLevel() >= LOGLEVEL_TRACE) {
696 line2 = gfxtool_dash_line(line, dash, dashphase);
699 msg("<trace> After dashing:");
702 if(getLogLevel() >= LOGLEVEL_TRACE) {
703 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
705 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
706 lineCap==0?"butt": (lineJoin==1?"round":"square"),
708 col.r,col.g,col.b,col.a
713 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
714 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
720 gfxcolor_t getFillColor(GfxState * state)
723 double opaq = state->getFillOpacity();
724 state->getFillRGB(&rgb);
726 col.r = colToByte(rgb.r);
727 col.g = colToByte(rgb.g);
728 col.b = colToByte(rgb.b);
729 col.a = (unsigned char)(opaq*255);
733 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
735 gfxcolor_t col = getFillColor(state);
737 if(getLogLevel() >= LOGLEVEL_TRACE) {
738 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
741 device->fill(device, line, &col);
744 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
746 if(getLogLevel() >= LOGLEVEL_TRACE) {
747 msg("<trace> clip\n");
751 device->startclip(device, line);
752 states[statepos].clipping++;
755 void GFXOutputDev::clip(GfxState *state)
757 GfxPath * path = state->getPath();
758 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
759 clipToGfxLine(state, line);
763 void GFXOutputDev::eoClip(GfxState *state)
765 GfxPath * path = state->getPath();
766 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
768 if(getLogLevel() >= LOGLEVEL_TRACE) {
769 msg("<trace> eoclip\n");
773 device->startclip(device, line);
774 states[statepos].clipping++;
778 void GFXOutputDev::endframe()
781 device->endclip(device);
785 device->endpage(device);
788 void GFXOutputDev::finish()
792 device->endclip(device);
798 GFXOutputDev::~GFXOutputDev()
803 free(this->pages); this->pages = 0;
806 fontlist_t*l = this->fontlist;
808 fontlist_t*next = l->next;
810 gfxfont_free(l->font);
811 free(l->filename);l->filename=0;
817 GBool GFXOutputDev::upsideDown()
821 GBool GFXOutputDev::useDrawChar()
826 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
827 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
829 #define RENDER_FILL 0
830 #define RENDER_STROKE 1
831 #define RENDER_FILLSTROKE 2
832 #define RENDER_INVISIBLE 3
833 #define RENDER_CLIP 4
835 static char tmp_printstr[4096];
836 char* makeStringPrintable(char*str)
838 int len = strlen(str);
853 tmp_printstr[len++] = '.';
854 tmp_printstr[len++] = '.';
855 tmp_printstr[len++] = '.';
857 tmp_printstr[len] = 0;
862 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
867 /* find out char name from unicode index
868 TODO: should be precomputed
870 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
871 if(nameToUnicodeTab[t].u == u) {
872 uniname = nameToUnicodeTab[t].name;
880 for(t=0;t<font->num_glyphs;t++) {
881 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
882 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
886 /* if we didn't find the character, maybe
887 we can find the capitalized version */
888 for(t=0;t<font->num_glyphs;t++) {
889 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
890 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
898 for(t=0;t<font->num_glyphs;t++) {
899 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
900 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
904 /* if we didn't find the character, maybe
905 we can find the capitalized version */
906 for(t=0;t<font->num_glyphs;t++) {
907 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
908 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
914 /* try to use the unicode id */
915 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
916 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
917 return font->unicode2glyph[u];
920 if(charnr>=0 && charnr<font->num_glyphs) {
921 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
929 void GFXOutputDev::beginString(GfxState *state, GString *s)
931 int render = state->getRender();
932 if(current_text_stroke) {
933 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
936 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
937 double m11,m21,m12,m22;
938 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
939 state->getFontTransMat(&m11, &m12, &m21, &m22);
940 m11 *= state->getHorizScaling();
941 m21 *= state->getHorizScaling();
943 this->current_font_matrix.m00 = m11 / 1024.0;
944 this->current_font_matrix.m01 = m12 / 1024.0;
945 this->current_font_matrix.m10 = -m21 / 1024.0;
946 this->current_font_matrix.m11 = -m22 / 1024.0;
947 this->current_font_matrix.tx = 0;
948 this->current_font_matrix.ty = 0;
950 gfxmatrix_t m = this->current_font_matrix;
952 states[statepos].textRender = render;
955 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
956 double dx, double dy,
957 double originX, double originY,
958 CharCode c, int nBytes, Unicode *_u, int uLen)
960 int render = state->getRender();
961 // check for invisible text -- this is used by Acrobat Capture
963 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
967 if(states[statepos].textRender != render)
968 msg("<error> Internal error: drawChar.render!=beginString.render");
970 gfxcolor_t col = getFillColor(state);
972 Gushort *CIDToGIDMap = 0;
973 GfxFont*font = state->getFont();
975 if(font->getType() == fontType3) {
976 /* type 3 chars are passed as graphics */
977 msg("<debug> type3 char at %f/%f", x, y);
987 if(font->isCIDFont()) {
988 GfxCIDFont*cfont = (GfxCIDFont*)font;
990 if(font->getType() == fontCIDType2)
991 CIDToGIDMap = cfont->getCIDToGID();
994 font8 = (Gfx8BitFont*)font;
995 char**enc=font8->getEncoding();
999 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);
1002 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);
1008 charid = getGfxCharID(current_gfxfont, c, name, u);
1010 charid = getGfxCharID(current_gfxfont, c, name, -1);
1013 /* multiple unicodes- should usually map to a ligature.
1014 if the ligature doesn't exist, we need to draw
1015 the characters one-by-one. */
1017 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1018 for(t=0;t<uLen;t++) {
1019 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1025 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1026 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1030 gfxmatrix_t m = this->current_font_matrix;
1031 state->transform(x, y, &m.tx, &m.ty);
1032 m.tx += user_movex + clipmovex;
1033 m.ty += user_movey + clipmovey;
1035 if(render == RENDER_FILL) {
1036 device->drawchar(device, current_gfxfont, charid, &col, &m);
1038 msg("<debug> Drawing glyph %d as shape", charid);
1040 msg("<notice> Some texts will be rendered as shape");
1043 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1044 gfxline_t*tglyph = gfxline_clone(glyph);
1045 gfxline_transform(tglyph, &m);
1046 if((render&3) != RENDER_INVISIBLE) {
1047 gfxline_t*add = gfxline_clone(tglyph);
1048 current_text_stroke = gfxline_append(current_text_stroke, add);
1050 if(render&RENDER_CLIP) {
1051 gfxline_t*add = gfxline_clone(tglyph);
1052 current_text_clip = gfxline_append(current_text_clip, add);
1054 gfxline_free(tglyph);
1058 void GFXOutputDev::endString(GfxState *state)
1060 int render = state->getRender();
1061 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1062 if(states[statepos].textRender != render)
1063 msg("<error> Internal error: drawChar.render!=beginString.render");
1065 if(current_text_stroke) {
1066 /* fillstroke and stroke text rendering objects we can process right
1067 now (as there may be texts of other rendering modes in this
1068 text object)- clipping objects have to wait until endTextObject,
1070 device->setparameter(device, "mark","TXT");
1071 if((render&3) == RENDER_FILL) {
1072 fillGfxLine(state, current_text_stroke);
1073 gfxline_free(current_text_stroke);
1074 current_text_stroke = 0;
1075 } else if((render&3) == RENDER_FILLSTROKE) {
1076 fillGfxLine(state, current_text_stroke);
1077 strokeGfxline(state, current_text_stroke);
1078 gfxline_free(current_text_stroke);
1079 current_text_stroke = 0;
1080 } else if((render&3) == RENDER_STROKE) {
1081 strokeGfxline(state, current_text_stroke);
1082 gfxline_free(current_text_stroke);
1083 current_text_stroke = 0;
1085 device->setparameter(device, "mark","");
1089 void GFXOutputDev::endTextObject(GfxState *state)
1091 int render = state->getRender();
1092 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1093 if(states[statepos].textRender != render)
1094 msg("<error> Internal error: drawChar.render!=beginString.render");
1096 if(current_text_clip) {
1097 device->setparameter(device, "mark","TXT");
1098 clipToGfxLine(state, current_text_clip);
1099 device->setparameter(device, "mark","");
1100 gfxline_free(current_text_clip);
1101 current_text_clip = 0;
1105 /* the logic seems to be as following:
1106 first, beginType3Char is called, with the charcode and the coordinates.
1107 if this function returns true, it already knew about the char and has now drawn it.
1108 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1109 the all draw operations until endType3Char are part of the char (which in this moment is
1110 at the position first passed to beginType3Char). the char ends with endType3Char.
1112 The drawing operations between beginType3Char and endType3Char are somewhat different to
1113 the normal ones. For example, the fillcolor equals the stroke color.
1116 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1118 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1120 /* the character itself is going to be passed using the draw functions */
1121 return gFalse; /* gTrue= is_in_cache? */
1124 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1125 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1127 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1128 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1132 void GFXOutputDev::endType3Char(GfxState *state)
1135 msg("<debug> endType3Char");
1138 void GFXOutputDev::startFrame(int width, int height)
1140 if(outer_clip_box) {
1141 device->endclip(device);
1145 device->startpage(device, width, height);
1146 this->width = width;
1147 this->height = height;
1150 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1152 this->currentpage = pageNum;
1154 int rot = doc->getPageRotate(1);
1157 gfxline_t clippath[5];
1159 white.r = white.g = white.b = white.a = 255;
1161 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1162 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1163 Use CropBox, not MediaBox, as page size
1170 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1171 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1173 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1174 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1176 this->clipmovex = -(int)x1;
1177 this->clipmovey = -(int)y1;
1179 /* apply user clip box */
1180 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1181 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1182 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1183 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1184 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1185 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1188 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1190 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);
1192 msg("<verbose> page is rotated %d degrees\n", rot);
1194 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1195 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1196 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1197 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1198 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1199 device->startclip(device, clippath); outer_clip_box = 1;
1200 device->fill(device, clippath, &white);
1204 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1206 double x1, y1, x2, y2, w;
1207 gfxline_t points[5];
1210 msg("<debug> drawlink\n");
1212 link->getRect(&x1, &y1, &x2, &y2);
1213 cvtUserToDev(x1, y1, &x, &y);
1214 points[0].type = gfx_moveTo;
1215 points[0].x = points[4].x = x + user_movex + clipmovex;
1216 points[0].y = points[4].y = y + user_movey + clipmovey;
1217 points[0].next = &points[1];
1218 cvtUserToDev(x2, y1, &x, &y);
1219 points[1].type = gfx_lineTo;
1220 points[1].x = x + user_movex + clipmovex;
1221 points[1].y = y + user_movey + clipmovey;
1222 points[1].next = &points[2];
1223 cvtUserToDev(x2, y2, &x, &y);
1224 points[2].type = gfx_lineTo;
1225 points[2].x = x + user_movex + clipmovex;
1226 points[2].y = y + user_movey + clipmovey;
1227 points[2].next = &points[3];
1228 cvtUserToDev(x1, y2, &x, &y);
1229 points[3].type = gfx_lineTo;
1230 points[3].x = x + user_movex + clipmovex;
1231 points[3].y = y + user_movey + clipmovey;
1232 points[3].next = &points[4];
1233 cvtUserToDev(x1, y1, &x, &y);
1234 points[4].type = gfx_lineTo;
1235 points[4].x = x + user_movex + clipmovex;
1236 points[4].y = y + user_movey + clipmovey;
1239 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1240 points[0].x, points[0].y,
1241 points[1].x, points[1].y,
1242 points[2].x, points[2].y,
1243 points[3].x, points[3].y);
1245 LinkAction*action=link->getAction();
1251 msg("<trace> drawlink action=%d\n", action->getKind());
1252 switch(action->getKind())
1256 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1257 LinkDest *dest=NULL;
1258 if (ha->getDest()==NULL)
1259 dest=catalog->findDest(ha->getNamedDest());
1260 else dest=ha->getDest();
1262 if (dest->isPageRef()){
1263 Ref pageref=dest->getPageRef();
1264 page=catalog->findPage(pageref.num,pageref.gen);
1266 else page=dest->getPageNum();
1267 sprintf(buf, "%d", page);
1274 LinkGoToR*l = (LinkGoToR*)action;
1275 GString*g = l->getFileName();
1277 s = strdup(g->getCString());
1279 /* if the GoToR link has no filename, then
1280 try to find a refernce in the *local*
1282 GString*g = l->getNamedDest();
1284 s = strdup(g->getCString());
1290 LinkNamed*l = (LinkNamed*)action;
1291 GString*name = l->getName();
1293 s = strdup(name->lowerCase()->getCString());
1294 named = name->getCString();
1297 if(strstr(s, "next") || strstr(s, "forward"))
1299 page = currentpage + 1;
1301 else if(strstr(s, "prev") || strstr(s, "back"))
1303 page = currentpage - 1;
1305 else if(strstr(s, "last") || strstr(s, "end"))
1307 if(pages && pagepos>0)
1308 page = pages[pagepos-1];
1310 else if(strstr(s, "first") || strstr(s, "top"))
1318 case actionLaunch: {
1320 LinkLaunch*l = (LinkLaunch*)action;
1321 GString * str = new GString(l->getFileName());
1322 GString * params = l->getParams();
1324 str->append(params);
1325 s = strdup(str->getCString());
1332 LinkURI*l = (LinkURI*)action;
1333 GString*g = l->getURI();
1335 url = g->getCString();
1340 case actionUnknown: {
1342 LinkUnknown*l = (LinkUnknown*)action;
1347 msg("<error> Unknown link type!\n");
1352 if(!s) s = strdup("-?-");
1354 msg("<trace> drawlink s=%s\n", s);
1356 if(!linkinfo && (page || s))
1358 msg("<notice> File contains links");
1366 for(t=1;t<=pagepos;t++) {
1367 if(pages[t]==page) {
1376 sprintf(buf, "page%d", lpage);
1377 device->drawlink(device, points, buf);
1381 device->drawlink(device, points, s);
1384 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1388 void GFXOutputDev::saveState(GfxState *state) {
1389 dbg("saveState");dbgindent+=2;
1391 msg("<trace> saveState\n");
1394 msg("<error> Too many nested states in pdf.");
1398 states[statepos].textRender = states[statepos-1].textRender;
1399 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1400 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1401 states[statepos].clipping = 0;
1404 void GFXOutputDev::restoreState(GfxState *state) {
1405 dbgindent-=2; dbg("restoreState");
1408 msg("<error> Invalid restoreState");
1411 msg("<trace> restoreState");
1412 if(states[statepos].softmask) {
1413 clearSoftMask(state);
1416 while(states[statepos].clipping) {
1417 device->endclip(device);
1418 states[statepos].clipping--;
1423 char* GFXOutputDev::searchFont(char*name)
1427 int is_standard_font = 0;
1429 msg("<verbose> SearchFont(%s)", name);
1431 /* see if it is a pdf standard font */
1432 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1434 if(!strcmp(name, pdf2t1map[i].pdffont))
1436 name = pdf2t1map[i].filename;
1437 is_standard_font = 1;
1441 /* look in all font files */
1442 for(i=0;i<fontnum;i++)
1444 if(strstr(fonts[i].filename, name))
1446 if(!fonts[i].used) {
1449 if(!is_standard_font)
1450 msg("<notice> Using %s for %s", fonts[i].filename, name);
1452 return strdup(fonts[i].filename);
1458 void GFXOutputDev::updateLineWidth(GfxState *state)
1460 double width = state->getTransformedLineWidth();
1461 //swfoutput_setlinewidth(&device, width);
1464 void GFXOutputDev::updateLineCap(GfxState *state)
1466 int c = state->getLineCap();
1469 void GFXOutputDev::updateLineJoin(GfxState *state)
1471 int j = state->getLineJoin();
1474 void GFXOutputDev::updateFillColor(GfxState *state)
1477 double opaq = state->getFillOpacity();
1478 state->getFillRGB(&rgb);
1480 void GFXOutputDev::updateFillOpacity(GfxState *state)
1483 double opaq = state->getFillOpacity();
1484 state->getFillRGB(&rgb);
1485 dbg("update fillopaq %f", opaq);
1487 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1489 double opaq = state->getFillOpacity();
1490 dbg("update strokeopaq %f", opaq);
1492 void GFXOutputDev::updateFillOverprint(GfxState *state)
1494 double opaq = state->getFillOverprint();
1495 dbg("update filloverprint %f", opaq);
1497 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1499 double opaq = state->getStrokeOverprint();
1500 dbg("update strokeoverprint %f", opaq);
1502 void GFXOutputDev::updateTransfer(GfxState *state)
1504 dbg("update transfer");
1508 void GFXOutputDev::updateStrokeColor(GfxState *state)
1511 double opaq = state->getStrokeOpacity();
1512 state->getStrokeRGB(&rgb);
1515 void FoFiWrite(void *stream, char *data, int len)
1517 int ret = fwrite(data, len, 1, (FILE*)stream);
1520 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1522 char*tmpFileName = NULL;
1528 Object refObj, strObj;
1530 tmpFileName = mktmpname(namebuf);
1533 ret = font->getEmbeddedFontID(&embRef);
1535 msg("<verbose> Didn't get embedded font id");
1536 /* not embedded- the caller should now search the font
1537 directories for this font */
1541 f = fopen(tmpFileName, "wb");
1543 msg("<error> Couldn't create temporary Type 1 font file");
1547 /*if(font->isCIDFont()) {
1548 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1549 GString c = cidFont->getCollection();
1550 msg("<notice> Collection: %s", c.getCString());
1553 //if (font->getType() == fontType1C) {
1554 if (0) { //font->getType() == fontType1C) {
1555 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1557 msg("<error> Couldn't read embedded font file");
1560 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1562 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1563 //cvt->convertToCIDType0("test", f);
1564 //cvt->convertToType0("test", f);
1567 } else if(font->getType() == fontTrueType) {
1568 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1569 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1571 msg("<error> Couldn't read embedded font file");
1574 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1575 cvt->writeTTF(FoFiWrite, f);
1579 font->getEmbeddedFontID(&embRef);
1580 refObj.initRef(embRef.num, embRef.gen);
1581 refObj.fetch(ref, &strObj);
1583 strObj.streamReset();
1588 f4[t] = strObj.streamGetChar();
1589 f4c[t] = (char)f4[t];
1594 if(!strncmp(f4c, "true", 4)) {
1595 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1596 Change this on the fly */
1597 f4[0] = f4[2] = f4[3] = 0;
1605 while ((c = strObj.streamGetChar()) != EOF) {
1609 strObj.streamClose();
1614 return strdup(tmpFileName);
1617 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1619 char*name = getFontName(gfxFont);
1623 if(!this->config_use_fontconfig)
1626 #ifdef HAVE_FONTCONFIG
1627 FcPattern *pattern, *match;
1631 static int fcinitcalled = false;
1633 msg("<debug> searchForSuitableFont(%s)", name);
1635 // call init ony once
1636 if (!fcinitcalled) {
1637 msg("<debug> Initializing FontConfig...");
1638 fcinitcalled = true;
1640 msg("<debug> FontConfig Initialization failed. Disabling.");
1641 config_use_fontconfig = 0;
1644 msg("<debug> ...initialized FontConfig");
1647 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1648 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1649 if (gfxFont->isItalic()) // check for italic
1650 msg("<debug> FontConfig: Adding Italic Slant");
1651 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1652 if (gfxFont->isBold()) // check for bold
1653 msg("<debug> FontConfig: Adding Bold Weight");
1654 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1656 msg("<debug> FontConfig: Try to match...");
1657 // configure and match using the original font name
1658 FcConfigSubstitute(0, pattern, FcMatchPattern);
1659 FcDefaultSubstitute(pattern);
1660 match = FcFontMatch(0, pattern, &result);
1662 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1663 msg("<debug> FontConfig: family=%s", (char*)v);
1664 // if we get an exact match
1665 if (strcmp((char *)v, name) == 0) {
1666 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1667 filename = strdup((char*)v); // mem leak
1668 char *nfn = strrchr(filename, '/');
1669 if(nfn) fontname = strdup(nfn+1);
1670 else fontname = filename;
1672 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1674 // initialize patterns
1675 FcPatternDestroy(pattern);
1676 FcPatternDestroy(match);
1678 // now match against serif etc.
1679 if (gfxFont->isSerif()) {
1680 msg("<debug> FontConfig: Create Serif Family Pattern");
1681 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1682 } else if (gfxFont->isFixedWidth()) {
1683 msg("<debug> FontConfig: Create Monospace Family Pattern");
1684 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1686 msg("<debug> FontConfig: Create Sans Family Pattern");
1687 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1691 if (gfxFont->isItalic()) {
1692 msg("<debug> FontConfig: Adding Italic Slant");
1693 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1696 if (gfxFont->isBold()) {
1697 msg("<debug> FontConfig: Adding Bold Weight");
1698 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1701 msg("<debug> FontConfig: Try to match... (2)");
1702 // configure and match using serif etc
1703 FcConfigSubstitute (0, pattern, FcMatchPattern);
1704 FcDefaultSubstitute (pattern);
1705 match = FcFontMatch (0, pattern, &result);
1707 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1708 filename = strdup((char*)v); // mem leak
1709 char *nfn = strrchr(filename, '/');
1710 if(nfn) fontname = strdup(nfn+1);
1711 else fontname = filename;
1713 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1717 //printf("FONTCONFIG: pattern");
1718 //FcPatternPrint(pattern);
1719 //printf("FONTCONFIG: match");
1720 //FcPatternPrint(match);
1722 FcPatternDestroy(pattern);
1723 FcPatternDestroy(match);
1725 pdfswf_addfont(filename);
1732 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1734 char*fontname = 0, *filename = 0;
1735 msg("<notice> substituteFont(%s)", oldname);
1737 if(!(fontname = searchForSuitableFont(gfxFont))) {
1738 fontname = "Times-Roman";
1740 filename = searchFont(fontname);
1742 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1746 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1747 msg("<fatal> Too many fonts in file.");
1751 substitutesource[substitutepos] = strdup(oldname); //mem leak
1752 substitutetarget[substitutepos] = fontname;
1753 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1756 return strdup(filename); //mem leak
1759 void unlinkfont(char* filename)
1766 if(!strncmp(&filename[l-4],".afm",4)) {
1767 memcpy(&filename[l-4],".pfb",4);
1769 memcpy(&filename[l-4],".pfa",4);
1771 memcpy(&filename[l-4],".afm",4);
1774 if(!strncmp(&filename[l-4],".pfa",4)) {
1775 memcpy(&filename[l-4],".afm",4);
1777 memcpy(&filename[l-4],".pfa",4);
1780 if(!strncmp(&filename[l-4],".pfb",4)) {
1781 memcpy(&filename[l-4],".afm",4);
1783 memcpy(&filename[l-4],".pfb",4);
1788 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1794 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1797 fontlist_t*last=0,*l = this->fontlist;
1800 msg("<error> Internal Error: FontID is null");
1802 /* TODO: should this be part of the state? */
1805 if(!strcmp(l->font->id, id)) {
1806 current_gfxfont = l->font;
1808 device->addfont(device, current_gfxfont);
1813 if(!filename) return 0;
1815 /* A font size of e.g. 9 means the font will be scaled down by
1816 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1817 we have to divide 0.05 by (fontsize/1024)
1819 double quality = (1024 * 0.05) / maxSize;
1821 msg("<verbose> Loading %s...", filename);
1822 font = gfxfont_load(id, filename, quality);
1824 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1827 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1831 l->filename = strdup(filename);
1833 current_gfxfont = l->font;
1839 device->addfont(device, current_gfxfont);
1843 void GFXOutputDev::updateFont(GfxState *state)
1845 GfxFont*gfxFont = state->getFont();
1851 char * fontid = getFontID(gfxFont);
1852 char * fontname = getFontName(gfxFont);
1854 double maxSize = 1.0;
1857 maxSize = this->info->getMaximumFontSize(fontid);
1861 /* first, look if we substituted this font before-
1862 this way, we don't initialize the T1 Fonts
1864 for(t=0;t<substitutepos;t++) {
1865 if(!strcmp(fontid, substitutesource[t])) {
1866 free(fontid);fontid=0;
1867 fontid = strdup(substitutetarget[t]);
1872 /* second, see if this is a font which was used before-
1873 if so, we are done */
1874 if(setGfxFont(fontid, fontname, 0, 0)) {
1879 /* if(swfoutput_queryfont(&device, fontid))
1880 swfoutput_setfont(&device, fontid, 0);
1882 msg("<debug> updateFont(%s) [cached]", fontid);
1886 // look for Type 3 font
1887 if (gfxFont->getType() == fontType3) {
1889 type3Warning = gTrue;
1890 showFontError(gfxFont, 2);
1897 /* now either load the font, or find a substitution */
1900 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1905 (gfxFont->getType() == fontType1 ||
1906 gfxFont->getType() == fontType1C ||
1907 gfxFont->getType() == fontCIDType0C ||
1908 gfxFont->getType() == fontTrueType ||
1909 gfxFont->getType() == fontCIDType2
1912 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1913 if(!fileName) showFontError(gfxFont,0);
1916 fileName = searchFont(fontname);
1917 if(!fileName) showFontError(gfxFont,0);
1920 char * fontname = getFontName(gfxFont);
1921 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1924 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1926 msg("<warning> Try specifying one or more font directories");
1928 fileName = substituteFont(gfxFont, fontid);
1931 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1932 msg("<notice> Font is now %s (%s)", fontid, fileName);
1936 msg("<error> Couldn't set font %s\n", fontid);
1942 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1943 dumpFontInfo("<verbose>", gfxFont);
1945 //swfoutput_setfont(&device, fontid, fileName);
1947 if(!setGfxFont(fontid, fontname, 0, 0)) {
1948 setGfxFont(fontid, fontname, fileName, maxSize);
1952 unlinkfont(fileName);
1962 #define SQR(x) ((x)*(x))
1964 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1966 if((newwidth<2 || newheight<2) ||
1967 (width<=newwidth || height<=newheight))
1969 unsigned char*newdata;
1971 newdata= (unsigned char*)malloc(newwidth*newheight);
1973 double fx = (double)(width)/newwidth;
1974 double fy = (double)(height)/newheight;
1976 int blocksize = (int)(8192/(fx*fy));
1977 int r = 8192*256/palettesize;
1978 for(x=0;x<newwidth;x++) {
1979 double ex = px + fx;
1980 int fromx = (int)px;
1982 int xweight1 = (int)(((fromx+1)-px)*256);
1983 int xweight2 = (int)((ex-tox)*256);
1985 for(y=0;y<newheight;y++) {
1986 double ey = py + fy;
1987 int fromy = (int)py;
1989 int yweight1 = (int)(((fromy+1)-py)*256);
1990 int yweight2 = (int)((ey-toy)*256);
1993 for(xx=fromx;xx<=tox;xx++)
1994 for(yy=fromy;yy<=toy;yy++) {
1995 int b = 1-data[width*yy+xx];
1997 if(xx==fromx) weight = (weight*xweight1)/256;
1998 if(xx==tox) weight = (weight*xweight2)/256;
1999 if(yy==fromy) weight = (weight*yweight1)/256;
2000 if(yy==toy) weight = (weight*yweight2)/256;
2003 //if(a) a=(palettesize-1)*r/blocksize;
2004 newdata[y*newwidth+x] = (a*blocksize)/r;
2012 #define IMAGE_TYPE_JPEG 0
2013 #define IMAGE_TYPE_LOSSLESS 1
2015 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2016 double x1,double y1,
2017 double x2,double y2,
2018 double x3,double y3,
2019 double x4,double y4, int type)
2021 gfxcolor_t*newpic=0;
2023 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2024 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2026 gfxline_t p1,p2,p3,p4,p5;
2027 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2028 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2029 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2030 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2031 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2033 {p1.x = (int)(p1.x*20)/20.0;
2034 p1.y = (int)(p1.y*20)/20.0;
2035 p2.x = (int)(p2.x*20)/20.0;
2036 p2.y = (int)(p2.y*20)/20.0;
2037 p3.x = (int)(p3.x*20)/20.0;
2038 p3.y = (int)(p3.y*20)/20.0;
2039 p4.x = (int)(p4.x*20)/20.0;
2040 p4.y = (int)(p4.y*20)/20.0;
2041 p5.x = (int)(p5.x*20)/20.0;
2042 p5.y = (int)(p5.y*20)/20.0;
2049 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2050 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2055 img.data = (gfxcolor_t*)data;
2059 if(type == IMAGE_TYPE_JPEG)
2060 /* TODO: pass image_dpi to device instead */
2061 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2063 dev->fillbitmap(dev, &p1, &img, &m, 0);
2066 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2067 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2069 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2072 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2073 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2075 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2079 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2080 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2081 GBool inlineImg, int mask, int*maskColors,
2082 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2084 double x1,y1,x2,y2,x3,y3,x4,y4;
2085 ImageStream *imgStr;
2090 unsigned char* maskbitmap = 0;
2093 ncomps = colorMap->getNumPixelComps();
2094 bits = colorMap->getBits();
2099 unsigned char buf[8];
2100 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2102 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2103 imgMaskStr->reset();
2104 unsigned char pal[256];
2105 int n = 1 << colorMap->getBits();
2110 maskColorMap->getGray(pixBuf, &gray);
2111 pal[t] = colToByte(gray);
2113 for (y = 0; y < maskHeight; y++) {
2114 for (x = 0; x < maskWidth; x++) {
2115 imgMaskStr->getPixel(buf);
2116 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2121 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2122 imgMaskStr->reset();
2123 for (y = 0; y < maskHeight; y++) {
2124 for (x = 0; x < maskWidth; x++) {
2125 imgMaskStr->getPixel(buf);
2127 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2135 imgStr = new ImageStream(str, width, ncomps,bits);
2138 if(!width || !height || (height<=1 && width<=1))
2140 msg("<verbose> Ignoring %d by %d image", width, height);
2141 unsigned char buf[8];
2143 for (y = 0; y < height; ++y)
2144 for (x = 0; x < width; ++x) {
2145 imgStr->getPixel(buf);
2153 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2154 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2155 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2156 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2158 if(!pbminfo && !(str->getKind()==strDCT)) {
2160 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2164 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2166 if(!jpeginfo && (str->getKind()==strDCT)) {
2167 msg("<notice> file contains jpeg pictures");
2173 unsigned char buf[8];
2175 unsigned char*pic = new unsigned char[width*height];
2176 gfxcolor_t pal[256];
2178 state->getFillRGB(&rgb);
2180 memset(pal,255,sizeof(pal));
2181 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2182 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2183 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2184 pal[0].a = 255; pal[1].a = 0;
2187 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2188 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2189 for (y = 0; y < height; ++y)
2190 for (x = 0; x < width; ++x)
2192 imgStr->getPixel(buf);
2195 pic[width*y+x] = buf[0];
2198 /* the size of the drawn image is added to the identifier
2199 as the same image may require different bitmaps if displayed
2200 at different sizes (due to antialiasing): */
2203 unsigned char*pic2 = 0;
2206 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2215 height = realheight;
2219 /* make a black/white palette */
2221 float r = 255/(numpalette-1);
2223 for(t=0;t<numpalette;t++) {
2224 pal[t].r = colToByte(rgb.r);
2225 pal[t].g = colToByte(rgb.g);
2226 pal[t].b = colToByte(rgb.b);
2227 pal[t].a = (unsigned char)(t*r);
2231 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2232 for (y = 0; y < height; ++y) {
2233 for (x = 0; x < width; ++x) {
2234 pic2[width*y+x] = pal[pic[y*width+x]];
2237 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2241 if(maskbitmap) free(maskbitmap);
2247 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2248 gfxcolor_t*pic=new gfxcolor_t[width*height];
2249 for (y = 0; y < height; ++y) {
2250 for (x = 0; x < width; ++x) {
2251 imgStr->getPixel(pixBuf);
2252 colorMap->getRGB(pixBuf, &rgb);
2253 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2254 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2255 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2256 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2258 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2262 if(str->getKind()==strDCT)
2263 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2265 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2268 if(maskbitmap) free(maskbitmap);
2271 gfxcolor_t*pic=new gfxcolor_t[width*height];
2272 gfxcolor_t pal[256];
2273 int n = 1 << colorMap->getBits();
2275 for(t=0;t<256;t++) {
2277 colorMap->getRGB(pixBuf, &rgb);
2279 {/*if(maskColors && *maskColors==t) {
2280 msg("<notice> Color %d is transparent", t);
2281 if (imgData->maskColors) {
2283 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2284 if (pix[i] < imgData->maskColors[2*i] ||
2285 pix[i] > imgData->maskColors[2*i+1]) {
2300 pal[t].r = (unsigned char)(colToByte(rgb.r));
2301 pal[t].g = (unsigned char)(colToByte(rgb.g));
2302 pal[t].b = (unsigned char)(colToByte(rgb.b));
2303 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2306 for (y = 0; y < height; ++y) {
2307 for (x = 0; x < width; ++x) {
2308 imgStr->getPixel(pixBuf);
2309 pic[width*y+x] = pal[pixBuf[0]];
2311 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2315 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2319 if(maskbitmap) free(maskbitmap);
2324 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2325 int width, int height, GBool invert,
2328 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2329 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2330 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2333 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2334 int width, int height, GfxImageColorMap *colorMap,
2335 int *maskColors, GBool inlineImg)
2337 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2338 colorMap?"colorMap":"no colorMap",
2339 maskColors?"maskColors":"no maskColors",
2341 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2342 colorMap?"colorMap":"no colorMap",
2343 maskColors?"maskColors":"no maskColors",
2346 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2347 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2348 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2351 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2352 int width, int height,
2353 GfxImageColorMap *colorMap,
2354 Stream *maskStr, int maskWidth, int maskHeight,
2357 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2358 colorMap?"colorMap":"no colorMap",
2359 maskWidth, maskHeight);
2360 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskWidth, maskHeight);
2364 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2365 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2366 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2369 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2370 int width, int height,
2371 GfxImageColorMap *colorMap,
2373 int maskWidth, int maskHeight,
2374 GfxImageColorMap *maskColorMap)
2376 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2377 colorMap?"colorMap":"no colorMap",
2378 maskWidth, maskHeight);
2379 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2380 colorMap?"colorMap":"no colorMap",
2381 maskWidth, maskHeight);
2383 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2384 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2385 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2388 void GFXOutputDev::stroke(GfxState *state)
2392 GfxPath * path = state->getPath();
2393 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2394 strokeGfxline(state, line);
2398 void GFXOutputDev::fill(GfxState *state)
2400 gfxcolor_t col = getFillColor(state);
2401 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2403 GfxPath * path = state->getPath();
2404 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2405 fillGfxLine(state, line);
2409 void GFXOutputDev::eoFill(GfxState *state)
2411 gfxcolor_t col = getFillColor(state);
2412 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2414 GfxPath * path = state->getPath();
2415 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2416 fillGfxLine(state, line);
2421 static char* dirseparator()
2430 void addGlobalFont(char*filename)
2433 memset(&f, 0, sizeof(fontfile_t));
2434 f.filename = filename;
2435 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2436 msg("<verbose> Adding font \"%s\".", filename);
2437 fonts[fontnum++] = f;
2439 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2443 void addGlobalLanguageDir(char*dir)
2446 globalParams = new GlobalParams("");
2448 msg("<notice> Adding %s to language pack directories", dir);
2452 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2453 strcpy(config_file, dir);
2454 strcat(config_file, dirseparator());
2455 strcat(config_file, "add-to-xpdfrc");
2457 fi = fopen(config_file, "rb");
2459 msg("<error> Could not open %s", config_file);
2462 globalParams->parseFile(new GString(config_file), fi);
2466 void addGlobalFontDir(char*dirname)
2468 #ifdef HAVE_DIRENT_H
2469 msg("<notice> Adding %s to font directories", dirname);
2470 lastfontdir = strdup(dirname);
2471 DIR*dir = opendir(dirname);
2473 msg("<warning> Couldn't open directory %s\n", dirname);
2478 ent = readdir (dir);
2482 char*name = ent->d_name;
2488 if(!strncasecmp(&name[l-4], ".pfa", 4))
2490 if(!strncasecmp(&name[l-4], ".pfb", 4))
2492 if(!strncasecmp(&name[l-4], ".ttf", 4))
2496 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2497 strcpy(fontname, dirname);
2498 strcat(fontname, dirseparator());
2499 strcat(fontname, name);
2500 addGlobalFont(fontname);
2505 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2509 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2515 this->pagebuflen = 1024;
2516 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2517 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2519 while(pdfpage >= this->pagebuflen)
2521 int oldlen = this->pagebuflen;
2522 this->pagebuflen+=1024;
2523 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2524 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2527 this->pages[pdfpage] = outputpage;
2528 if(pdfpage>this->pagepos)
2529 this->pagepos = pdfpage;
2535 double width,height;
2538 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2540 double xMin, yMin, xMax, yMax, x, y;
2541 double tx, ty, w, h;
2542 // transform the bbox
2543 state->transform(bbox[0], bbox[1], &x, &y);
2546 state->transform(bbox[0], bbox[3], &x, &y);
2549 } else if (x > xMax) {
2554 } else if (y > yMax) {
2557 state->transform(bbox[2], bbox[1], &x, &y);
2560 } else if (x > xMax) {
2565 } else if (y > yMax) {
2568 state->transform(bbox[2], bbox[3], &x, &y);
2571 } else if (x > xMax) {
2576 } else if (y > yMax) {
2579 tx = (int)floor(xMin);
2582 } else if (tx > width) {
2585 ty = (int)floor(yMin);
2588 } else if (ty > height) {
2591 w = (int)ceil(xMax) - tx + 1;
2592 if (tx + w > width) {
2598 h = (int)ceil(yMax) - ty + 1;
2599 if (ty + h > height) {
2613 #if xpdfUpdateVersion >= 16
2614 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2615 GfxColorSpace *blendingColorSpace,
2616 GBool isolated, GBool knockout,
2619 char*colormodename = "";
2620 BBox rect = mkBBox(state, bbox, this->width, this->height);
2622 if(blendingColorSpace) {
2623 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2625 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);
2626 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2627 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);
2628 states[statepos].createsoftmask |= forSoftMask;
2629 states[statepos].transparencygroup = !forSoftMask;
2630 states[statepos].olddevice = this->device;
2632 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2634 gfxdevice_record_init(this->device);
2636 /*if(!forSoftMask) { ////???
2637 state->setFillOpacity(0.0);
2642 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2645 dbg("endTransparencyGroup");
2646 msg("<verbose> endTransparencyGroup");
2648 gfxdevice_t*r = this->device;
2650 this->device = states[statepos].olddevice;
2652 if(states[statepos].createsoftmask) {
2653 states[statepos-1].softmaskrecording = r->finish(r);
2655 states[statepos-1].grouprecording = r->finish(r);
2658 states[statepos].createsoftmask = 0;
2659 states[statepos].transparencygroup = 0;
2663 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2665 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2666 "colordodge","colorburn","hardlight","softlight","difference",
2667 "exclusion","hue","saturation","color","luminosity"};
2669 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2670 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2672 if(state->getBlendMode() == gfxBlendNormal)
2673 infofeature("transparency groups");
2676 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2677 warnfeature(buffer, 0);
2680 gfxresult_t*grouprecording = states[statepos].grouprecording;
2682 if(state->getBlendMode() == gfxBlendNormal) {
2684 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2685 gfxresult_record_replay(grouprecording, &ops);
2688 grouprecording->destroy(grouprecording);
2690 states[statepos].grouprecording = 0;
2693 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2695 /* alpha = 1: retrieve mask values from alpha layer
2696 alpha = 0: retrieve mask values from luminance */
2697 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2698 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2699 msg("<verbose> 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]));
2702 infofeature("soft masks");
2704 warnfeature("soft masks from alpha channel",0);
2706 states[statepos].olddevice = this->device;
2707 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2708 gfxdevice_record_init(this->device);
2710 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2712 states[statepos].softmask = 1;
2715 static inline Guchar div255(int x) {
2716 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2719 void GFXOutputDev::clearSoftMask(GfxState *state)
2721 if(!states[statepos].softmask)
2723 states[statepos].softmask = 0;
2724 dbg("clearSoftMask statepos=%d", statepos);
2725 msg("<verbose> clearSoftMask");
2727 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2728 msg("<error> Error in softmask/tgroup ordering");
2732 gfxresult_t*mask = states[statepos].softmaskrecording;
2733 gfxresult_t*below = this->device->finish(this->device);
2734 this->device = states[statepos].olddevice;
2736 /* get outline of all objects below the soft mask */
2737 gfxdevice_t uniondev;
2738 gfxdevice_union_init(&uniondev, 0);
2739 gfxresult_record_replay(below, &uniondev);
2740 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2741 uniondev.finish(&uniondev);
2743 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2745 this->device->startclip(this->device, belowoutline);
2746 gfxresult_record_replay(below, this->device);
2747 gfxresult_record_replay(mask, this->device);
2748 this->device->endclip(this->device);
2749 gfxline_free(belowoutline);
2752 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2754 gfxdevice_t belowrender;
2755 gfxdevice_render_init(&belowrender);
2756 belowrender.setparameter(&belowrender, "fillwhite", "1"); //for isolated=0?
2757 belowrender.setparameter(&belowrender, "antialize", "2");
2758 belowrender.startpage(&belowrender, width, height);
2759 gfxresult_record_replay(below, &belowrender);
2760 belowrender.endpage(&belowrender);
2761 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2762 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2764 gfxdevice_t maskrender;
2765 gfxdevice_render_init(&maskrender);
2766 maskrender.startpage(&maskrender, width, height);
2767 gfxresult_record_replay(mask, &maskrender);
2768 maskrender.endpage(&maskrender);
2769 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2770 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2772 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2773 msg("<fatal> Internal error in mask drawing");
2778 for(y=0;y<height;y++) {
2779 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2780 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2781 for(x=0;x<width;x++) {
2782 l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2784 /* premultiply alpha */
2785 l2->r = div255(l2->a*l2->r);
2786 l2->g = div255(l2->a*l2->g);
2787 l2->b = div255(l2->a*l2->b);
2793 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2796 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2797 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2799 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2801 mask->destroy(mask);
2802 below->destroy(below);
2803 maskresult->destroy(maskresult);
2804 belowresult->destroy(belowresult);
2805 states[statepos].softmaskrecording = 0;
2814 delete globalParams;globalParams=0;
2815 Object::memCheck(stderr);