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
49 #include "OutputDev.h"
52 #include "CharCodeToUnicode.h"
53 #include "NameToUnicodeTable.h"
54 #include "GlobalParams.h"
55 #include "FoFiType1C.h"
56 #include "FoFiTrueType.h"
58 #include "GFXOutputDev.h"
60 //swftools header files
62 #include "../gfxdevice.h"
63 #include "../gfxtools.h"
64 #include "../gfxfont.h"
65 #include "../devices/record.h"
66 #include "../devices/ops.h"
67 #include "../devices/arts.h"
68 #include "../devices/render.h"
70 #include "../art/libart.h"
71 #include "../devices/artsutils.c"
78 typedef struct _fontfile
85 static fontfile_t fonts[2048];
86 static int fontnum = 0;
90 static char* lastfontdir = 0;
101 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
102 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
103 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
104 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
105 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
106 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
107 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
108 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
109 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
110 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
111 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
112 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
113 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
114 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
117 static int verbose = 0;
118 static int dbgindent = 0;
119 static void dbg(const char*format, ...)
126 va_start(arglist, format);
127 vsprintf(buf, format, arglist);
130 while(l && buf[l-1]=='\n') {
135 int indent = dbgindent;
145 typedef struct _feature
148 struct _feature*next;
150 feature_t*featurewarnings = 0;
152 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
154 feature_t*f = featurewarnings;
156 if(!strcmp(feature, f->string))
160 f = (feature_t*)malloc(sizeof(feature_t));
161 f->string = strdup(feature);
162 f->next = featurewarnings;
165 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
166 if(this->config_break_on_warning) {
167 msg("<fatal> Aborting conversion due to unsupported feature");
171 msg("<notice> File contains %s",feature);
174 void GFXOutputDev::warnfeature(const char*feature,char fully)
176 showfeature(feature,fully,1);
178 void GFXOutputDev::infofeature(const char*feature)
180 showfeature(feature,0,0);
183 GFXOutputState::GFXOutputState() {
185 this->createsoftmask = 0;
186 this->transparencygroup = 0;
188 this->grouprecording = 0;
192 GBool GFXOutputDev::interpretType3Chars()
197 typedef struct _drawnchar
215 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
216 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
221 free(chars);chars = 0;
228 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
232 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
235 chars[num_chars].x = x;
236 chars[num_chars].y = y;
237 chars[num_chars].color = color;
238 chars[num_chars].charid = charid;
242 char* writeOutStdFont(fontentry* f)
247 char* tmpFileName = mktmpname(namebuf1);
249 sprintf(namebuf2, "%s.afm", tmpFileName);
250 fi = fopen(namebuf2, "wb");
253 fwrite(f->afm, 1, f->afmlen, fi);
256 sprintf(namebuf2, "%s.pfb", tmpFileName);
257 fi = fopen(namebuf2, "wb");
260 fwrite(f->pfb, 1, f->pfblen, fi);
262 return strdup(namebuf2);
264 void unlinkfont(char* filename)
269 msg("<verbose> Removing temporary font file %s", filename);
272 if(!strncmp(&filename[l-4],".afm",4)) {
273 memcpy(&filename[l-4],".pfb",4); unlink(filename);
274 memcpy(&filename[l-4],".pfa",4); unlink(filename);
275 memcpy(&filename[l-4],".afm",4);
278 if(!strncmp(&filename[l-4],".pfa",4)) {
279 memcpy(&filename[l-4],".afm",4); unlink(filename);
280 memcpy(&filename[l-4],".pfa",4);
283 if(!strncmp(&filename[l-4],".pfb",4)) {
284 memcpy(&filename[l-4],".afm",4); unlink(filename);
285 memcpy(&filename[l-4],".pfb",4);
291 GFXGlobalParams::GFXGlobalParams()
294 //setupBaseFonts(char *dir); //not tested yet
296 GFXGlobalParams::~GFXGlobalParams()
298 msg("<verbose> Performing cleanups");
300 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
301 if(pdf2t1map[t].fullfilename) {
302 unlinkfont(pdf2t1map[t].fullfilename);
306 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
308 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
310 char*name = fontName->getCString();
311 /* see if it is a pdf standard font */
313 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
314 if(!strcmp(name, pdf2t1map[t].pdffont)) {
315 if(!pdf2t1map[t].fullfilename) {
316 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
317 if(!pdf2t1map[t].fullfilename) {
318 msg("<error> Couldn't save default font- is the Temp Directory writable?");
320 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
323 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
324 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
328 for(t=0;t<fontnum;t++) {
329 if(strstr(fonts[t].filename, name)) {
330 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
331 dfp->t1.fileName = new GString(fonts[t].filename);
335 return GlobalParams::getDisplayFont(fontName);
338 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
342 this->xref = doc->getXRef();
345 this->textmodeinfo = 0;
348 this->type3active = 0;
351 this->substitutepos = 0;
352 this->type3Warning = 0;
353 this->user_movex = 0;
354 this->user_movey = 0;
357 this->user_clipx1 = 0;
358 this->user_clipy1 = 0;
359 this->user_clipx2 = 0;
360 this->user_clipy2 = 0;
361 this->current_text_stroke = 0;
362 this->current_text_clip = 0;
363 this->outer_clip_box = 0;
365 this->pagebuflen = 0;
367 this->config_break_on_warning=0;
368 this->config_remapunicode=0;
369 this->config_transparent=0;
370 this->config_extrafontdata = 0;
372 this->gfxfontlist = gfxfontlist_create();
374 memset(states, 0, sizeof(states));
377 void GFXOutputDev::setParameter(const char*key, const char*value)
379 if(!strcmp(key,"breakonwarning")) {
380 this->config_break_on_warning = atoi(value);
381 } else if(!strcmp(key,"remapunicode")) {
382 this->config_remapunicode = atoi(value);
383 } else if(!strcmp(key,"transparent")) {
384 this->config_transparent = atoi(value);
385 } else if(!strcmp(key,"extrafontdata")) {
386 this->config_extrafontdata = atoi(value);
387 } else if(!strcmp(key,"help")) {
388 printf("\nPDF layer options:\n");
389 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
390 printf(" are not 100%% supported\n");
391 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
392 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
397 void GFXOutputDev::setDevice(gfxdevice_t*dev)
402 void GFXOutputDev::setMove(int x,int y)
404 this->user_movex = x;
405 this->user_movey = y;
408 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
410 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
411 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
413 this->user_clipx1 = x1;
414 this->user_clipy1 = y1;
415 this->user_clipx2 = x2;
416 this->user_clipy2 = y2;
419 static char*getFontName(GfxFont*font)
422 GString*gstr = font->getName();
423 char* fname = gstr==0?0:gstr->getCString();
427 sprintf(buf, "UFONT%d", r->num);
428 fontid = strdup(buf);
430 fontid = strdup(fname);
434 char* plus = strchr(fontid, '+');
435 if(plus && plus < &fontid[strlen(fontid)-1]) {
436 fontname = strdup(plus+1);
438 fontname = strdup(fontid);
444 static void dumpFontInfo(const char*loglevel, GfxFont*font);
445 static int lastdumps[1024];
446 static int lastdumppos = 0;
451 static void showFontError(GfxFont*font, int nr)
455 for(t=0;t<lastdumppos;t++)
456 if(lastdumps[t] == r->num)
460 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
461 lastdumps[lastdumppos++] = r->num;
463 msg("<warning> The following font caused problems:");
465 msg("<warning> The following font caused problems (substituting):");
467 msg("<warning> The following Type 3 Font will be rendered as graphics:");
468 dumpFontInfo("<warning>", font);
471 static void dumpFontInfo(const char*loglevel, GfxFont*font)
473 char* id = getFontID(font);
474 char* name = getFontName(font);
475 Ref* r=font->getID();
476 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
478 GString*gstr = font->getTag();
480 msg("%s| Tag: %s\n", loglevel, id);
482 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
484 GfxFontType type=font->getType();
486 case fontUnknownType:
487 msg("%s| Type: unknown\n",loglevel);
490 msg("%s| Type: 1\n",loglevel);
493 msg("%s| Type: 1C\n",loglevel);
496 msg("%s| Type: 3\n",loglevel);
499 msg("%s| Type: TrueType\n",loglevel);
502 msg("%s| Type: CIDType0\n",loglevel);
505 msg("%s| Type: CIDType0C\n",loglevel);
508 msg("%s| Type: CIDType2\n",loglevel);
513 GBool embedded = font->getEmbeddedFontID(&embRef);
515 if(font->getEmbeddedFontName()) {
516 embeddedName = font->getEmbeddedFontName()->getCString();
519 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
521 gstr = font->getExtFontFile();
523 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
525 // Get font descriptor flags.
526 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
527 if(font->isSerif()) msg("%s| is serif\n", loglevel);
528 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
529 if(font->isItalic()) msg("%s| is italic\n", loglevel);
530 if(font->isBold()) msg("%s| is bold\n", loglevel);
536 //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");}
537 //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");}
539 void dump_outline(gfxline_t*line)
542 if(line->type == gfx_moveTo) {
543 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
544 } else if(line->type == gfx_lineTo) {
545 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
546 } else if(line->type == gfx_splineTo) {
547 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
553 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
555 int num = path->getNumSubpaths();
558 double lastx=0,lasty=0,posx=0,posy=0;
561 msg("<warning> empty path");
565 gfxdrawer_target_gfxline(&draw);
567 for(t = 0; t < num; t++) {
568 GfxSubpath *subpath = path->getSubpath(t);
569 int subnum = subpath->getNumPoints();
570 double bx=0,by=0,cx=0,cy=0;
572 for(s=0;s<subnum;s++) {
575 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
580 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
581 draw.lineTo(&draw, lastx, lasty);
583 draw.moveTo(&draw, x,y);
588 } else if(subpath->getCurve(s) && cpos==0) {
592 } else if(subpath->getCurve(s) && cpos==1) {
600 draw.lineTo(&draw, x,y);
602 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
609 /* fix non-closed lines */
610 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
611 draw.lineTo(&draw, lastx, lasty);
613 gfxline_t*result = (gfxline_t*)draw.result(&draw);
615 gfxline_optimize(result);
620 GBool GFXOutputDev::useTilingPatternFill()
622 infofeature("tiled patterns");
626 GBool GFXOutputDev::useShadedFills()
628 infofeature("shaded fills");
632 GBool GFXOutputDev::useDrawForm()
634 infofeature("forms");
637 void GFXOutputDev::drawForm(Ref id)
639 msg("<error> drawForm not implemented");
641 GBool GFXOutputDev::needNonText()
645 void GFXOutputDev::endPage()
647 msg("<verbose> endPage");
649 device->endclip(device);
654 #define STROKE_FILL 1
655 #define STROKE_CLIP 2
656 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
658 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
659 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
660 double miterLimit = state->getMiterLimit();
661 double width = state->getTransformedLineWidth();
664 double opaq = state->getStrokeOpacity();
666 state->getFillRGB(&rgb);
668 state->getStrokeRGB(&rgb);
670 col.r = colToByte(rgb.r);
671 col.g = colToByte(rgb.g);
672 col.b = colToByte(rgb.b);
673 col.a = (unsigned char)(opaq*255);
675 gfx_capType capType = gfx_capRound;
676 if(lineCap == 0) capType = gfx_capButt;
677 else if(lineCap == 1) capType = gfx_capRound;
678 else if(lineCap == 2) capType = gfx_capSquare;
680 gfx_joinType joinType = gfx_joinRound;
681 if(lineJoin == 0) joinType = gfx_joinMiter;
682 else if(lineJoin == 1) joinType = gfx_joinRound;
683 else if(lineJoin == 2) joinType = gfx_joinBevel;
686 double dashphase = 0;
688 state->getLineDash(&ldash, &dashnum, &dashphase);
692 if(dashnum && ldash) {
693 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
695 msg("<trace> %d dashes", dashnum);
696 msg("<trace> | phase: %f", dashphase);
697 for(t=0;t<dashnum;t++) {
699 msg("<trace> | d%-3d: %f", t, ldash[t]);
702 if(getLogLevel() >= LOGLEVEL_TRACE) {
706 line2 = gfxtool_dash_line(line, dash, dashphase);
709 msg("<trace> After dashing:");
712 if(getLogLevel() >= LOGLEVEL_TRACE) {
713 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
715 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
716 lineCap==0?"butt": (lineJoin==1?"round":"square"),
718 col.r,col.g,col.b,col.a
723 if(flags&STROKE_FILL) {
724 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
725 gfxline_t*gfxline = SVPtogfxline(svp);
726 if(getLogLevel() >= LOGLEVEL_TRACE) {
727 dump_outline(gfxline);
730 msg("<warning> Empty polygon (resulting from stroked line)");
732 if(flags&STROKE_CLIP) {
733 device->startclip(device, gfxline);
734 states[statepos].clipping++;
736 device->fill(device, gfxline, &col);
741 if(flags&STROKE_CLIP)
742 msg("<error> Stroke&clip not supported at the same time");
743 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
750 gfxcolor_t getFillColor(GfxState * state)
753 double opaq = state->getFillOpacity();
754 state->getFillRGB(&rgb);
756 col.r = colToByte(rgb.r);
757 col.g = colToByte(rgb.g);
758 col.b = colToByte(rgb.b);
759 col.a = (unsigned char)(opaq*255);
763 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
765 gfxcolor_t col = getFillColor(state);
767 if(getLogLevel() >= LOGLEVEL_TRACE) {
768 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
771 device->fill(device, line, &col);
774 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
776 if(getLogLevel() >= LOGLEVEL_TRACE) {
777 msg("<trace> clip\n");
781 device->startclip(device, line);
782 states[statepos].clipping++;
785 void GFXOutputDev::clip(GfxState *state)
787 GfxPath * path = state->getPath();
788 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
789 clipToGfxLine(state, line);
793 void GFXOutputDev::eoClip(GfxState *state)
795 GfxPath * path = state->getPath();
796 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
798 if(getLogLevel() >= LOGLEVEL_TRACE) {
799 msg("<trace> eoclip\n");
803 device->startclip(device, line);
804 states[statepos].clipping++;
807 void GFXOutputDev::clipToStrokePath(GfxState *state)
809 GfxPath * path = state->getPath();
810 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
812 if(getLogLevel() >= LOGLEVEL_TRACE) {
813 msg("<trace> cliptostrokepath\n");
817 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
821 void GFXOutputDev::finish()
825 device->endclip(device);
831 GFXOutputDev::~GFXOutputDev()
836 free(this->pages); this->pages = 0;
839 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
841 GBool GFXOutputDev::upsideDown()
845 GBool GFXOutputDev::useDrawChar()
850 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
851 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
853 static char tmp_printstr[4096];
854 char* makeStringPrintable(char*str)
856 int len = strlen(str);
871 tmp_printstr[len++] = '.';
872 tmp_printstr[len++] = '.';
873 tmp_printstr[len++] = '.';
875 tmp_printstr[len] = 0;
878 #define INTERNAL_FONT_SIZE 1024.0
879 void GFXOutputDev::updateFontMatrix(GfxState*state)
881 double* ctm = state->getCTM();
882 double fontSize = state->getFontSize();
883 double*textMat = state->getTextMat();
885 /* taking the absolute value of horizScaling seems to be required for
886 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
887 double hscale = fabs(state->getHorizScaling());
889 // from xpdf-3.02/SplashOutputDev:updateFont
890 double mm11 = textMat[0] * fontSize * hscale;
891 double mm12 = textMat[1] * fontSize * hscale;
892 double mm21 = textMat[2] * fontSize;
893 double mm22 = textMat[3] * fontSize;
895 // multiply with ctm, like state->getFontTransMat() does
896 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
897 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
898 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
899 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
900 this->current_font_matrix.tx = 0;
901 this->current_font_matrix.ty = 0;
904 void GFXOutputDev::beginString(GfxState *state, GString *s)
906 int render = state->getRender();
907 if(current_text_stroke) {
908 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
911 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
914 static gfxline_t* mkEmptyGfxShape(double x, double y)
916 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
917 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
921 static char isValidUnicode(int c)
923 if(c>=32 && c<0x2fffe)
928 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
929 double dx, double dy,
930 double originX, double originY,
931 CharCode charid, int nBytes, Unicode *_u, int uLen)
933 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
934 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
938 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
940 int render = state->getRender();
941 gfxcolor_t col = getFillColor(state);
943 // check for invisible text -- this is used by Acrobat Capture
944 if (render == RENDER_INVISIBLE) {
946 if(!config_extrafontdata)
950 GfxFont*font = state->getFont();
952 if(font->getType() == fontType3) {
953 /* type 3 chars are passed as graphics */
954 msg("<debug> type3 char at %f/%f", x, y);
958 Unicode u = uLen?(_u[0]):0;
959 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid);
961 gfxmatrix_t m = this->current_font_matrix;
962 state->transform(x, y, &m.tx, &m.ty);
963 m.tx += user_movex + clipmovex;
964 m.ty += user_movey + clipmovey;
966 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
967 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
969 msg("<debug> Drawing glyph %d as shape", charid);
971 msg("<notice> Some texts will be rendered as shape");
974 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
975 gfxline_t*tglyph = gfxline_clone(glyph);
976 gfxline_transform(tglyph, &m);
977 if((render&3) != RENDER_INVISIBLE) {
978 gfxline_t*add = gfxline_clone(tglyph);
979 current_text_stroke = gfxline_append(current_text_stroke, add);
981 if(render&RENDER_CLIP) {
982 gfxline_t*add = gfxline_clone(tglyph);
983 current_text_clip = gfxline_append(current_text_clip, add);
984 if(!current_text_clip) {
985 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
988 gfxline_free(tglyph);
992 void GFXOutputDev::endString(GfxState *state)
994 int render = state->getRender();
995 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
997 if(current_text_stroke) {
998 /* fillstroke and stroke text rendering objects we can process right
999 now (as there may be texts of other rendering modes in this
1000 text object)- clipping objects have to wait until endTextObject,
1002 device->setparameter(device, "mark","TXT");
1003 if((render&3) == RENDER_FILL) {
1004 fillGfxLine(state, current_text_stroke);
1005 gfxline_free(current_text_stroke);
1006 current_text_stroke = 0;
1007 } else if((render&3) == RENDER_FILLSTROKE) {
1008 fillGfxLine(state, current_text_stroke);
1009 strokeGfxline(state, current_text_stroke,0);
1010 gfxline_free(current_text_stroke);
1011 current_text_stroke = 0;
1012 } else if((render&3) == RENDER_STROKE) {
1013 strokeGfxline(state, current_text_stroke,0);
1014 gfxline_free(current_text_stroke);
1015 current_text_stroke = 0;
1017 device->setparameter(device, "mark","");
1021 void GFXOutputDev::endTextObject(GfxState *state)
1023 int render = state->getRender();
1024 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1026 if(current_text_clip) {
1027 device->setparameter(device, "mark","TXT");
1028 clipToGfxLine(state, current_text_clip);
1029 device->setparameter(device, "mark","");
1030 gfxline_free(current_text_clip);
1031 current_text_clip = 0;
1035 /* the logic seems to be as following:
1036 first, beginType3Char is called, with the charcode and the coordinates.
1037 if this function returns true, it already knew about the char and has now drawn it.
1038 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1039 called with some parameters.
1040 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1041 at the position first passed to beginType3Char). the char ends with endType3Char.
1043 The drawing operations between beginType3Char and endType3Char are somewhat different to
1044 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1045 color determines the color of a font)
1048 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1050 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1053 if(config_extrafontdata && current_fontinfo) {
1055 gfxmatrix_t m = this->current_font_matrix;
1056 state->transform(0, 0, &m.tx, &m.ty);
1057 m.m00*=INTERNAL_FONT_SIZE;
1058 m.m01*=INTERNAL_FONT_SIZE;
1059 m.m10*=INTERNAL_FONT_SIZE;
1060 m.m11*=INTERNAL_FONT_SIZE;
1061 m.tx += user_movex + clipmovex;
1062 m.ty += user_movey + clipmovey;
1064 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1065 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1068 gfxcolor_t col={0,0,0,0};
1069 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1070 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1074 /* the character itself is going to be passed using the draw functions */
1075 return gFalse; /* gTrue= is_in_cache? */
1078 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1080 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1083 void GFXOutputDev::endType3Char(GfxState *state)
1086 msg("<debug> endType3Char");
1089 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1091 this->currentpage = pageNum;
1093 int rot = doc->getPageRotate(1);
1096 gfxline_t clippath[5];
1098 white.r = white.g = white.b = white.a = 255;
1100 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1101 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1102 Use CropBox, not MediaBox, as page size
1109 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1110 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1112 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1113 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1115 this->clipmovex = -(int)x1;
1116 this->clipmovey = -(int)y1;
1118 /* apply user clip box */
1119 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1120 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1121 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1122 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1123 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1124 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1127 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1129 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);
1131 msg("<verbose> page is rotated %d degrees\n", rot);
1133 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1134 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1135 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1136 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1137 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1138 device->startclip(device, clippath); outer_clip_box = 1;
1139 if(!config_transparent) {
1140 device->fill(device, clippath, &white);
1145 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1147 double x1, y1, x2, y2, w;
1148 gfxline_t points[5];
1151 msg("<debug> drawlink\n");
1153 link->getRect(&x1, &y1, &x2, &y2);
1154 cvtUserToDev(x1, y1, &x, &y);
1155 points[0].type = gfx_moveTo;
1156 points[0].x = points[4].x = x + user_movex + clipmovex;
1157 points[0].y = points[4].y = y + user_movey + clipmovey;
1158 points[0].next = &points[1];
1159 cvtUserToDev(x2, y1, &x, &y);
1160 points[1].type = gfx_lineTo;
1161 points[1].x = x + user_movex + clipmovex;
1162 points[1].y = y + user_movey + clipmovey;
1163 points[1].next = &points[2];
1164 cvtUserToDev(x2, y2, &x, &y);
1165 points[2].type = gfx_lineTo;
1166 points[2].x = x + user_movex + clipmovex;
1167 points[2].y = y + user_movey + clipmovey;
1168 points[2].next = &points[3];
1169 cvtUserToDev(x1, y2, &x, &y);
1170 points[3].type = gfx_lineTo;
1171 points[3].x = x + user_movex + clipmovex;
1172 points[3].y = y + user_movey + clipmovey;
1173 points[3].next = &points[4];
1174 cvtUserToDev(x1, y1, &x, &y);
1175 points[4].type = gfx_lineTo;
1176 points[4].x = x + user_movex + clipmovex;
1177 points[4].y = y + user_movey + clipmovey;
1180 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1181 points[0].x, points[0].y,
1182 points[1].x, points[1].y,
1183 points[2].x, points[2].y,
1184 points[3].x, points[3].y);
1186 LinkAction*action=link->getAction();
1189 const char*type = "-?-";
1192 msg("<trace> drawlink action=%d\n", action->getKind());
1193 switch(action->getKind())
1197 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1198 LinkDest *dest=NULL;
1199 if (ha->getDest()==NULL)
1200 dest=catalog->findDest(ha->getNamedDest());
1201 else dest=ha->getDest();
1203 if (dest->isPageRef()){
1204 Ref pageref=dest->getPageRef();
1205 page=catalog->findPage(pageref.num,pageref.gen);
1207 else page=dest->getPageNum();
1208 sprintf(buf, "%d", page);
1215 LinkGoToR*l = (LinkGoToR*)action;
1216 GString*g = l->getFileName();
1218 s = strdup(g->getCString());
1220 /* if the GoToR link has no filename, then
1221 try to find a refernce in the *local*
1223 GString*g = l->getNamedDest();
1225 s = strdup(g->getCString());
1231 LinkNamed*l = (LinkNamed*)action;
1232 GString*name = l->getName();
1234 s = strdup(name->lowerCase()->getCString());
1235 named = name->getCString();
1238 if(strstr(s, "next") || strstr(s, "forward"))
1240 page = currentpage + 1;
1242 else if(strstr(s, "prev") || strstr(s, "back"))
1244 page = currentpage - 1;
1246 else if(strstr(s, "last") || strstr(s, "end"))
1248 if(pages && pagepos>0)
1249 page = pages[pagepos-1];
1251 else if(strstr(s, "first") || strstr(s, "top"))
1259 case actionLaunch: {
1261 LinkLaunch*l = (LinkLaunch*)action;
1262 GString * str = new GString(l->getFileName());
1263 GString * params = l->getParams();
1265 str->append(params);
1266 s = strdup(str->getCString());
1273 LinkURI*l = (LinkURI*)action;
1274 GString*g = l->getURI();
1276 url = g->getCString();
1281 case actionUnknown: {
1283 LinkUnknown*l = (LinkUnknown*)action;
1288 msg("<error> Unknown link type!\n");
1293 if(!s) s = strdup("-?-");
1295 msg("<trace> drawlink s=%s\n", s);
1297 if(!linkinfo && (page || s))
1299 msg("<notice> File contains links");
1307 for(t=1;t<=pagepos;t++) {
1308 if(pages[t]==page) {
1317 sprintf(buf, "page%d", lpage);
1318 device->drawlink(device, points, buf);
1322 device->drawlink(device, points, s);
1325 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1329 void GFXOutputDev::saveState(GfxState *state) {
1330 dbg("saveState");dbgindent+=2;
1332 msg("<trace> saveState\n");
1335 msg("<error> Too many nested states in pdf.");
1339 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1340 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1341 states[statepos].clipping = 0;
1344 void GFXOutputDev::restoreState(GfxState *state) {
1345 dbgindent-=2; dbg("restoreState");
1348 msg("<error> Invalid restoreState");
1351 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1352 states[statepos].clipping?" (end clipping)":"");
1353 if(states[statepos].softmask) {
1354 clearSoftMask(state);
1357 while(states[statepos].clipping) {
1358 device->endclip(device);
1359 states[statepos].clipping--;
1364 void GFXOutputDev::updateLineWidth(GfxState *state)
1366 double width = state->getTransformedLineWidth();
1367 //swfoutput_setlinewidth(&device, width);
1370 void GFXOutputDev::updateLineCap(GfxState *state)
1372 int c = state->getLineCap();
1375 void GFXOutputDev::updateLineJoin(GfxState *state)
1377 int j = state->getLineJoin();
1380 void GFXOutputDev::updateFillColor(GfxState *state)
1383 double opaq = state->getFillOpacity();
1384 state->getFillRGB(&rgb);
1386 void GFXOutputDev::updateFillOpacity(GfxState *state)
1389 double opaq = state->getFillOpacity();
1390 state->getFillRGB(&rgb);
1391 dbg("update fillopaq %f", opaq);
1393 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1395 double opaq = state->getFillOpacity();
1396 dbg("update strokeopaq %f", opaq);
1398 void GFXOutputDev::updateFillOverprint(GfxState *state)
1400 double opaq = state->getFillOverprint();
1401 dbg("update filloverprint %f", opaq);
1403 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1405 double opaq = state->getStrokeOverprint();
1406 dbg("update strokeoverprint %f", opaq);
1408 void GFXOutputDev::updateTransfer(GfxState *state)
1410 dbg("update transfer");
1414 void GFXOutputDev::updateStrokeColor(GfxState *state)
1417 double opaq = state->getStrokeOpacity();
1418 state->getStrokeRGB(&rgb);
1422 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1424 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1425 memset(font, 0, sizeof(gfxfont_t));
1427 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1428 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1429 font->id = strdup(getFontID(xpdffont));
1431 double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
1433 //printf("%d glyphs\n", font->num_glyphs);
1434 font->num_glyphs = 0;
1435 for(t=0;t<src->num_glyphs;t++) {
1436 if(src->glyphs[t]) {
1437 SplashPath*path = src->glyphs[t]->path;
1438 int len = path?path->getLength():0;
1439 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1440 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1441 src->glyphs[t]->glyphid = font->num_glyphs;
1442 glyph->unicode = src->glyphs[t]->unicode;
1443 if(glyph->unicode >= font->max_unicode)
1444 font->max_unicode = glyph->unicode+1;
1446 gfxdrawer_target_gfxline(&drawer);
1450 for(s=0;s<len;s++) {
1453 path->getPoint(s, &x, &y, &f);
1456 if(f&splashPathFirst) {
1457 drawer.moveTo(&drawer, x*scale, y*scale);
1459 if(f&splashPathCurve) {
1461 path->getPoint(++s, &x2, &y2, &f);
1462 if(f&splashPathCurve) {
1464 path->getPoint(++s, &x3, &y3, &f);
1465 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1467 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1470 drawer.lineTo(&drawer, x*scale, y*scale);
1472 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1473 // (f&splashPathFirst)?"first":"",
1474 // (f&splashPathLast)?"last":"");
1476 glyph->line = (gfxline_t*)drawer.result(&drawer);
1477 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1481 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1482 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1483 for(t=0;t<font->num_glyphs;t++) {
1484 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1485 font->unicode2glyph[font->glyphs[t].unicode] = t;
1489 msg("<trace> %d glyphs.", t, font->num_glyphs);
1493 void GFXOutputDev::updateFont(GfxState *state)
1495 GfxFont* gfxFont = state->getFont();
1499 char*id = getFontID(gfxFont);
1500 msg("<verbose> Updating font to %s", id);
1501 if(gfxFont->getType() == fontType3) {
1502 infofeature("Type3 fonts");
1503 if(!config_extrafontdata) {
1508 msg("<error> Internal Error: FontID is null");
1512 this->current_fontinfo = this->info->getFont(id);
1513 if(!this->current_fontinfo) {
1514 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1516 if(!this->current_fontinfo->seen) {
1517 dumpFontInfo("<verbose>", gfxFont);
1520 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1522 font = createGfxFont(gfxFont, current_fontinfo);
1523 font->id = strdup(id);
1524 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1525 device->addfont(device, font);
1527 current_gfxfont = font;
1530 updateFontMatrix(state);
1533 #define SQR(x) ((x)*(x))
1535 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1537 if((newwidth<2 || newheight<2) ||
1538 (width<=newwidth || height<=newheight))
1540 unsigned char*newdata;
1542 newdata= (unsigned char*)malloc(newwidth*newheight);
1544 double fx = (double)(width)/newwidth;
1545 double fy = (double)(height)/newheight;
1547 int blocksize = (int)(8192/(fx*fy));
1548 int r = 8192*256/palettesize;
1549 for(x=0;x<newwidth;x++) {
1550 double ex = px + fx;
1551 int fromx = (int)px;
1553 int xweight1 = (int)(((fromx+1)-px)*256);
1554 int xweight2 = (int)((ex-tox)*256);
1556 for(y=0;y<newheight;y++) {
1557 double ey = py + fy;
1558 int fromy = (int)py;
1560 int yweight1 = (int)(((fromy+1)-py)*256);
1561 int yweight2 = (int)((ey-toy)*256);
1564 for(xx=fromx;xx<=tox;xx++)
1565 for(yy=fromy;yy<=toy;yy++) {
1566 int b = 1-data[width*yy+xx];
1568 if(xx==fromx) weight = (weight*xweight1)/256;
1569 if(xx==tox) weight = (weight*xweight2)/256;
1570 if(yy==fromy) weight = (weight*yweight1)/256;
1571 if(yy==toy) weight = (weight*yweight2)/256;
1574 //if(a) a=(palettesize-1)*r/blocksize;
1575 newdata[y*newwidth+x] = (a*blocksize)/r;
1583 #define IMAGE_TYPE_JPEG 0
1584 #define IMAGE_TYPE_LOSSLESS 1
1586 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1587 double x1,double y1,
1588 double x2,double y2,
1589 double x3,double y3,
1590 double x4,double y4, int type)
1592 gfxcolor_t*newpic=0;
1594 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1595 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1597 gfxline_t p1,p2,p3,p4,p5;
1598 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1599 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1600 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1601 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1602 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1604 {p1.x = (int)(p1.x*20)/20.0;
1605 p1.y = (int)(p1.y*20)/20.0;
1606 p2.x = (int)(p2.x*20)/20.0;
1607 p2.y = (int)(p2.y*20)/20.0;
1608 p3.x = (int)(p3.x*20)/20.0;
1609 p3.y = (int)(p3.y*20)/20.0;
1610 p4.x = (int)(p4.x*20)/20.0;
1611 p4.y = (int)(p4.y*20)/20.0;
1612 p5.x = (int)(p5.x*20)/20.0;
1613 p5.y = (int)(p5.y*20)/20.0;
1620 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1621 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1626 img.data = (gfxcolor_t*)data;
1630 if(type == IMAGE_TYPE_JPEG)
1631 /* TODO: pass image_dpi to device instead */
1632 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1634 dev->fillbitmap(dev, &p1, &img, &m, 0);
1637 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1638 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1640 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1643 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1644 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1646 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1650 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1651 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1652 GBool inlineImg, int mask, int*maskColors,
1653 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1655 double x1,y1,x2,y2,x3,y3,x4,y4;
1656 ImageStream *imgStr;
1661 unsigned char* maskbitmap = 0;
1664 ncomps = colorMap->getNumPixelComps();
1665 bits = colorMap->getBits();
1670 unsigned char buf[8];
1671 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1673 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1674 imgMaskStr->reset();
1675 unsigned char pal[256];
1676 int n = 1 << colorMap->getBits();
1681 maskColorMap->getGray(pixBuf, &gray);
1682 pal[t] = colToByte(gray);
1684 for (y = 0; y < maskHeight; y++) {
1685 for (x = 0; x < maskWidth; x++) {
1686 imgMaskStr->getPixel(buf);
1687 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1692 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1693 imgMaskStr->reset();
1694 for (y = 0; y < maskHeight; y++) {
1695 for (x = 0; x < maskWidth; x++) {
1696 imgMaskStr->getPixel(buf);
1698 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1706 imgStr = new ImageStream(str, width, ncomps,bits);
1709 if(!width || !height || (height<=1 && width<=1))
1711 msg("<verbose> Ignoring %d by %d image", width, height);
1712 unsigned char buf[8];
1714 for (y = 0; y < height; ++y)
1715 for (x = 0; x < width; ++x) {
1716 imgStr->getPixel(buf);
1724 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1725 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1726 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1727 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1729 if(!pbminfo && !(str->getKind()==strDCT)) {
1731 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1735 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1737 if(!jpeginfo && (str->getKind()==strDCT)) {
1738 msg("<notice> file contains jpeg pictures");
1744 unsigned char buf[8];
1746 unsigned char*pic = new unsigned char[width*height];
1747 gfxcolor_t pal[256];
1749 state->getFillRGB(&rgb);
1751 memset(pal,255,sizeof(pal));
1752 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1753 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1754 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1755 pal[0].a = 255; pal[1].a = 0;
1758 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1759 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1760 for (y = 0; y < height; ++y)
1761 for (x = 0; x < width; ++x)
1763 imgStr->getPixel(buf);
1766 pic[width*y+x] = buf[0];
1769 /* the size of the drawn image is added to the identifier
1770 as the same image may require different bitmaps if displayed
1771 at different sizes (due to antialiasing): */
1774 unsigned char*pic2 = 0;
1777 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1786 height = realheight;
1790 /* make a black/white palette */
1792 float r = 255/(numpalette-1);
1794 for(t=0;t<numpalette;t++) {
1795 pal[t].r = colToByte(rgb.r);
1796 pal[t].g = colToByte(rgb.g);
1797 pal[t].b = colToByte(rgb.b);
1798 pal[t].a = (unsigned char)(t*r);
1802 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1803 for (y = 0; y < height; ++y) {
1804 for (x = 0; x < width; ++x) {
1805 pic2[width*y+x] = pal[pic[y*width+x]];
1808 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1812 if(maskbitmap) free(maskbitmap);
1818 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1819 gfxcolor_t*pic=new gfxcolor_t[width*height];
1820 for (y = 0; y < height; ++y) {
1821 for (x = 0; x < width; ++x) {
1822 imgStr->getPixel(pixBuf);
1823 colorMap->getRGB(pixBuf, &rgb);
1824 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1825 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1826 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1827 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1829 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1833 if(str->getKind()==strDCT)
1834 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1836 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1839 if(maskbitmap) free(maskbitmap);
1842 gfxcolor_t*pic=new gfxcolor_t[width*height];
1843 gfxcolor_t pal[256];
1844 int n = 1 << colorMap->getBits();
1846 for(t=0;t<256;t++) {
1848 colorMap->getRGB(pixBuf, &rgb);
1850 {/*if(maskColors && *maskColors==t) {
1851 msg("<notice> Color %d is transparent", t);
1852 if (imgData->maskColors) {
1854 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1855 if (pix[i] < imgData->maskColors[2*i] ||
1856 pix[i] > imgData->maskColors[2*i+1]) {
1871 pal[t].r = (unsigned char)(colToByte(rgb.r));
1872 pal[t].g = (unsigned char)(colToByte(rgb.g));
1873 pal[t].b = (unsigned char)(colToByte(rgb.b));
1874 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1877 for (y = 0; y < height; ++y) {
1878 for (x = 0; x < width; ++x) {
1879 imgStr->getPixel(pixBuf);
1880 pic[width*y+x] = pal[pixBuf[0]];
1882 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1886 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1890 if(maskbitmap) free(maskbitmap);
1895 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1896 int width, int height, GBool invert,
1899 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1900 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1901 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1904 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1905 int width, int height, GfxImageColorMap *colorMap,
1906 int *maskColors, GBool inlineImg)
1908 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1909 colorMap?"colorMap":"no colorMap",
1910 maskColors?"maskColors":"no maskColors",
1912 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1913 colorMap?"colorMap":"no colorMap",
1914 maskColors?"maskColors":"no maskColors",
1917 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1918 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1919 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1922 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1923 int width, int height,
1924 GfxImageColorMap *colorMap,
1925 Stream *maskStr, int maskWidth, int maskHeight,
1928 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1929 colorMap?"colorMap":"no colorMap",
1930 maskWidth, maskHeight);
1931 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1932 colorMap?"colorMap":"no colorMap",
1933 maskWidth, maskHeight);
1935 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1936 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1937 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1940 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1941 int width, int height,
1942 GfxImageColorMap *colorMap,
1944 int maskWidth, int maskHeight,
1945 GfxImageColorMap *maskColorMap)
1947 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1948 colorMap?"colorMap":"no colorMap",
1949 maskWidth, maskHeight);
1950 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1951 colorMap?"colorMap":"no colorMap",
1952 maskWidth, maskHeight);
1954 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1955 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1956 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1959 void GFXOutputDev::stroke(GfxState *state)
1963 GfxPath * path = state->getPath();
1964 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1965 strokeGfxline(state, line, 0);
1969 void GFXOutputDev::fill(GfxState *state)
1971 gfxcolor_t col = getFillColor(state);
1972 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1974 GfxPath * path = state->getPath();
1975 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1976 fillGfxLine(state, line);
1980 void GFXOutputDev::eoFill(GfxState *state)
1982 gfxcolor_t col = getFillColor(state);
1983 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1985 GfxPath * path = state->getPath();
1986 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1987 fillGfxLine(state, line);
1992 static const char* dirseparator()
2001 void addGlobalFont(const char*filename)
2004 memset(&f, 0, sizeof(fontfile_t));
2005 f.filename = filename;
2006 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2007 msg("<notice> Adding font \"%s\".", filename);
2008 fonts[fontnum++] = f;
2010 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2014 void addGlobalLanguageDir(const char*dir)
2016 msg("<notice> Adding %s to language pack directories", dir);
2020 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2021 strcpy(config_file, dir);
2022 strcat(config_file, dirseparator());
2023 strcat(config_file, "add-to-xpdfrc");
2025 fi = fopen(config_file, "rb");
2027 msg("<error> Could not open %s", config_file);
2030 globalParams->parseFile(new GString(config_file), fi);
2034 void addGlobalFontDir(const char*dirname)
2036 #ifdef HAVE_DIRENT_H
2037 msg("<notice> Adding %s to font directories", dirname);
2038 lastfontdir = strdup(dirname);
2039 DIR*dir = opendir(dirname);
2041 msg("<warning> Couldn't open directory %s\n", dirname);
2046 ent = readdir (dir);
2050 char*name = ent->d_name;
2056 if(!strncasecmp(&name[l-4], ".pfa", 4))
2058 if(!strncasecmp(&name[l-4], ".pfb", 4))
2060 if(!strncasecmp(&name[l-4], ".ttf", 4))
2063 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2064 strcpy(fontname, dirname);
2065 strcat(fontname, dirseparator());
2066 strcat(fontname, name);
2067 addGlobalFont(fontname);
2072 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2076 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2082 this->pagebuflen = 1024;
2083 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2084 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2086 while(pdfpage >= this->pagebuflen)
2088 int oldlen = this->pagebuflen;
2089 this->pagebuflen+=1024;
2090 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2091 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2094 this->pages[pdfpage] = outputpage;
2095 if(pdfpage>this->pagepos)
2096 this->pagepos = pdfpage;
2102 double width,height;
2105 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2107 double xMin, yMin, xMax, yMax, x, y;
2108 double tx, ty, w, h;
2109 // transform the bbox
2110 state->transform(bbox[0], bbox[1], &x, &y);
2113 state->transform(bbox[0], bbox[3], &x, &y);
2116 } else if (x > xMax) {
2121 } else if (y > yMax) {
2124 state->transform(bbox[2], bbox[1], &x, &y);
2127 } else if (x > xMax) {
2132 } else if (y > yMax) {
2135 state->transform(bbox[2], bbox[3], &x, &y);
2138 } else if (x > xMax) {
2143 } else if (y > yMax) {
2146 tx = (int)floor(xMin);
2149 } else if (tx > width) {
2152 ty = (int)floor(yMin);
2155 } else if (ty > height) {
2158 w = (int)ceil(xMax) - tx + 1;
2159 if (tx + w > width) {
2165 h = (int)ceil(yMax) - ty + 1;
2166 if (ty + h > height) {
2180 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2181 GfxColorSpace *blendingColorSpace,
2182 GBool isolated, GBool knockout,
2185 const char*colormodename = "";
2187 if(blendingColorSpace) {
2188 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2190 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);
2191 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);
2193 states[statepos].createsoftmask |= forSoftMask;
2194 states[statepos].transparencygroup = !forSoftMask;
2195 states[statepos].isolated = isolated;
2197 states[statepos].olddevice = this->device;
2198 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2200 gfxdevice_record_init(this->device);
2202 /*if(!forSoftMask) { ////???
2203 state->setFillOpacity(0.0);
2208 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2211 dbg("endTransparencyGroup");
2212 msg("<verbose> endTransparencyGroup");
2214 gfxdevice_t*r = this->device;
2216 this->device = states[statepos].olddevice;
2218 if(states[statepos].createsoftmask) {
2219 states[statepos-1].softmaskrecording = r->finish(r);
2221 states[statepos-1].grouprecording = r->finish(r);
2224 states[statepos].createsoftmask = 0;
2225 states[statepos].transparencygroup = 0;
2229 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2231 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2232 "colordodge","colorburn","hardlight","softlight","difference",
2233 "exclusion","hue","saturation","color","luminosity"};
2235 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2236 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2238 if(state->getBlendMode() == gfxBlendNormal)
2239 infofeature("transparency groups");
2242 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2243 warnfeature(buffer, 0);
2246 gfxresult_t*grouprecording = states[statepos].grouprecording;
2248 if(state->getBlendMode() == gfxBlendNormal) {
2250 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2251 gfxresult_record_replay(grouprecording, &ops);
2254 grouprecording->destroy(grouprecording);
2256 states[statepos].grouprecording = 0;
2259 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2261 /* alpha = 1: retrieve mask values from alpha layer
2262 alpha = 0: retrieve mask values from luminance */
2263 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2264 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2265 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2266 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2268 infofeature("soft masks");
2270 warnfeature("soft masks from alpha channel",0);
2272 states[statepos].olddevice = this->device;
2273 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2274 gfxdevice_record_init(this->device);
2276 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2278 states[statepos].softmask = 1;
2279 states[statepos].softmask_alpha = alpha;
2282 static inline Guchar div255(int x) {
2283 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2286 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2288 if(c < min) c = min;
2289 if(c > max) c = max;
2293 void GFXOutputDev::clearSoftMask(GfxState *state)
2295 if(!states[statepos].softmask)
2297 states[statepos].softmask = 0;
2298 dbg("clearSoftMask statepos=%d", statepos);
2299 msg("<verbose> clearSoftMask");
2301 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2302 msg("<error> Error in softmask/tgroup ordering");
2306 gfxresult_t*mask = states[statepos].softmaskrecording;
2307 gfxresult_t*below = this->device->finish(this->device);
2308 this->device = states[statepos].olddevice;
2310 /* get outline of all objects below the soft mask */
2311 gfxdevice_t uniondev;
2312 gfxdevice_union_init(&uniondev, 0);
2313 gfxresult_record_replay(below, &uniondev);
2314 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2315 uniondev.finish(&uniondev);
2317 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2319 this->device->startclip(this->device, belowoutline);
2320 gfxresult_record_replay(below, this->device);
2321 gfxresult_record_replay(mask, this->device);
2322 this->device->endclip(this->device);
2323 gfxline_free(belowoutline);
2326 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2327 if(width<=0 || height<=0)
2330 gfxdevice_t belowrender;
2331 gfxdevice_render_init(&belowrender);
2332 if(states[statepos+1].isolated) {
2333 belowrender.setparameter(&belowrender, "fillwhite", "1");
2335 belowrender.setparameter(&belowrender, "antialize", "2");
2336 belowrender.startpage(&belowrender, width, height);
2337 gfxresult_record_replay(below, &belowrender);
2338 belowrender.endpage(&belowrender);
2339 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2340 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2341 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2343 gfxdevice_t maskrender;
2344 gfxdevice_render_init(&maskrender);
2345 maskrender.startpage(&maskrender, width, height);
2346 gfxresult_record_replay(mask, &maskrender);
2347 maskrender.endpage(&maskrender);
2348 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2349 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2351 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2352 msg("<fatal> Internal error in mask drawing");
2357 for(y=0;y<height;y++) {
2358 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2359 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2360 for(x=0;x<width;x++) {
2362 if(states[statepos].softmask_alpha) {
2365 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2368 l2->a = div255(alpha*l2->a);
2370 /* DON'T premultiply alpha- this is done by fillbitmap,
2371 depending on the output device */
2372 //l2->r = div255(alpha*l2->r);
2373 //l2->g = div255(alpha*l2->g);
2374 //l2->b = div255(alpha*l2->b);
2380 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2383 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2384 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2386 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2388 mask->destroy(mask);
2389 below->destroy(below);
2390 maskresult->destroy(maskresult);
2391 belowresult->destroy(belowresult);
2392 states[statepos].softmaskrecording = 0;
2397 // public: ~MemCheck()
2399 // delete globalParams;globalParams=0;
2400 // Object::memCheck(stderr);
2401 // gMemReport(stderr);