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 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;
185 GBool GFXOutputDev::interpretType3Chars()
190 typedef struct _drawnchar
208 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
209 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
214 free(chars);chars = 0;
221 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
225 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
228 chars[num_chars].x = x;
229 chars[num_chars].y = y;
230 chars[num_chars].color = color;
231 chars[num_chars].charid = charid;
235 static char*getFontID(GfxFont*font);
237 GFXOutputDev::GFXOutputDev(parameter_t*p)
240 this->textmodeinfo = 0;
243 this->type3active = 0;
246 this->substitutepos = 0;
247 this->type3Warning = 0;
248 this->user_movex = 0;
249 this->user_movey = 0;
252 this->user_clipx1 = 0;
253 this->user_clipy1 = 0;
254 this->user_clipx2 = 0;
255 this->user_clipy2 = 0;
256 this->current_text_stroke = 0;
257 this->current_text_clip = 0;
259 this->outer_clip_box = 0;
261 this->pagebuflen = 0;
263 this->config_use_fontconfig=1;
264 this->config_break_on_warning=0;
266 this->parameters = p;
268 memset(states, 0, sizeof(states));
270 /* configure device */
272 if(!strcmp(p->name,"fontconfig")) {
273 this->config_use_fontconfig = atoi(p->value);
274 } else if(!strcmp(p->name,"breakonwarning")) {
275 this->config_break_on_warning = atoi(p->value);
281 void GFXOutputDev::setDevice(gfxdevice_t*dev)
283 parameter_t*p = this->parameters;
285 /* pass parameters to output device */
289 this->device->setparameter(this->device, p->name, p->value);
295 void GFXOutputDev::setMove(int x,int y)
297 this->user_movex = x;
298 this->user_movey = y;
301 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
303 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
304 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
306 this->user_clipx1 = x1;
307 this->user_clipy1 = y1;
308 this->user_clipx2 = x2;
309 this->user_clipy2 = y2;
312 static char*getFontID(GfxFont*font)
314 Ref*ref = font->getID();
315 GString*gstr = font->getName();
316 char* fname = gstr==0?0:gstr->getCString();
319 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
321 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
326 static char*getFontName(GfxFont*font)
329 GString*gstr = font->getName();
330 char* fname = gstr==0?0:gstr->getCString();
334 sprintf(buf, "UFONT%d", r->num);
335 fontid = strdup(buf);
337 fontid = strdup(fname);
341 char* plus = strchr(fontid, '+');
342 if(plus && plus < &fontid[strlen(fontid)-1]) {
343 fontname = strdup(plus+1);
345 fontname = strdup(fontid);
351 static char mybuf[1024];
352 static char* gfxstate2str(GfxState *state)
356 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
363 if(state->getX1()!=0.0)
364 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
365 if(state->getY1()!=0.0)
366 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
367 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
368 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
369 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
370 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
371 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
372 state->getFillColor()->c[0], state->getFillColor()->c[1]);
373 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
374 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
375 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
376 state->getFillColor()->c[0], state->getFillColor()->c[1],
377 state->getFillColor()->c[2], state->getFillColor()->c[3],
378 state->getFillColor()->c[4], state->getFillColor()->c[5],
379 state->getFillColor()->c[6], state->getFillColor()->c[7]);
380 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
381 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
382 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
383 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
384 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
385 state->getFillRGB(&rgb);
386 if(rgb.r || rgb.g || rgb.b)
387 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
388 state->getStrokeRGB(&rgb);
389 if(rgb.r || rgb.g || rgb.b)
390 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
391 if(state->getFillColorSpace()->getNComps()>1)
392 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
393 if(state->getStrokeColorSpace()->getNComps()>1)
394 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
395 if(state->getFillPattern())
396 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
397 if(state->getStrokePattern())
398 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
400 if(state->getFillOpacity()!=1.0)
401 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
402 if(state->getStrokeOpacity()!=1.0)
403 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
405 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
410 state->getLineDash(&dash, &length, &start);
414 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
415 for(t=0;t<length;t++) {
416 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
418 bufpos+=sprintf(bufpos,"]");
421 if(state->getFlatness()!=1)
422 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
423 if(state->getLineJoin()!=0)
424 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
425 if(state->getLineJoin()!=0)
426 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
427 if(state->getLineJoin()!=0)
428 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
430 if(state->getFont() && getFontID(state->getFont()))
431 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
432 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
433 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
434 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
435 if(state->getCharSpace())
436 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
437 if(state->getWordSpace())
438 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
439 if(state->getHorizScaling()!=1.0)
440 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
441 if(state->getLeading())
442 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
444 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
445 if(state->getRender())
446 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
447 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
448 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
449 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
450 if(state->getLineX())
451 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
452 if(state->getLineY())
453 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
454 bufpos+=sprintf(bufpos," ");
458 static void dumpFontInfo(char*loglevel, GfxFont*font);
459 static int lastdumps[1024];
460 static int lastdumppos = 0;
465 static void showFontError(GfxFont*font, int nr)
469 for(t=0;t<lastdumppos;t++)
470 if(lastdumps[t] == r->num)
474 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
475 lastdumps[lastdumppos++] = r->num;
477 msg("<warning> The following font caused problems:");
479 msg("<warning> The following font caused problems (substituting):");
481 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
482 dumpFontInfo("<warning>", font);
485 static void dumpFontInfo(char*loglevel, GfxFont*font)
487 char* id = getFontID(font);
488 char* name = getFontName(font);
489 Ref* r=font->getID();
490 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
492 GString*gstr = font->getTag();
494 msg("%s| Tag: %s\n", loglevel, id);
496 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
498 GfxFontType type=font->getType();
500 case fontUnknownType:
501 msg("%s| Type: unknown\n",loglevel);
504 msg("%s| Type: 1\n",loglevel);
507 msg("%s| Type: 1C\n",loglevel);
510 msg("%s| Type: 3\n",loglevel);
513 msg("%s| Type: TrueType\n",loglevel);
516 msg("%s| Type: CIDType0\n",loglevel);
519 msg("%s| Type: CIDType0C\n",loglevel);
522 msg("%s| Type: CIDType2\n",loglevel);
527 GBool embedded = font->getEmbeddedFontID(&embRef);
529 if(font->getEmbeddedFontName()) {
530 embeddedName = font->getEmbeddedFontName()->getCString();
533 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
535 gstr = font->getExtFontFile();
537 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
539 // Get font descriptor flags.
540 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
541 if(font->isSerif()) msg("%s| is serif\n", loglevel);
542 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
543 if(font->isItalic()) msg("%s| is italic\n", loglevel);
544 if(font->isBold()) msg("%s| is bold\n", loglevel);
550 //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");}
551 //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");}
554 void dump_outline(gfxline_t*line)
557 if(line->type == gfx_moveTo) {
558 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
559 } else if(line->type == gfx_lineTo) {
560 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
561 } else if(line->type == gfx_splineTo) {
562 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
568 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
570 int num = path->getNumSubpaths();
573 double lastx=0,lasty=0,posx=0,posy=0;
576 msg("<warning> empty path");
580 gfxdrawer_target_gfxline(&draw);
582 for(t = 0; t < num; t++) {
583 GfxSubpath *subpath = path->getSubpath(t);
584 int subnum = subpath->getNumPoints();
585 double bx=0,by=0,cx=0,cy=0;
587 for(s=0;s<subnum;s++) {
590 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
595 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
596 draw.lineTo(&draw, lastx, lasty);
598 draw.moveTo(&draw, x,y);
603 } else if(subpath->getCurve(s) && cpos==0) {
607 } else if(subpath->getCurve(s) && cpos==1) {
615 draw.lineTo(&draw, x,y);
617 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
624 /* fix non-closed lines */
625 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
626 draw.lineTo(&draw, lastx, lasty);
628 gfxline_t*result = (gfxline_t*)draw.result(&draw);
630 gfxline_optimize(result);
635 GBool GFXOutputDev::useTilingPatternFill()
637 infofeature("tiled patterns");
641 GBool GFXOutputDev::useShadedFills()
643 infofeature("shaded fills");
647 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
649 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
650 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
651 double miterLimit = state->getMiterLimit();
652 double width = state->getTransformedLineWidth();
655 double opaq = state->getStrokeOpacity();
657 state->getFillRGB(&rgb);
659 state->getStrokeRGB(&rgb);
661 col.r = colToByte(rgb.r);
662 col.g = colToByte(rgb.g);
663 col.b = colToByte(rgb.b);
664 col.a = (unsigned char)(opaq*255);
666 gfx_capType capType = gfx_capRound;
667 if(lineCap == 0) capType = gfx_capButt;
668 else if(lineCap == 1) capType = gfx_capRound;
669 else if(lineCap == 2) capType = gfx_capSquare;
671 gfx_joinType joinType = gfx_joinRound;
672 if(lineJoin == 0) joinType = gfx_joinMiter;
673 else if(lineJoin == 1) joinType = gfx_joinRound;
674 else if(lineJoin == 2) joinType = gfx_joinBevel;
677 double dashphase = 0;
679 state->getLineDash(&ldash, &dashnum, &dashphase);
683 if(dashnum && ldash) {
684 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
688 msg("<trace> %d dashes", dashnum);
689 msg("<trace> | phase: %f", dashphase);
690 for(t=0;t<dashnum;t++) {
692 msg("<trace> | d%-3d: %f", t, ldash[t]);
695 if(getLogLevel() >= LOGLEVEL_TRACE) {
699 line2 = gfxtool_dash_line(line, dash, dashphase);
702 msg("<trace> After dashing:");
705 if(getLogLevel() >= LOGLEVEL_TRACE) {
706 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
708 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
709 lineCap==0?"butt": (lineJoin==1?"round":"square"),
711 col.r,col.g,col.b,col.a
716 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
717 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
723 gfxcolor_t getFillColor(GfxState * state)
726 double opaq = state->getFillOpacity();
727 state->getFillRGB(&rgb);
729 col.r = colToByte(rgb.r);
730 col.g = colToByte(rgb.g);
731 col.b = colToByte(rgb.b);
732 col.a = (unsigned char)(opaq*255);
736 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
738 gfxcolor_t col = getFillColor(state);
740 if(getLogLevel() >= LOGLEVEL_TRACE) {
741 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
744 device->fill(device, line, &col);
747 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
749 if(getLogLevel() >= LOGLEVEL_TRACE) {
750 msg("<trace> clip\n");
754 device->startclip(device, line);
755 states[statepos].clipping++;
758 void GFXOutputDev::clip(GfxState *state)
760 GfxPath * path = state->getPath();
761 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
762 clipToGfxLine(state, line);
766 void GFXOutputDev::eoClip(GfxState *state)
768 GfxPath * path = state->getPath();
769 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
771 if(getLogLevel() >= LOGLEVEL_TRACE) {
772 msg("<trace> eoclip\n");
776 device->startclip(device, line);
777 states[statepos].clipping++;
781 void GFXOutputDev::endframe()
784 device->endclip(device);
788 device->endpage(device);
791 void GFXOutputDev::finish()
795 device->endclip(device);
801 GFXOutputDev::~GFXOutputDev()
806 free(this->pages); this->pages = 0;
809 fontlist_t*l = this->fontlist;
811 fontlist_t*next = l->next;
813 gfxfont_free(l->font);
814 free(l->filename);l->filename=0;
820 GBool GFXOutputDev::upsideDown()
824 GBool GFXOutputDev::useDrawChar()
829 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
830 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
832 #define RENDER_FILL 0
833 #define RENDER_STROKE 1
834 #define RENDER_FILLSTROKE 2
835 #define RENDER_INVISIBLE 3
836 #define RENDER_CLIP 4
838 static char tmp_printstr[4096];
839 char* makeStringPrintable(char*str)
841 int len = strlen(str);
856 tmp_printstr[len++] = '.';
857 tmp_printstr[len++] = '.';
858 tmp_printstr[len++] = '.';
860 tmp_printstr[len] = 0;
865 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
870 /* find out char name from unicode index
871 TODO: should be precomputed
873 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
874 if(nameToUnicodeTab[t].u == u) {
875 uniname = nameToUnicodeTab[t].name;
883 for(t=0;t<font->num_glyphs;t++) {
884 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
885 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
889 /* if we didn't find the character, maybe
890 we can find the capitalized version */
891 for(t=0;t<font->num_glyphs;t++) {
892 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
893 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
901 for(t=0;t<font->num_glyphs;t++) {
902 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
903 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
907 /* if we didn't find the character, maybe
908 we can find the capitalized version */
909 for(t=0;t<font->num_glyphs;t++) {
910 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
911 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
917 /* try to use the unicode id */
918 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
919 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
920 return font->unicode2glyph[u];
923 if(charnr>=0 && charnr<font->num_glyphs) {
924 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
932 void GFXOutputDev::beginString(GfxState *state, GString *s)
934 int render = state->getRender();
935 if(current_text_stroke) {
936 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
939 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
940 double m11,m21,m12,m22;
941 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
942 state->getFontTransMat(&m11, &m12, &m21, &m22);
943 m11 *= state->getHorizScaling();
944 m21 *= state->getHorizScaling();
946 this->current_font_matrix.m00 = m11 / 1024.0;
947 this->current_font_matrix.m01 = m12 / 1024.0;
948 this->current_font_matrix.m10 = -m21 / 1024.0;
949 this->current_font_matrix.m11 = -m22 / 1024.0;
950 this->current_font_matrix.tx = 0;
951 this->current_font_matrix.ty = 0;
953 gfxmatrix_t m = this->current_font_matrix;
955 states[statepos].textRender = render;
958 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
959 double dx, double dy,
960 double originX, double originY,
961 CharCode c, int nBytes, Unicode *_u, int uLen)
963 int render = state->getRender();
964 // check for invisible text -- this is used by Acrobat Capture
966 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
970 if(states[statepos].textRender != render)
971 msg("<error> Internal error: drawChar.render!=beginString.render");
973 gfxcolor_t col = getFillColor(state);
975 Gushort *CIDToGIDMap = 0;
976 GfxFont*font = state->getFont();
978 if(font->getType() == fontType3) {
979 /* type 3 chars are passed as graphics */
980 msg("<debug> type3 char at %f/%f", x, y);
990 if(font->isCIDFont()) {
991 GfxCIDFont*cfont = (GfxCIDFont*)font;
993 if(font->getType() == fontCIDType2)
994 CIDToGIDMap = cfont->getCIDToGID();
997 font8 = (Gfx8BitFont*)font;
998 char**enc=font8->getEncoding();
1002 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);
1005 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);
1011 charid = getGfxCharID(current_gfxfont, c, name, u);
1013 charid = getGfxCharID(current_gfxfont, c, name, -1);
1016 /* multiple unicodes- should usually map to a ligature.
1017 if the ligature doesn't exist, we need to draw
1018 the characters one-by-one. */
1020 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1021 for(t=0;t<uLen;t++) {
1022 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1028 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1029 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1033 gfxmatrix_t m = this->current_font_matrix;
1034 state->transform(x, y, &m.tx, &m.ty);
1035 m.tx += user_movex + clipmovex;
1036 m.ty += user_movey + clipmovey;
1038 if(render == RENDER_FILL) {
1039 device->drawchar(device, current_gfxfont, charid, &col, &m);
1041 msg("<debug> Drawing glyph %d as shape", charid);
1043 msg("<notice> Some texts will be rendered as shape");
1046 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1047 gfxline_t*tglyph = gfxline_clone(glyph);
1048 gfxline_transform(tglyph, &m);
1049 if((render&3) != RENDER_INVISIBLE) {
1050 gfxline_t*add = gfxline_clone(tglyph);
1051 current_text_stroke = gfxline_append(current_text_stroke, add);
1053 if(render&RENDER_CLIP) {
1054 gfxline_t*add = gfxline_clone(tglyph);
1055 current_text_clip = gfxline_append(current_text_clip, add);
1057 gfxline_free(tglyph);
1061 void GFXOutputDev::endString(GfxState *state)
1063 int render = state->getRender();
1064 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1065 if(states[statepos].textRender != render)
1066 msg("<error> Internal error: drawChar.render!=beginString.render");
1068 if(current_text_stroke) {
1069 /* fillstroke and stroke text rendering objects we can process right
1070 now (as there may be texts of other rendering modes in this
1071 text object)- clipping objects have to wait until endTextObject,
1073 device->setparameter(device, "mark","TXT");
1074 if((render&3) == RENDER_FILL) {
1075 fillGfxLine(state, current_text_stroke);
1076 gfxline_free(current_text_stroke);
1077 current_text_stroke = 0;
1078 } else if((render&3) == RENDER_FILLSTROKE) {
1079 fillGfxLine(state, current_text_stroke);
1080 strokeGfxline(state, current_text_stroke);
1081 gfxline_free(current_text_stroke);
1082 current_text_stroke = 0;
1083 } else if((render&3) == RENDER_STROKE) {
1084 strokeGfxline(state, current_text_stroke);
1085 gfxline_free(current_text_stroke);
1086 current_text_stroke = 0;
1088 device->setparameter(device, "mark","");
1092 void GFXOutputDev::endTextObject(GfxState *state)
1094 int render = state->getRender();
1095 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1096 if(states[statepos].textRender != render)
1097 msg("<error> Internal error: drawChar.render!=beginString.render");
1099 if(current_text_clip) {
1100 device->setparameter(device, "mark","TXT");
1101 clipToGfxLine(state, current_text_clip);
1102 device->setparameter(device, "mark","");
1103 gfxline_free(current_text_clip);
1104 current_text_clip = 0;
1108 /* the logic seems to be as following:
1109 first, beginType3Char is called, with the charcode and the coordinates.
1110 if this function returns true, it already knew about the char and has now drawn it.
1111 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1112 the all draw operations until endType3Char are part of the char (which in this moment is
1113 at the position first passed to beginType3Char). the char ends with endType3Char.
1115 The drawing operations between beginType3Char and endType3Char are somewhat different to
1116 the normal ones. For example, the fillcolor equals the stroke color.
1119 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1121 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1123 /* the character itself is going to be passed using the draw functions */
1124 return gFalse; /* gTrue= is_in_cache? */
1127 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1128 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1130 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1131 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1135 void GFXOutputDev::endType3Char(GfxState *state)
1138 msg("<debug> endType3Char");
1141 void GFXOutputDev::startFrame(int width, int height)
1143 if(outer_clip_box) {
1144 device->endclip(device);
1148 device->startpage(device, width, height);
1149 this->width = width;
1150 this->height = height;
1153 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1155 this->currentpage = pageNum;
1157 int rot = doc->getPageRotate(1);
1160 gfxline_t clippath[5];
1162 white.r = white.g = white.b = white.a = 255;
1164 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1165 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1166 Use CropBox, not MediaBox, as page size
1173 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1174 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1176 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1177 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1179 this->clipmovex = -(int)x1;
1180 this->clipmovey = -(int)y1;
1182 /* apply user clip box */
1183 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1184 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1185 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1186 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1187 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1188 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1191 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1193 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);
1195 msg("<verbose> page is rotated %d degrees\n", rot);
1197 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1198 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1199 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1200 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1201 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1202 device->startclip(device, clippath); outer_clip_box = 1;
1203 device->fill(device, clippath, &white);
1207 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1209 double x1, y1, x2, y2, w;
1210 gfxline_t points[5];
1213 msg("<debug> drawlink\n");
1215 link->getRect(&x1, &y1, &x2, &y2);
1216 cvtUserToDev(x1, y1, &x, &y);
1217 points[0].type = gfx_moveTo;
1218 points[0].x = points[4].x = x + user_movex + clipmovex;
1219 points[0].y = points[4].y = y + user_movey + clipmovey;
1220 points[0].next = &points[1];
1221 cvtUserToDev(x2, y1, &x, &y);
1222 points[1].type = gfx_lineTo;
1223 points[1].x = x + user_movex + clipmovex;
1224 points[1].y = y + user_movey + clipmovey;
1225 points[1].next = &points[2];
1226 cvtUserToDev(x2, y2, &x, &y);
1227 points[2].type = gfx_lineTo;
1228 points[2].x = x + user_movex + clipmovex;
1229 points[2].y = y + user_movey + clipmovey;
1230 points[2].next = &points[3];
1231 cvtUserToDev(x1, y2, &x, &y);
1232 points[3].type = gfx_lineTo;
1233 points[3].x = x + user_movex + clipmovex;
1234 points[3].y = y + user_movey + clipmovey;
1235 points[3].next = &points[4];
1236 cvtUserToDev(x1, y1, &x, &y);
1237 points[4].type = gfx_lineTo;
1238 points[4].x = x + user_movex + clipmovex;
1239 points[4].y = y + user_movey + clipmovey;
1242 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1243 points[0].x, points[0].y,
1244 points[1].x, points[1].y,
1245 points[2].x, points[2].y,
1246 points[3].x, points[3].y);
1248 LinkAction*action=link->getAction();
1254 msg("<trace> drawlink action=%d\n", action->getKind());
1255 switch(action->getKind())
1259 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1260 LinkDest *dest=NULL;
1261 if (ha->getDest()==NULL)
1262 dest=catalog->findDest(ha->getNamedDest());
1263 else dest=ha->getDest();
1265 if (dest->isPageRef()){
1266 Ref pageref=dest->getPageRef();
1267 page=catalog->findPage(pageref.num,pageref.gen);
1269 else page=dest->getPageNum();
1270 sprintf(buf, "%d", page);
1277 LinkGoToR*l = (LinkGoToR*)action;
1278 GString*g = l->getFileName();
1280 s = strdup(g->getCString());
1282 /* if the GoToR link has no filename, then
1283 try to find a refernce in the *local*
1285 GString*g = l->getNamedDest();
1287 s = strdup(g->getCString());
1293 LinkNamed*l = (LinkNamed*)action;
1294 GString*name = l->getName();
1296 s = strdup(name->lowerCase()->getCString());
1297 named = name->getCString();
1300 if(strstr(s, "next") || strstr(s, "forward"))
1302 page = currentpage + 1;
1304 else if(strstr(s, "prev") || strstr(s, "back"))
1306 page = currentpage - 1;
1308 else if(strstr(s, "last") || strstr(s, "end"))
1310 if(pages && pagepos>0)
1311 page = pages[pagepos-1];
1313 else if(strstr(s, "first") || strstr(s, "top"))
1321 case actionLaunch: {
1323 LinkLaunch*l = (LinkLaunch*)action;
1324 GString * str = new GString(l->getFileName());
1325 GString * params = l->getParams();
1327 str->append(params);
1328 s = strdup(str->getCString());
1335 LinkURI*l = (LinkURI*)action;
1336 GString*g = l->getURI();
1338 url = g->getCString();
1343 case actionUnknown: {
1345 LinkUnknown*l = (LinkUnknown*)action;
1350 msg("<error> Unknown link type!\n");
1355 if(!s) s = strdup("-?-");
1357 msg("<trace> drawlink s=%s\n", s);
1359 if(!linkinfo && (page || s))
1361 msg("<notice> File contains links");
1369 for(t=1;t<=pagepos;t++) {
1370 if(pages[t]==page) {
1379 sprintf(buf, "page%d", lpage);
1380 device->drawlink(device, points, buf);
1384 device->drawlink(device, points, s);
1387 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1391 void GFXOutputDev::saveState(GfxState *state) {
1392 dbg("saveState");dbgindent+=2;
1394 msg("<trace> saveState\n");
1397 msg("<error> Too many nested states in pdf.");
1401 states[statepos].textRender = states[statepos-1].textRender;
1402 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1403 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1404 states[statepos].clipping = 0;
1407 void GFXOutputDev::restoreState(GfxState *state) {
1408 dbgindent-=2; dbg("restoreState");
1411 msg("<error> Invalid restoreState");
1414 msg("<trace> restoreState");
1415 if(states[statepos].softmask) {
1416 clearSoftMask(state);
1419 while(states[statepos].clipping) {
1420 device->endclip(device);
1421 states[statepos].clipping--;
1426 char* GFXOutputDev::searchFont(char*name)
1430 int is_standard_font = 0;
1432 msg("<verbose> SearchFont(%s)", name);
1434 /* see if it is a pdf standard font */
1435 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1437 if(!strcmp(name, pdf2t1map[i].pdffont))
1439 name = pdf2t1map[i].filename;
1440 is_standard_font = 1;
1444 /* look in all font files */
1445 for(i=0;i<fontnum;i++)
1447 if(strstr(fonts[i].filename, name))
1449 if(!fonts[i].used) {
1452 if(!is_standard_font)
1453 msg("<notice> Using %s for %s", fonts[i].filename, name);
1455 return strdup(fonts[i].filename);
1461 void GFXOutputDev::updateLineWidth(GfxState *state)
1463 double width = state->getTransformedLineWidth();
1464 //swfoutput_setlinewidth(&device, width);
1467 void GFXOutputDev::updateLineCap(GfxState *state)
1469 int c = state->getLineCap();
1472 void GFXOutputDev::updateLineJoin(GfxState *state)
1474 int j = state->getLineJoin();
1477 void GFXOutputDev::updateFillColor(GfxState *state)
1480 double opaq = state->getFillOpacity();
1481 state->getFillRGB(&rgb);
1483 void GFXOutputDev::updateFillOpacity(GfxState *state)
1486 double opaq = state->getFillOpacity();
1487 state->getFillRGB(&rgb);
1488 dbg("update fillopaq %f", opaq);
1490 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1492 double opaq = state->getFillOpacity();
1493 dbg("update strokeopaq %f", opaq);
1495 void GFXOutputDev::updateFillOverprint(GfxState *state)
1497 double opaq = state->getFillOverprint();
1498 dbg("update filloverprint %f", opaq);
1500 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1502 double opaq = state->getStrokeOverprint();
1503 dbg("update strokeoverprint %f", opaq);
1505 void GFXOutputDev::updateTransfer(GfxState *state)
1507 dbg("update transfer");
1511 void GFXOutputDev::updateStrokeColor(GfxState *state)
1514 double opaq = state->getStrokeOpacity();
1515 state->getStrokeRGB(&rgb);
1518 void FoFiWrite(void *stream, char *data, int len)
1520 int ret = fwrite(data, len, 1, (FILE*)stream);
1523 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1525 char*tmpFileName = NULL;
1531 Object refObj, strObj;
1533 tmpFileName = mktmpname(namebuf);
1536 ret = font->getEmbeddedFontID(&embRef);
1538 msg("<verbose> Didn't get embedded font id");
1539 /* not embedded- the caller should now search the font
1540 directories for this font */
1544 f = fopen(tmpFileName, "wb");
1546 msg("<error> Couldn't create temporary Type 1 font file");
1550 /*if(font->isCIDFont()) {
1551 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1552 GString c = cidFont->getCollection();
1553 msg("<notice> Collection: %s", c.getCString());
1556 //if (font->getType() == fontType1C) {
1557 if (0) { //font->getType() == fontType1C) {
1558 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1560 msg("<error> Couldn't read embedded font file");
1563 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1565 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1566 //cvt->convertToCIDType0("test", f);
1567 //cvt->convertToType0("test", f);
1570 } else if(font->getType() == fontTrueType) {
1571 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1572 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1574 msg("<error> Couldn't read embedded font file");
1577 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1578 cvt->writeTTF(FoFiWrite, f);
1582 font->getEmbeddedFontID(&embRef);
1583 refObj.initRef(embRef.num, embRef.gen);
1584 refObj.fetch(ref, &strObj);
1586 strObj.streamReset();
1591 f4[t] = strObj.streamGetChar();
1592 f4c[t] = (char)f4[t];
1597 if(!strncmp(f4c, "true", 4)) {
1598 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1599 Change this on the fly */
1600 f4[0] = f4[2] = f4[3] = 0;
1608 while ((c = strObj.streamGetChar()) != EOF) {
1612 strObj.streamClose();
1617 return strdup(tmpFileName);
1620 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1622 char*name = getFontName(gfxFont);
1626 if(!this->config_use_fontconfig)
1629 #ifdef HAVE_FONTCONFIG
1630 FcPattern *pattern, *match;
1634 static int fcinitcalled = false;
1636 msg("<debug> searchForSuitableFont(%s)", name);
1638 // call init ony once
1639 if (!fcinitcalled) {
1640 msg("<debug> Initializing FontConfig...");
1641 fcinitcalled = true;
1643 msg("<debug> FontConfig Initialization failed. Disabling.");
1644 config_use_fontconfig = 0;
1647 msg("<debug> ...initialized FontConfig");
1650 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1651 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1652 if (gfxFont->isItalic()) // check for italic
1653 msg("<debug> FontConfig: Adding Italic Slant");
1654 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1655 if (gfxFont->isBold()) // check for bold
1656 msg("<debug> FontConfig: Adding Bold Weight");
1657 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1659 msg("<debug> FontConfig: Try to match...");
1660 // configure and match using the original font name
1661 FcConfigSubstitute(0, pattern, FcMatchPattern);
1662 FcDefaultSubstitute(pattern);
1663 match = FcFontMatch(0, pattern, &result);
1665 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1666 msg("<debug> FontConfig: family=%s", (char*)v);
1667 // if we get an exact match
1668 if (strcmp((char *)v, name) == 0) {
1669 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1670 filename = strdup((char*)v); // mem leak
1671 char *nfn = strrchr(filename, '/');
1672 if(nfn) fontname = strdup(nfn+1);
1673 else fontname = filename;
1675 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1677 // initialize patterns
1678 FcPatternDestroy(pattern);
1679 FcPatternDestroy(match);
1681 // now match against serif etc.
1682 if (gfxFont->isSerif()) {
1683 msg("<debug> FontConfig: Create Serif Family Pattern");
1684 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1685 } else if (gfxFont->isFixedWidth()) {
1686 msg("<debug> FontConfig: Create Monospace Family Pattern");
1687 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1689 msg("<debug> FontConfig: Create Sans Family Pattern");
1690 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1694 if (gfxFont->isItalic()) {
1695 msg("<debug> FontConfig: Adding Italic Slant");
1696 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1699 if (gfxFont->isBold()) {
1700 msg("<debug> FontConfig: Adding Bold Weight");
1701 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1704 msg("<debug> FontConfig: Try to match... (2)");
1705 // configure and match using serif etc
1706 FcConfigSubstitute (0, pattern, FcMatchPattern);
1707 FcDefaultSubstitute (pattern);
1708 match = FcFontMatch (0, pattern, &result);
1710 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1711 filename = strdup((char*)v); // mem leak
1712 char *nfn = strrchr(filename, '/');
1713 if(nfn) fontname = strdup(nfn+1);
1714 else fontname = filename;
1716 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1720 //printf("FONTCONFIG: pattern");
1721 //FcPatternPrint(pattern);
1722 //printf("FONTCONFIG: match");
1723 //FcPatternPrint(match);
1725 FcPatternDestroy(pattern);
1726 FcPatternDestroy(match);
1728 pdfswf_addfont(filename);
1735 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1737 char*fontname = 0, *filename = 0;
1738 msg("<notice> substituteFont(%s)", oldname);
1740 if(!(fontname = searchForSuitableFont(gfxFont))) {
1741 fontname = "Times-Roman";
1743 filename = searchFont(fontname);
1745 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1749 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1750 msg("<fatal> Too many fonts in file.");
1754 substitutesource[substitutepos] = strdup(oldname); //mem leak
1755 substitutetarget[substitutepos] = fontname;
1756 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1759 return strdup(filename); //mem leak
1762 void unlinkfont(char* filename)
1769 if(!strncmp(&filename[l-4],".afm",4)) {
1770 memcpy(&filename[l-4],".pfb",4);
1772 memcpy(&filename[l-4],".pfa",4);
1774 memcpy(&filename[l-4],".afm",4);
1777 if(!strncmp(&filename[l-4],".pfa",4)) {
1778 memcpy(&filename[l-4],".afm",4);
1780 memcpy(&filename[l-4],".pfa",4);
1783 if(!strncmp(&filename[l-4],".pfb",4)) {
1784 memcpy(&filename[l-4],".afm",4);
1786 memcpy(&filename[l-4],".pfb",4);
1791 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1797 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1800 fontlist_t*last=0,*l = this->fontlist;
1803 msg("<error> Internal Error: FontID is null");
1805 /* TODO: should this be part of the state? */
1808 if(!strcmp(l->font->id, id)) {
1809 current_gfxfont = l->font;
1811 device->addfont(device, current_gfxfont);
1816 if(!filename) return 0;
1818 /* A font size of e.g. 9 means the font will be scaled down by
1819 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1820 we have to divide 0.05 by (fontsize/1024)
1822 double quality = (1024 * 0.05) / maxSize;
1824 msg("<verbose> Loading %s...", filename);
1825 font = gfxfont_load(id, filename, quality);
1827 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1830 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1834 l->filename = strdup(filename);
1836 current_gfxfont = l->font;
1842 device->addfont(device, current_gfxfont);
1846 void GFXOutputDev::updateFont(GfxState *state)
1848 GfxFont*gfxFont = state->getFont();
1854 char * fontid = getFontID(gfxFont);
1855 char * fontname = getFontName(gfxFont);
1857 double maxSize = 1.0;
1860 maxSize = this->info->getMaximumFontSize(fontid);
1864 /* first, look if we substituted this font before-
1865 this way, we don't initialize the T1 Fonts
1867 for(t=0;t<substitutepos;t++) {
1868 if(!strcmp(fontid, substitutesource[t])) {
1869 free(fontid);fontid=0;
1870 fontid = strdup(substitutetarget[t]);
1875 /* second, see if this is a font which was used before-
1876 if so, we are done */
1877 if(setGfxFont(fontid, fontname, 0, 0)) {
1882 /* if(swfoutput_queryfont(&device, fontid))
1883 swfoutput_setfont(&device, fontid, 0);
1885 msg("<debug> updateFont(%s) [cached]", fontid);
1889 // look for Type 3 font
1890 if (gfxFont->getType() == fontType3) {
1892 type3Warning = gTrue;
1893 showFontError(gfxFont, 2);
1900 /* now either load the font, or find a substitution */
1903 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1908 (gfxFont->getType() == fontType1 ||
1909 gfxFont->getType() == fontType1C ||
1910 gfxFont->getType() == fontCIDType0C ||
1911 gfxFont->getType() == fontTrueType ||
1912 gfxFont->getType() == fontCIDType2
1915 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1916 if(!fileName) showFontError(gfxFont,0);
1919 fileName = searchFont(fontname);
1920 if(!fileName) showFontError(gfxFont,0);
1923 char * fontname = getFontName(gfxFont);
1924 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1927 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1929 msg("<warning> Try specifying one or more font directories");
1931 fileName = substituteFont(gfxFont, fontid);
1934 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1935 msg("<notice> Font is now %s (%s)", fontid, fileName);
1939 msg("<error> Couldn't set font %s\n", fontid);
1945 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1946 dumpFontInfo("<verbose>", gfxFont);
1948 //swfoutput_setfont(&device, fontid, fileName);
1950 if(!setGfxFont(fontid, fontname, 0, 0)) {
1951 setGfxFont(fontid, fontname, fileName, maxSize);
1955 unlinkfont(fileName);
1965 #define SQR(x) ((x)*(x))
1967 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1969 if((newwidth<2 || newheight<2) ||
1970 (width<=newwidth || height<=newheight))
1972 unsigned char*newdata;
1974 newdata= (unsigned char*)malloc(newwidth*newheight);
1976 double fx = (double)(width)/newwidth;
1977 double fy = (double)(height)/newheight;
1979 int blocksize = (int)(8192/(fx*fy));
1980 int r = 8192*256/palettesize;
1981 for(x=0;x<newwidth;x++) {
1982 double ex = px + fx;
1983 int fromx = (int)px;
1985 int xweight1 = (int)(((fromx+1)-px)*256);
1986 int xweight2 = (int)((ex-tox)*256);
1988 for(y=0;y<newheight;y++) {
1989 double ey = py + fy;
1990 int fromy = (int)py;
1992 int yweight1 = (int)(((fromy+1)-py)*256);
1993 int yweight2 = (int)((ey-toy)*256);
1996 for(xx=fromx;xx<=tox;xx++)
1997 for(yy=fromy;yy<=toy;yy++) {
1998 int b = 1-data[width*yy+xx];
2000 if(xx==fromx) weight = (weight*xweight1)/256;
2001 if(xx==tox) weight = (weight*xweight2)/256;
2002 if(yy==fromy) weight = (weight*yweight1)/256;
2003 if(yy==toy) weight = (weight*yweight2)/256;
2006 //if(a) a=(palettesize-1)*r/blocksize;
2007 newdata[y*newwidth+x] = (a*blocksize)/r;
2015 #define IMAGE_TYPE_JPEG 0
2016 #define IMAGE_TYPE_LOSSLESS 1
2018 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2019 double x1,double y1,
2020 double x2,double y2,
2021 double x3,double y3,
2022 double x4,double y4, int type)
2024 gfxcolor_t*newpic=0;
2026 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2027 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2029 gfxline_t p1,p2,p3,p4,p5;
2030 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2031 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2032 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2033 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2034 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2036 {p1.x = (int)(p1.x*20)/20.0;
2037 p1.y = (int)(p1.y*20)/20.0;
2038 p2.x = (int)(p2.x*20)/20.0;
2039 p2.y = (int)(p2.y*20)/20.0;
2040 p3.x = (int)(p3.x*20)/20.0;
2041 p3.y = (int)(p3.y*20)/20.0;
2042 p4.x = (int)(p4.x*20)/20.0;
2043 p4.y = (int)(p4.y*20)/20.0;
2044 p5.x = (int)(p5.x*20)/20.0;
2045 p5.y = (int)(p5.y*20)/20.0;
2052 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2053 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2058 img.data = (gfxcolor_t*)data;
2062 if(type == IMAGE_TYPE_JPEG)
2063 /* TODO: pass image_dpi to device instead */
2064 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2066 dev->fillbitmap(dev, &p1, &img, &m, 0);
2069 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2070 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2072 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2075 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2076 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2078 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2082 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2083 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2084 GBool inlineImg, int mask, int*maskColors,
2085 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2087 double x1,y1,x2,y2,x3,y3,x4,y4;
2088 ImageStream *imgStr;
2093 unsigned char* maskbitmap = 0;
2096 ncomps = colorMap->getNumPixelComps();
2097 bits = colorMap->getBits();
2102 unsigned char buf[8];
2103 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2105 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2106 imgMaskStr->reset();
2107 unsigned char pal[256];
2108 int n = 1 << colorMap->getBits();
2113 maskColorMap->getGray(pixBuf, &gray);
2114 pal[t] = colToByte(gray);
2116 for (y = 0; y < maskHeight; y++) {
2117 for (x = 0; x < maskWidth; x++) {
2118 imgMaskStr->getPixel(buf);
2119 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2124 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2125 imgMaskStr->reset();
2126 for (y = 0; y < maskHeight; y++) {
2127 for (x = 0; x < maskWidth; x++) {
2128 imgMaskStr->getPixel(buf);
2130 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2138 imgStr = new ImageStream(str, width, ncomps,bits);
2141 if(!width || !height || (height<=1 && width<=1))
2143 msg("<verbose> Ignoring %d by %d image", width, height);
2144 unsigned char buf[8];
2146 for (y = 0; y < height; ++y)
2147 for (x = 0; x < width; ++x) {
2148 imgStr->getPixel(buf);
2156 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2157 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2158 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2159 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2161 if(!pbminfo && !(str->getKind()==strDCT)) {
2163 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2167 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2169 if(!jpeginfo && (str->getKind()==strDCT)) {
2170 msg("<notice> file contains jpeg pictures");
2176 unsigned char buf[8];
2178 unsigned char*pic = new unsigned char[width*height];
2179 gfxcolor_t pal[256];
2181 state->getFillRGB(&rgb);
2183 memset(pal,255,sizeof(pal));
2184 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2185 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2186 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2187 pal[0].a = 255; pal[1].a = 0;
2190 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2191 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2192 for (y = 0; y < height; ++y)
2193 for (x = 0; x < width; ++x)
2195 imgStr->getPixel(buf);
2198 pic[width*y+x] = buf[0];
2201 /* the size of the drawn image is added to the identifier
2202 as the same image may require different bitmaps if displayed
2203 at different sizes (due to antialiasing): */
2206 unsigned char*pic2 = 0;
2209 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2218 height = realheight;
2222 /* make a black/white palette */
2224 float r = 255/(numpalette-1);
2226 for(t=0;t<numpalette;t++) {
2227 pal[t].r = colToByte(rgb.r);
2228 pal[t].g = colToByte(rgb.g);
2229 pal[t].b = colToByte(rgb.b);
2230 pal[t].a = (unsigned char)(t*r);
2234 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2235 for (y = 0; y < height; ++y) {
2236 for (x = 0; x < width; ++x) {
2237 pic2[width*y+x] = pal[pic[y*width+x]];
2240 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2244 if(maskbitmap) free(maskbitmap);
2250 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2251 gfxcolor_t*pic=new gfxcolor_t[width*height];
2252 for (y = 0; y < height; ++y) {
2253 for (x = 0; x < width; ++x) {
2254 imgStr->getPixel(pixBuf);
2255 colorMap->getRGB(pixBuf, &rgb);
2256 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2257 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2258 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2259 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2261 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2265 if(str->getKind()==strDCT)
2266 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2268 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2271 if(maskbitmap) free(maskbitmap);
2274 gfxcolor_t*pic=new gfxcolor_t[width*height];
2275 gfxcolor_t pal[256];
2276 int n = 1 << colorMap->getBits();
2278 for(t=0;t<256;t++) {
2280 colorMap->getRGB(pixBuf, &rgb);
2282 {/*if(maskColors && *maskColors==t) {
2283 msg("<notice> Color %d is transparent", t);
2284 if (imgData->maskColors) {
2286 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2287 if (pix[i] < imgData->maskColors[2*i] ||
2288 pix[i] > imgData->maskColors[2*i+1]) {
2303 pal[t].r = (unsigned char)(colToByte(rgb.r));
2304 pal[t].g = (unsigned char)(colToByte(rgb.g));
2305 pal[t].b = (unsigned char)(colToByte(rgb.b));
2306 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2309 for (y = 0; y < height; ++y) {
2310 for (x = 0; x < width; ++x) {
2311 imgStr->getPixel(pixBuf);
2312 pic[width*y+x] = pal[pixBuf[0]];
2314 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2318 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2322 if(maskbitmap) free(maskbitmap);
2327 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2328 int width, int height, GBool invert,
2331 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2332 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2333 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2336 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2337 int width, int height, GfxImageColorMap *colorMap,
2338 int *maskColors, GBool inlineImg)
2340 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2341 colorMap?"colorMap":"no colorMap",
2342 maskColors?"maskColors":"no maskColors",
2344 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2345 colorMap?"colorMap":"no colorMap",
2346 maskColors?"maskColors":"no maskColors",
2349 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2350 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2351 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2354 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2355 int width, int height,
2356 GfxImageColorMap *colorMap,
2357 Stream *maskStr, int maskWidth, int maskHeight,
2360 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskWidth, maskHeight);
2363 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2364 colorMap?"colorMap":"no colorMap",
2365 maskWidth, maskHeight);
2367 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2368 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2369 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2372 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2373 int width, int height,
2374 GfxImageColorMap *colorMap,
2376 int maskWidth, int maskHeight,
2377 GfxImageColorMap *maskColorMap)
2379 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2380 colorMap?"colorMap":"no colorMap",
2381 maskWidth, maskHeight);
2382 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2383 colorMap?"colorMap":"no colorMap",
2384 maskWidth, maskHeight);
2386 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2387 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2388 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2391 void GFXOutputDev::stroke(GfxState *state)
2395 GfxPath * path = state->getPath();
2396 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2397 strokeGfxline(state, line);
2401 void GFXOutputDev::fill(GfxState *state)
2403 gfxcolor_t col = getFillColor(state);
2404 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2406 GfxPath * path = state->getPath();
2407 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2408 fillGfxLine(state, line);
2412 void GFXOutputDev::eoFill(GfxState *state)
2414 gfxcolor_t col = getFillColor(state);
2415 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2417 GfxPath * path = state->getPath();
2418 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2419 fillGfxLine(state, line);
2424 static char* dirseparator()
2433 void addGlobalFont(char*filename)
2436 memset(&f, 0, sizeof(fontfile_t));
2437 f.filename = filename;
2438 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2439 msg("<verbose> Adding font \"%s\".", filename);
2440 fonts[fontnum++] = f;
2442 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2446 void addGlobalLanguageDir(char*dir)
2449 globalParams = new GlobalParams("");
2451 msg("<notice> Adding %s to language pack directories", dir);
2455 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2456 strcpy(config_file, dir);
2457 strcat(config_file, dirseparator());
2458 strcat(config_file, "add-to-xpdfrc");
2460 fi = fopen(config_file, "rb");
2462 msg("<error> Could not open %s", config_file);
2465 globalParams->parseFile(new GString(config_file), fi);
2469 void addGlobalFontDir(char*dirname)
2471 #ifdef HAVE_DIRENT_H
2472 msg("<notice> Adding %s to font directories", dirname);
2473 lastfontdir = strdup(dirname);
2474 DIR*dir = opendir(dirname);
2476 msg("<warning> Couldn't open directory %s\n", dirname);
2481 ent = readdir (dir);
2485 char*name = ent->d_name;
2491 if(!strncasecmp(&name[l-4], ".pfa", 4))
2493 if(!strncasecmp(&name[l-4], ".pfb", 4))
2495 if(!strncasecmp(&name[l-4], ".ttf", 4))
2499 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2500 strcpy(fontname, dirname);
2501 strcat(fontname, dirseparator());
2502 strcat(fontname, name);
2503 addGlobalFont(fontname);
2508 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2512 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2518 this->pagebuflen = 1024;
2519 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2520 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2522 while(pdfpage >= this->pagebuflen)
2524 int oldlen = this->pagebuflen;
2525 this->pagebuflen+=1024;
2526 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2527 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2530 this->pages[pdfpage] = outputpage;
2531 if(pdfpage>this->pagepos)
2532 this->pagepos = pdfpage;
2538 double width,height;
2541 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2543 double xMin, yMin, xMax, yMax, x, y;
2544 double tx, ty, w, h;
2545 // transform the bbox
2546 state->transform(bbox[0], bbox[1], &x, &y);
2549 state->transform(bbox[0], bbox[3], &x, &y);
2552 } else if (x > xMax) {
2557 } else if (y > yMax) {
2560 state->transform(bbox[2], bbox[1], &x, &y);
2563 } else if (x > xMax) {
2568 } else if (y > yMax) {
2571 state->transform(bbox[2], bbox[3], &x, &y);
2574 } else if (x > xMax) {
2579 } else if (y > yMax) {
2582 tx = (int)floor(xMin);
2585 } else if (tx > width) {
2588 ty = (int)floor(yMin);
2591 } else if (ty > height) {
2594 w = (int)ceil(xMax) - tx + 1;
2595 if (tx + w > width) {
2601 h = (int)ceil(yMax) - ty + 1;
2602 if (ty + h > height) {
2616 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2617 GfxColorSpace *blendingColorSpace,
2618 GBool isolated, GBool knockout,
2621 char*colormodename = "";
2622 BBox rect = mkBBox(state, bbox, this->width, this->height);
2624 if(blendingColorSpace) {
2625 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2627 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2628 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2629 msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2631 states[statepos].createsoftmask |= forSoftMask;
2632 states[statepos].transparencygroup = !forSoftMask;
2633 states[statepos].isolated = isolated;
2635 states[statepos].olddevice = this->device;
2636 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2638 gfxdevice_record_init(this->device);
2640 /*if(!forSoftMask) { ////???
2641 state->setFillOpacity(0.0);
2646 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2649 dbg("endTransparencyGroup");
2650 msg("<verbose> endTransparencyGroup");
2652 gfxdevice_t*r = this->device;
2654 this->device = states[statepos].olddevice;
2656 if(states[statepos].createsoftmask) {
2657 states[statepos-1].softmaskrecording = r->finish(r);
2659 states[statepos-1].grouprecording = r->finish(r);
2662 states[statepos].createsoftmask = 0;
2663 states[statepos].transparencygroup = 0;
2667 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2669 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2670 "colordodge","colorburn","hardlight","softlight","difference",
2671 "exclusion","hue","saturation","color","luminosity"};
2673 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2674 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2676 if(state->getBlendMode() == gfxBlendNormal)
2677 infofeature("transparency groups");
2680 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2681 warnfeature(buffer, 0);
2684 gfxresult_t*grouprecording = states[statepos].grouprecording;
2686 if(state->getBlendMode() == gfxBlendNormal) {
2688 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2689 gfxresult_record_replay(grouprecording, &ops);
2692 grouprecording->destroy(grouprecording);
2694 states[statepos].grouprecording = 0;
2697 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2699 /* alpha = 1: retrieve mask values from alpha layer
2700 alpha = 0: retrieve mask values from luminance */
2701 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2702 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2703 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2704 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2706 infofeature("soft masks");
2708 warnfeature("soft masks from alpha channel",0);
2710 states[statepos].olddevice = this->device;
2711 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2712 gfxdevice_record_init(this->device);
2714 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2716 states[statepos].softmask = 1;
2717 states[statepos].softmask_alpha = alpha;
2720 static inline Guchar div255(int x) {
2721 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2724 void GFXOutputDev::clearSoftMask(GfxState *state)
2726 if(!states[statepos].softmask)
2728 states[statepos].softmask = 0;
2729 dbg("clearSoftMask statepos=%d", statepos);
2730 msg("<verbose> clearSoftMask");
2732 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2733 msg("<error> Error in softmask/tgroup ordering");
2737 gfxresult_t*mask = states[statepos].softmaskrecording;
2738 gfxresult_t*below = this->device->finish(this->device);
2739 this->device = states[statepos].olddevice;
2741 /* get outline of all objects below the soft mask */
2742 gfxdevice_t uniondev;
2743 gfxdevice_union_init(&uniondev, 0);
2744 gfxresult_record_replay(below, &uniondev);
2745 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2746 uniondev.finish(&uniondev);
2748 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2750 this->device->startclip(this->device, belowoutline);
2751 gfxresult_record_replay(below, this->device);
2752 gfxresult_record_replay(mask, this->device);
2753 this->device->endclip(this->device);
2754 gfxline_free(belowoutline);
2757 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2759 gfxdevice_t belowrender;
2760 gfxdevice_render_init(&belowrender);
2761 if(states[statepos+1].isolated) {
2762 belowrender.setparameter(&belowrender, "fillwhite", "1");
2764 belowrender.setparameter(&belowrender, "antialize", "2");
2765 belowrender.startpage(&belowrender, width, height);
2766 gfxresult_record_replay(below, &belowrender);
2767 belowrender.endpage(&belowrender);
2768 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2769 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2770 writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2772 gfxdevice_t maskrender;
2773 gfxdevice_render_init(&maskrender);
2774 maskrender.startpage(&maskrender, width, height);
2775 gfxresult_record_replay(mask, &maskrender);
2776 maskrender.endpage(&maskrender);
2777 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2778 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2780 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2781 msg("<fatal> Internal error in mask drawing");
2786 for(y=0;y<height;y++) {
2787 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2788 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2789 for(x=0;x<width;x++) {
2791 if(states[statepos].softmask_alpha) {
2794 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2797 /* premultiply alpha */
2798 l2->a = div255(alpha*l2->a);
2799 l2->r = div255(alpha*l2->r);
2800 l2->g = div255(alpha*l2->g);
2801 l2->b = div255(alpha*l2->b);
2807 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2810 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2811 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2813 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2815 mask->destroy(mask);
2816 below->destroy(below);
2817 maskresult->destroy(maskresult);
2818 belowresult->destroy(belowresult);
2819 states[statepos].softmaskrecording = 0;
2826 delete globalParams;globalParams=0;
2827 Object::memCheck(stderr);