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);
1203 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1205 double x1, y1, x2, y2, w;
1206 gfxline_t points[5];
1209 msg("<debug> drawlink\n");
1211 link->getRect(&x1, &y1, &x2, &y2);
1212 cvtUserToDev(x1, y1, &x, &y);
1213 points[0].type = gfx_moveTo;
1214 points[0].x = points[4].x = x + user_movex + clipmovex;
1215 points[0].y = points[4].y = y + user_movey + clipmovey;
1216 points[0].next = &points[1];
1217 cvtUserToDev(x2, y1, &x, &y);
1218 points[1].type = gfx_lineTo;
1219 points[1].x = x + user_movex + clipmovex;
1220 points[1].y = y + user_movey + clipmovey;
1221 points[1].next = &points[2];
1222 cvtUserToDev(x2, y2, &x, &y);
1223 points[2].type = gfx_lineTo;
1224 points[2].x = x + user_movex + clipmovex;
1225 points[2].y = y + user_movey + clipmovey;
1226 points[2].next = &points[3];
1227 cvtUserToDev(x1, y2, &x, &y);
1228 points[3].type = gfx_lineTo;
1229 points[3].x = x + user_movex + clipmovex;
1230 points[3].y = y + user_movey + clipmovey;
1231 points[3].next = &points[4];
1232 cvtUserToDev(x1, y1, &x, &y);
1233 points[4].type = gfx_lineTo;
1234 points[4].x = x + user_movex + clipmovex;
1235 points[4].y = y + user_movey + clipmovey;
1238 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1239 points[0].x, points[0].y,
1240 points[1].x, points[1].y,
1241 points[2].x, points[2].y,
1242 points[3].x, points[3].y);
1244 LinkAction*action=link->getAction();
1250 msg("<trace> drawlink action=%d\n", action->getKind());
1251 switch(action->getKind())
1255 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1256 LinkDest *dest=NULL;
1257 if (ha->getDest()==NULL)
1258 dest=catalog->findDest(ha->getNamedDest());
1259 else dest=ha->getDest();
1261 if (dest->isPageRef()){
1262 Ref pageref=dest->getPageRef();
1263 page=catalog->findPage(pageref.num,pageref.gen);
1265 else page=dest->getPageNum();
1266 sprintf(buf, "%d", page);
1273 LinkGoToR*l = (LinkGoToR*)action;
1274 GString*g = l->getFileName();
1276 s = strdup(g->getCString());
1278 /* if the GoToR link has no filename, then
1279 try to find a refernce in the *local*
1281 GString*g = l->getNamedDest();
1283 s = strdup(g->getCString());
1289 LinkNamed*l = (LinkNamed*)action;
1290 GString*name = l->getName();
1292 s = strdup(name->lowerCase()->getCString());
1293 named = name->getCString();
1296 if(strstr(s, "next") || strstr(s, "forward"))
1298 page = currentpage + 1;
1300 else if(strstr(s, "prev") || strstr(s, "back"))
1302 page = currentpage - 1;
1304 else if(strstr(s, "last") || strstr(s, "end"))
1306 if(pages && pagepos>0)
1307 page = pages[pagepos-1];
1309 else if(strstr(s, "first") || strstr(s, "top"))
1317 case actionLaunch: {
1319 LinkLaunch*l = (LinkLaunch*)action;
1320 GString * str = new GString(l->getFileName());
1321 GString * params = l->getParams();
1323 str->append(params);
1324 s = strdup(str->getCString());
1331 LinkURI*l = (LinkURI*)action;
1332 GString*g = l->getURI();
1334 url = g->getCString();
1339 case actionUnknown: {
1341 LinkUnknown*l = (LinkUnknown*)action;
1346 msg("<error> Unknown link type!\n");
1351 if(!s) s = strdup("-?-");
1353 msg("<trace> drawlink s=%s\n", s);
1355 if(!linkinfo && (page || s))
1357 msg("<notice> File contains links");
1365 for(t=1;t<=pagepos;t++) {
1366 if(pages[t]==page) {
1375 sprintf(buf, "page%d", lpage);
1376 device->drawlink(device, points, buf);
1380 device->drawlink(device, points, s);
1383 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1387 void GFXOutputDev::saveState(GfxState *state) {
1388 dbg("saveState");dbgindent+=2;
1390 msg("<trace> saveState\n");
1393 msg("<error> Too many nested states in pdf.");
1397 states[statepos].textRender = states[statepos-1].textRender;
1398 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1399 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1400 states[statepos].clipping = 0;
1403 void GFXOutputDev::restoreState(GfxState *state) {
1404 dbgindent-=2; dbg("restoreState");
1407 msg("<error> Invalid restoreState");
1410 msg("<trace> restoreState");
1411 if(states[statepos].softmask) {
1412 clearSoftMask(state);
1415 while(states[statepos].clipping) {
1416 device->endclip(device);
1417 states[statepos].clipping--;
1422 char* GFXOutputDev::searchFont(char*name)
1426 int is_standard_font = 0;
1428 msg("<verbose> SearchFont(%s)", name);
1430 /* see if it is a pdf standard font */
1431 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1433 if(!strcmp(name, pdf2t1map[i].pdffont))
1435 name = pdf2t1map[i].filename;
1436 is_standard_font = 1;
1440 /* look in all font files */
1441 for(i=0;i<fontnum;i++)
1443 if(strstr(fonts[i].filename, name))
1445 if(!fonts[i].used) {
1448 if(!is_standard_font)
1449 msg("<notice> Using %s for %s", fonts[i].filename, name);
1451 return strdup(fonts[i].filename);
1457 void GFXOutputDev::updateLineWidth(GfxState *state)
1459 double width = state->getTransformedLineWidth();
1460 //swfoutput_setlinewidth(&device, width);
1463 void GFXOutputDev::updateLineCap(GfxState *state)
1465 int c = state->getLineCap();
1468 void GFXOutputDev::updateLineJoin(GfxState *state)
1470 int j = state->getLineJoin();
1473 void GFXOutputDev::updateFillColor(GfxState *state)
1476 double opaq = state->getFillOpacity();
1477 state->getFillRGB(&rgb);
1479 void GFXOutputDev::updateFillOpacity(GfxState *state)
1482 double opaq = state->getFillOpacity();
1483 state->getFillRGB(&rgb);
1484 dbg("update fillopaq %f", opaq);
1486 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1488 double opaq = state->getFillOpacity();
1489 dbg("update strokeopaq %f", opaq);
1491 void GFXOutputDev::updateFillOverprint(GfxState *state)
1493 double opaq = state->getFillOverprint();
1494 dbg("update filloverprint %f", opaq);
1496 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1498 double opaq = state->getStrokeOverprint();
1499 dbg("update strokeoverprint %f", opaq);
1501 void GFXOutputDev::updateTransfer(GfxState *state)
1503 dbg("update transfer");
1507 void GFXOutputDev::updateStrokeColor(GfxState *state)
1510 double opaq = state->getStrokeOpacity();
1511 state->getStrokeRGB(&rgb);
1514 void FoFiWrite(void *stream, char *data, int len)
1516 int ret = fwrite(data, len, 1, (FILE*)stream);
1519 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1521 char*tmpFileName = NULL;
1527 Object refObj, strObj;
1529 tmpFileName = mktmpname(namebuf);
1532 ret = font->getEmbeddedFontID(&embRef);
1534 msg("<verbose> Didn't get embedded font id");
1535 /* not embedded- the caller should now search the font
1536 directories for this font */
1540 f = fopen(tmpFileName, "wb");
1542 msg("<error> Couldn't create temporary Type 1 font file");
1546 /*if(font->isCIDFont()) {
1547 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1548 GString c = cidFont->getCollection();
1549 msg("<notice> Collection: %s", c.getCString());
1552 //if (font->getType() == fontType1C) {
1553 if (0) { //font->getType() == fontType1C) {
1554 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1556 msg("<error> Couldn't read embedded font file");
1559 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1561 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1562 //cvt->convertToCIDType0("test", f);
1563 //cvt->convertToType0("test", f);
1566 } else if(font->getType() == fontTrueType) {
1567 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1568 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1570 msg("<error> Couldn't read embedded font file");
1573 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1574 cvt->writeTTF(FoFiWrite, f);
1578 font->getEmbeddedFontID(&embRef);
1579 refObj.initRef(embRef.num, embRef.gen);
1580 refObj.fetch(ref, &strObj);
1582 strObj.streamReset();
1587 f4[t] = strObj.streamGetChar();
1588 f4c[t] = (char)f4[t];
1593 if(!strncmp(f4c, "true", 4)) {
1594 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1595 Change this on the fly */
1596 f4[0] = f4[2] = f4[3] = 0;
1604 while ((c = strObj.streamGetChar()) != EOF) {
1608 strObj.streamClose();
1613 return strdup(tmpFileName);
1616 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1618 char*name = getFontName(gfxFont);
1622 if(!this->config_use_fontconfig)
1625 #ifdef HAVE_FONTCONFIG
1626 FcPattern *pattern, *match;
1630 static int fcinitcalled = false;
1632 msg("<debug> searchForSuitableFont(%s)", name);
1634 // call init ony once
1635 if (!fcinitcalled) {
1636 msg("<debug> Initializing FontConfig...");
1637 fcinitcalled = true;
1639 msg("<debug> FontConfig Initialization failed. Disabling.");
1640 config_use_fontconfig = 0;
1643 msg("<debug> ...initialized FontConfig");
1646 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1647 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1648 if (gfxFont->isItalic()) // check for italic
1649 msg("<debug> FontConfig: Adding Italic Slant");
1650 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1651 if (gfxFont->isBold()) // check for bold
1652 msg("<debug> FontConfig: Adding Bold Weight");
1653 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1655 msg("<debug> FontConfig: Try to match...");
1656 // configure and match using the original font name
1657 FcConfigSubstitute(0, pattern, FcMatchPattern);
1658 FcDefaultSubstitute(pattern);
1659 match = FcFontMatch(0, pattern, &result);
1661 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1662 msg("<debug> FontConfig: family=%s", (char*)v);
1663 // if we get an exact match
1664 if (strcmp((char *)v, name) == 0) {
1665 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1666 filename = strdup((char*)v); // mem leak
1667 char *nfn = strrchr(filename, '/');
1668 if(nfn) fontname = strdup(nfn+1);
1669 else fontname = filename;
1671 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1673 // initialize patterns
1674 FcPatternDestroy(pattern);
1675 FcPatternDestroy(match);
1677 // now match against serif etc.
1678 if (gfxFont->isSerif()) {
1679 msg("<debug> FontConfig: Create Serif Family Pattern");
1680 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1681 } else if (gfxFont->isFixedWidth()) {
1682 msg("<debug> FontConfig: Create Monospace Family Pattern");
1683 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1685 msg("<debug> FontConfig: Create Sans Family Pattern");
1686 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1690 if (gfxFont->isItalic()) {
1691 msg("<debug> FontConfig: Adding Italic Slant");
1692 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1695 if (gfxFont->isBold()) {
1696 msg("<debug> FontConfig: Adding Bold Weight");
1697 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1700 msg("<debug> FontConfig: Try to match... (2)");
1701 // configure and match using serif etc
1702 FcConfigSubstitute (0, pattern, FcMatchPattern);
1703 FcDefaultSubstitute (pattern);
1704 match = FcFontMatch (0, pattern, &result);
1706 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1707 filename = strdup((char*)v); // mem leak
1708 char *nfn = strrchr(filename, '/');
1709 if(nfn) fontname = strdup(nfn+1);
1710 else fontname = filename;
1712 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1716 //printf("FONTCONFIG: pattern");
1717 //FcPatternPrint(pattern);
1718 //printf("FONTCONFIG: match");
1719 //FcPatternPrint(match);
1721 FcPatternDestroy(pattern);
1722 FcPatternDestroy(match);
1724 pdfswf_addfont(filename);
1731 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1733 char*fontname = 0, *filename = 0;
1734 msg("<notice> substituteFont(%s)", oldname);
1736 if(!(fontname = searchForSuitableFont(gfxFont))) {
1737 fontname = "Times-Roman";
1739 filename = searchFont(fontname);
1741 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1745 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1746 msg("<fatal> Too many fonts in file.");
1750 substitutesource[substitutepos] = strdup(oldname); //mem leak
1751 substitutetarget[substitutepos] = fontname;
1752 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1755 return strdup(filename); //mem leak
1758 void unlinkfont(char* filename)
1765 if(!strncmp(&filename[l-4],".afm",4)) {
1766 memcpy(&filename[l-4],".pfb",4);
1768 memcpy(&filename[l-4],".pfa",4);
1770 memcpy(&filename[l-4],".afm",4);
1773 if(!strncmp(&filename[l-4],".pfa",4)) {
1774 memcpy(&filename[l-4],".afm",4);
1776 memcpy(&filename[l-4],".pfa",4);
1779 if(!strncmp(&filename[l-4],".pfb",4)) {
1780 memcpy(&filename[l-4],".afm",4);
1782 memcpy(&filename[l-4],".pfb",4);
1787 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1793 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1796 fontlist_t*last=0,*l = this->fontlist;
1799 msg("<error> Internal Error: FontID is null");
1801 /* TODO: should this be part of the state? */
1804 if(!strcmp(l->font->id, id)) {
1805 current_gfxfont = l->font;
1807 device->addfont(device, current_gfxfont);
1812 if(!filename) return 0;
1814 /* A font size of e.g. 9 means the font will be scaled down by
1815 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1816 we have to divide 0.05 by (fontsize/1024)
1818 double quality = (1024 * 0.05) / maxSize;
1820 msg("<verbose> Loading %s...", filename);
1821 font = gfxfont_load(id, filename, quality);
1823 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1826 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1830 l->filename = strdup(filename);
1832 current_gfxfont = l->font;
1838 device->addfont(device, current_gfxfont);
1842 void GFXOutputDev::updateFont(GfxState *state)
1844 GfxFont*gfxFont = state->getFont();
1850 char * fontid = getFontID(gfxFont);
1851 char * fontname = getFontName(gfxFont);
1853 double maxSize = 1.0;
1856 maxSize = this->info->getMaximumFontSize(fontid);
1860 /* first, look if we substituted this font before-
1861 this way, we don't initialize the T1 Fonts
1863 for(t=0;t<substitutepos;t++) {
1864 if(!strcmp(fontid, substitutesource[t])) {
1865 free(fontid);fontid=0;
1866 fontid = strdup(substitutetarget[t]);
1871 /* second, see if this is a font which was used before-
1872 if so, we are done */
1873 if(setGfxFont(fontid, fontname, 0, 0)) {
1878 /* if(swfoutput_queryfont(&device, fontid))
1879 swfoutput_setfont(&device, fontid, 0);
1881 msg("<debug> updateFont(%s) [cached]", fontid);
1885 // look for Type 3 font
1886 if (gfxFont->getType() == fontType3) {
1888 type3Warning = gTrue;
1889 showFontError(gfxFont, 2);
1896 /* now either load the font, or find a substitution */
1899 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1904 (gfxFont->getType() == fontType1 ||
1905 gfxFont->getType() == fontType1C ||
1906 gfxFont->getType() == fontCIDType0C ||
1907 gfxFont->getType() == fontTrueType ||
1908 gfxFont->getType() == fontCIDType2
1911 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1912 if(!fileName) showFontError(gfxFont,0);
1915 fileName = searchFont(fontname);
1916 if(!fileName) showFontError(gfxFont,0);
1919 char * fontname = getFontName(gfxFont);
1920 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1923 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1925 msg("<warning> Try specifying one or more font directories");
1927 fileName = substituteFont(gfxFont, fontid);
1930 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1931 msg("<notice> Font is now %s (%s)", fontid, fileName);
1935 msg("<error> Couldn't set font %s\n", fontid);
1941 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1942 dumpFontInfo("<verbose>", gfxFont);
1944 //swfoutput_setfont(&device, fontid, fileName);
1946 if(!setGfxFont(fontid, fontname, 0, 0)) {
1947 setGfxFont(fontid, fontname, fileName, maxSize);
1951 unlinkfont(fileName);
1961 #define SQR(x) ((x)*(x))
1963 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1965 if((newwidth<2 || newheight<2) ||
1966 (width<=newwidth || height<=newheight))
1968 unsigned char*newdata;
1970 newdata= (unsigned char*)malloc(newwidth*newheight);
1972 double fx = (double)(width)/newwidth;
1973 double fy = (double)(height)/newheight;
1975 int blocksize = (int)(8192/(fx*fy));
1976 int r = 8192*256/palettesize;
1977 for(x=0;x<newwidth;x++) {
1978 double ex = px + fx;
1979 int fromx = (int)px;
1981 int xweight1 = (int)(((fromx+1)-px)*256);
1982 int xweight2 = (int)((ex-tox)*256);
1984 for(y=0;y<newheight;y++) {
1985 double ey = py + fy;
1986 int fromy = (int)py;
1988 int yweight1 = (int)(((fromy+1)-py)*256);
1989 int yweight2 = (int)((ey-toy)*256);
1992 for(xx=fromx;xx<=tox;xx++)
1993 for(yy=fromy;yy<=toy;yy++) {
1994 int b = 1-data[width*yy+xx];
1996 if(xx==fromx) weight = (weight*xweight1)/256;
1997 if(xx==tox) weight = (weight*xweight2)/256;
1998 if(yy==fromy) weight = (weight*yweight1)/256;
1999 if(yy==toy) weight = (weight*yweight2)/256;
2002 //if(a) a=(palettesize-1)*r/blocksize;
2003 newdata[y*newwidth+x] = (a*blocksize)/r;
2011 #define IMAGE_TYPE_JPEG 0
2012 #define IMAGE_TYPE_LOSSLESS 1
2014 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2015 double x1,double y1,
2016 double x2,double y2,
2017 double x3,double y3,
2018 double x4,double y4, int type)
2020 gfxcolor_t*newpic=0;
2022 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2023 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2025 gfxline_t p1,p2,p3,p4,p5;
2026 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2027 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2028 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2029 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2030 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2032 {p1.x = (int)(p1.x*20)/20.0;
2033 p1.y = (int)(p1.y*20)/20.0;
2034 p2.x = (int)(p2.x*20)/20.0;
2035 p2.y = (int)(p2.y*20)/20.0;
2036 p3.x = (int)(p3.x*20)/20.0;
2037 p3.y = (int)(p3.y*20)/20.0;
2038 p4.x = (int)(p4.x*20)/20.0;
2039 p4.y = (int)(p4.y*20)/20.0;
2040 p5.x = (int)(p5.x*20)/20.0;
2041 p5.y = (int)(p5.y*20)/20.0;
2048 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2049 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2054 img.data = (gfxcolor_t*)data;
2058 if(type == IMAGE_TYPE_JPEG)
2059 /* TODO: pass image_dpi to device instead */
2060 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2062 dev->fillbitmap(dev, &p1, &img, &m, 0);
2065 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2066 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2068 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2071 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2072 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2074 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2078 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2079 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2080 GBool inlineImg, int mask, int*maskColors,
2081 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2083 double x1,y1,x2,y2,x3,y3,x4,y4;
2084 ImageStream *imgStr;
2089 unsigned char* maskbitmap = 0;
2092 ncomps = colorMap->getNumPixelComps();
2093 bits = colorMap->getBits();
2098 unsigned char buf[8];
2099 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2101 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2102 imgMaskStr->reset();
2103 unsigned char pal[256];
2104 int n = 1 << colorMap->getBits();
2109 maskColorMap->getGray(pixBuf, &gray);
2110 pal[t] = colToByte(gray);
2112 for (y = 0; y < maskHeight; y++) {
2113 for (x = 0; x < maskWidth; x++) {
2114 imgMaskStr->getPixel(buf);
2115 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2120 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2121 imgMaskStr->reset();
2122 for (y = 0; y < maskHeight; y++) {
2123 for (x = 0; x < maskWidth; x++) {
2124 imgMaskStr->getPixel(buf);
2126 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2134 imgStr = new ImageStream(str, width, ncomps,bits);
2137 if(!width || !height || (height<=1 && width<=1))
2139 msg("<verbose> Ignoring %d by %d image", width, height);
2140 unsigned char buf[8];
2142 for (y = 0; y < height; ++y)
2143 for (x = 0; x < width; ++x) {
2144 imgStr->getPixel(buf);
2152 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2153 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2154 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2155 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2157 if(!pbminfo && !(str->getKind()==strDCT)) {
2159 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2163 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2165 if(!jpeginfo && (str->getKind()==strDCT)) {
2166 msg("<notice> file contains jpeg pictures");
2172 unsigned char buf[8];
2174 unsigned char*pic = new unsigned char[width*height];
2175 gfxcolor_t pal[256];
2177 state->getFillRGB(&rgb);
2179 memset(pal,255,sizeof(pal));
2180 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2181 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2182 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2183 pal[0].a = 255; pal[1].a = 0;
2186 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2187 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2188 for (y = 0; y < height; ++y)
2189 for (x = 0; x < width; ++x)
2191 imgStr->getPixel(buf);
2194 pic[width*y+x] = buf[0];
2197 /* the size of the drawn image is added to the identifier
2198 as the same image may require different bitmaps if displayed
2199 at different sizes (due to antialiasing): */
2202 unsigned char*pic2 = 0;
2205 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2214 height = realheight;
2218 /* make a black/white palette */
2220 float r = 255/(numpalette-1);
2222 for(t=0;t<numpalette;t++) {
2223 pal[t].r = colToByte(rgb.r);
2224 pal[t].g = colToByte(rgb.g);
2225 pal[t].b = colToByte(rgb.b);
2226 pal[t].a = (unsigned char)(t*r);
2230 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2231 for (y = 0; y < height; ++y) {
2232 for (x = 0; x < width; ++x) {
2233 pic2[width*y+x] = pal[pic[y*width+x]];
2236 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2240 if(maskbitmap) free(maskbitmap);
2246 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2247 gfxcolor_t*pic=new gfxcolor_t[width*height];
2248 for (y = 0; y < height; ++y) {
2249 for (x = 0; x < width; ++x) {
2250 imgStr->getPixel(pixBuf);
2251 colorMap->getRGB(pixBuf, &rgb);
2252 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2253 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2254 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2255 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2257 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2261 if(str->getKind()==strDCT)
2262 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2264 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2267 if(maskbitmap) free(maskbitmap);
2270 gfxcolor_t*pic=new gfxcolor_t[width*height];
2271 gfxcolor_t pal[256];
2272 int n = 1 << colorMap->getBits();
2274 for(t=0;t<256;t++) {
2276 colorMap->getRGB(pixBuf, &rgb);
2278 {/*if(maskColors && *maskColors==t) {
2279 msg("<notice> Color %d is transparent", t);
2280 if (imgData->maskColors) {
2282 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2283 if (pix[i] < imgData->maskColors[2*i] ||
2284 pix[i] > imgData->maskColors[2*i+1]) {
2299 pal[t].r = (unsigned char)(colToByte(rgb.r));
2300 pal[t].g = (unsigned char)(colToByte(rgb.g));
2301 pal[t].b = (unsigned char)(colToByte(rgb.b));
2302 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2305 for (y = 0; y < height; ++y) {
2306 for (x = 0; x < width; ++x) {
2307 imgStr->getPixel(pixBuf);
2308 pic[width*y+x] = pal[pixBuf[0]];
2310 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2314 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2318 if(maskbitmap) free(maskbitmap);
2323 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2324 int width, int height, GBool invert,
2327 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2328 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2329 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2332 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2333 int width, int height, GfxImageColorMap *colorMap,
2334 int *maskColors, GBool inlineImg)
2336 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2337 colorMap?"colorMap":"no colorMap",
2338 maskColors?"maskColors":"no maskColors",
2340 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2341 colorMap?"colorMap":"no colorMap",
2342 maskColors?"maskColors":"no maskColors",
2345 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2346 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2347 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2350 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2351 int width, int height,
2352 GfxImageColorMap *colorMap,
2353 Stream *maskStr, int maskWidth, int maskHeight,
2356 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2357 colorMap?"colorMap":"no colorMap",
2358 maskWidth, maskHeight);
2359 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2360 colorMap?"colorMap":"no colorMap",
2361 maskWidth, maskHeight);
2363 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2364 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2365 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2368 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2369 int width, int height,
2370 GfxImageColorMap *colorMap,
2372 int maskWidth, int maskHeight,
2373 GfxImageColorMap *maskColorMap)
2375 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2376 colorMap?"colorMap":"no colorMap",
2377 maskWidth, maskHeight);
2378 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2379 colorMap?"colorMap":"no colorMap",
2380 maskWidth, maskHeight);
2382 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2383 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2384 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2387 void GFXOutputDev::stroke(GfxState *state)
2391 GfxPath * path = state->getPath();
2392 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2393 strokeGfxline(state, line);
2397 void GFXOutputDev::fill(GfxState *state)
2401 GfxPath * path = state->getPath();
2402 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2403 fillGfxLine(state, line);
2407 void GFXOutputDev::eoFill(GfxState *state)
2411 GfxPath * path = state->getPath();
2412 gfxcolor_t col = getFillColor(state);
2414 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2416 if(getLogLevel() >= LOGLEVEL_TRACE) {
2417 msg("<trace> eofill\n");
2421 device->fill(device, line, &col);
2426 static char* dirseparator()
2435 void addGlobalFont(char*filename)
2438 memset(&f, 0, sizeof(fontfile_t));
2439 f.filename = filename;
2440 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2441 msg("<verbose> Adding font \"%s\".", filename);
2442 fonts[fontnum++] = f;
2444 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2448 void addGlobalLanguageDir(char*dir)
2451 globalParams = new GlobalParams("");
2453 msg("<notice> Adding %s to language pack directories", dir);
2457 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2458 strcpy(config_file, dir);
2459 strcat(config_file, dirseparator());
2460 strcat(config_file, "add-to-xpdfrc");
2462 fi = fopen(config_file, "rb");
2464 msg("<error> Could not open %s", config_file);
2467 globalParams->parseFile(new GString(config_file), fi);
2471 void addGlobalFontDir(char*dirname)
2473 #ifdef HAVE_DIRENT_H
2474 msg("<notice> Adding %s to font directories", dirname);
2475 lastfontdir = strdup(dirname);
2476 DIR*dir = opendir(dirname);
2478 msg("<warning> Couldn't open directory %s\n", dirname);
2483 ent = readdir (dir);
2487 char*name = ent->d_name;
2493 if(!strncasecmp(&name[l-4], ".pfa", 4))
2495 if(!strncasecmp(&name[l-4], ".pfb", 4))
2497 if(!strncasecmp(&name[l-4], ".ttf", 4))
2501 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2502 strcpy(fontname, dirname);
2503 strcat(fontname, dirseparator());
2504 strcat(fontname, name);
2505 addGlobalFont(fontname);
2510 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2514 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2520 this->pagebuflen = 1024;
2521 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2522 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2524 while(pdfpage >= this->pagebuflen)
2526 int oldlen = this->pagebuflen;
2527 this->pagebuflen+=1024;
2528 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2529 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2532 this->pages[pdfpage] = outputpage;
2533 if(pdfpage>this->pagepos)
2534 this->pagepos = pdfpage;
2540 double width,height;
2543 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2545 double xMin, yMin, xMax, yMax, x, y;
2546 double tx, ty, w, h;
2547 // transform the bbox
2548 state->transform(bbox[0], bbox[1], &x, &y);
2551 state->transform(bbox[0], bbox[3], &x, &y);
2554 } else if (x > xMax) {
2559 } else if (y > yMax) {
2562 state->transform(bbox[2], bbox[1], &x, &y);
2565 } else if (x > xMax) {
2570 } else if (y > yMax) {
2573 state->transform(bbox[2], bbox[3], &x, &y);
2576 } else if (x > xMax) {
2581 } else if (y > yMax) {
2584 tx = (int)floor(xMin);
2587 } else if (tx > width) {
2590 ty = (int)floor(yMin);
2593 } else if (ty > height) {
2596 w = (int)ceil(xMax) - tx + 1;
2597 if (tx + w > width) {
2603 h = (int)ceil(yMax) - ty + 1;
2604 if (ty + h > height) {
2618 #if xpdfUpdateVersion >= 16
2619 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2620 GfxColorSpace *blendingColorSpace,
2621 GBool isolated, GBool knockout,
2624 char*colormodename = "";
2625 BBox rect = mkBBox(state, bbox, this->width, this->height);
2627 if(blendingColorSpace) {
2628 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2630 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);
2631 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2632 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);
2633 states[statepos].createsoftmask |= forSoftMask;
2634 states[statepos].transparencygroup = !forSoftMask;
2635 states[statepos].olddevice = this->device;
2637 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2639 gfxdevice_record_init(this->device);
2641 /*if(!forSoftMask) { ////???
2642 state->setFillOpacity(0.0);
2647 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2650 dbg("endTransparencyGroup");
2651 msg("<verbose> endTransparencyGroup");
2653 gfxdevice_t*r = this->device;
2655 this->device = states[statepos].olddevice;
2657 if(states[statepos].createsoftmask) {
2658 states[statepos-1].softmaskrecording = r->finish(r);
2660 states[statepos-1].grouprecording = r->finish(r);
2663 states[statepos].createsoftmask = 0;
2664 states[statepos].transparencygroup = 0;
2668 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2670 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2671 "colordodge","colorburn","hardlight","softlight","difference",
2672 "exclusion","hue","saturation","color","luminosity"};
2674 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2675 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2677 if(state->getBlendMode() == gfxBlendNormal)
2678 infofeature("transparency groups");
2681 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2682 warnfeature(buffer, 0);
2685 gfxresult_t*grouprecording = states[statepos].grouprecording;
2687 if(state->getBlendMode() == gfxBlendNormal) {
2689 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2690 gfxresult_record_replay(grouprecording, &ops);
2693 grouprecording->destroy(grouprecording);
2695 states[statepos].grouprecording = 0;
2698 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2700 /* alpha = 1: retrieve mask values from alpha layer
2701 alpha = 0: retrieve mask values from luminance */
2702 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2703 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2704 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2705 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2707 infofeature("soft masks");
2709 warnfeature("soft masks from alpha channel",0);
2711 states[statepos].olddevice = this->device;
2712 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2713 gfxdevice_record_init(this->device);
2715 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2717 states[statepos].softmask = 1;
2720 void GFXOutputDev::clearSoftMask(GfxState *state)
2722 if(!states[statepos].softmask)
2724 states[statepos].softmask = 0;
2725 dbg("clearSoftMask statepos=%d", statepos);
2726 msg("<verbose> clearSoftMask");
2728 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2729 msg("<error> Error in softmask/tgroup ordering");
2733 gfxresult_t*mask = states[statepos].softmaskrecording;
2734 gfxresult_t*below = this->device->finish(this->device);
2735 this->device = states[statepos].olddevice;
2737 /* get outline of all objects below the soft mask */
2738 gfxdevice_t uniondev;
2739 gfxdevice_union_init(&uniondev, 0);
2740 gfxresult_record_replay(below, &uniondev);
2741 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2742 uniondev.finish(&uniondev);
2744 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2746 this->device->startclip(this->device, belowoutline);
2747 gfxresult_record_replay(below, this->device);
2748 gfxresult_record_replay(mask, this->device);
2749 this->device->endclip(this->device);
2750 gfxline_free(belowoutline);
2753 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2755 gfxdevice_t belowrender;
2756 gfxdevice_render_init(&belowrender);
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++) {
2783 l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2785 /* premultiply alpha... do we need this? (depends on output device)
2786 l2->r = (l2->a*l2->r) >> 8;
2787 l2->g = (l2->a*l2->g) >> 8;
2788 l2->b = (l2->a*l2->b) >> 8;
2795 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2798 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2799 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2801 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2803 mask->destroy(mask);
2804 below->destroy(below);
2805 maskresult->destroy(maskresult);
2806 belowresult->destroy(belowresult);
2807 states[statepos].softmaskrecording = 0;
2816 delete globalParams;globalParams=0;
2817 Object::memCheck(stderr);