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);
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 #define RENDER_FILL 0
854 #define RENDER_STROKE 1
855 #define RENDER_FILLSTROKE 2
856 #define RENDER_INVISIBLE 3
857 #define RENDER_CLIP 4
859 static char tmp_printstr[4096];
860 char* makeStringPrintable(char*str)
862 int len = strlen(str);
877 tmp_printstr[len++] = '.';
878 tmp_printstr[len++] = '.';
879 tmp_printstr[len++] = '.';
881 tmp_printstr[len] = 0;
884 #define INTERNAL_FONT_SIZE 1024.0
885 void GFXOutputDev::updateFontMatrix(GfxState*state)
887 double* ctm = state->getCTM();
888 double fontSize = state->getFontSize();
889 double*textMat = state->getTextMat();
891 /* taking the absolute value of horizScaling seems to be required for
892 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
893 double hscale = fabs(state->getHorizScaling());
895 // from xpdf-3.02/SplashOutputDev:updateFont
896 double mm11 = textMat[0] * fontSize * hscale;
897 double mm12 = textMat[1] * fontSize * hscale;
898 double mm21 = textMat[2] * fontSize;
899 double mm22 = textMat[3] * fontSize;
901 // multiply with ctm, like state->getFontTransMat() does
902 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
903 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
904 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
905 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
906 this->current_font_matrix.tx = 0;
907 this->current_font_matrix.ty = 0;
910 void GFXOutputDev::beginString(GfxState *state, GString *s)
912 int render = state->getRender();
913 if(current_text_stroke) {
914 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
917 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
920 static gfxline_t* mkEmptyGfxShape(double x, double y)
922 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
923 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
927 static char isValidUnicode(int c)
929 if(c>=32 && c<0x2fffe)
934 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
935 double dx, double dy,
936 double originX, double originY,
937 CharCode charid, int nBytes, Unicode *_u, int uLen)
939 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
940 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
944 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
946 int render = state->getRender();
947 gfxcolor_t col = getFillColor(state);
949 // check for invisible text -- this is used by Acrobat Capture
950 if (render == RENDER_INVISIBLE) {
952 if(!config_extrafontdata)
956 GfxFont*font = state->getFont();
958 if(font->getType() == fontType3) {
959 /* type 3 chars are passed as graphics */
960 msg("<debug> type3 char at %f/%f", x, y);
964 Unicode u = uLen?(_u[0]):0;
965 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);
967 gfxmatrix_t m = this->current_font_matrix;
968 state->transform(x, y, &m.tx, &m.ty);
969 m.tx += user_movex + clipmovex;
970 m.ty += user_movey + clipmovey;
972 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
973 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
975 msg("<debug> Drawing glyph %d as shape", charid);
977 msg("<notice> Some texts will be rendered as shape");
980 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
981 gfxline_t*tglyph = gfxline_clone(glyph);
982 gfxline_transform(tglyph, &m);
983 if((render&3) != RENDER_INVISIBLE) {
984 gfxline_t*add = gfxline_clone(tglyph);
985 current_text_stroke = gfxline_append(current_text_stroke, add);
987 if(render&RENDER_CLIP) {
988 gfxline_t*add = gfxline_clone(tglyph);
989 current_text_clip = gfxline_append(current_text_clip, add);
990 if(!current_text_clip) {
991 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
994 gfxline_free(tglyph);
998 void GFXOutputDev::endString(GfxState *state)
1000 int render = state->getRender();
1001 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1003 if(current_text_stroke) {
1004 /* fillstroke and stroke text rendering objects we can process right
1005 now (as there may be texts of other rendering modes in this
1006 text object)- clipping objects have to wait until endTextObject,
1008 device->setparameter(device, "mark","TXT");
1009 if((render&3) == RENDER_FILL) {
1010 fillGfxLine(state, current_text_stroke);
1011 gfxline_free(current_text_stroke);
1012 current_text_stroke = 0;
1013 } else if((render&3) == RENDER_FILLSTROKE) {
1014 fillGfxLine(state, current_text_stroke);
1015 strokeGfxline(state, current_text_stroke,0);
1016 gfxline_free(current_text_stroke);
1017 current_text_stroke = 0;
1018 } else if((render&3) == RENDER_STROKE) {
1019 strokeGfxline(state, current_text_stroke,0);
1020 gfxline_free(current_text_stroke);
1021 current_text_stroke = 0;
1023 device->setparameter(device, "mark","");
1027 void GFXOutputDev::endTextObject(GfxState *state)
1029 int render = state->getRender();
1030 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1032 if(current_text_clip) {
1033 device->setparameter(device, "mark","TXT");
1034 clipToGfxLine(state, current_text_clip);
1035 device->setparameter(device, "mark","");
1036 gfxline_free(current_text_clip);
1037 current_text_clip = 0;
1041 /* the logic seems to be as following:
1042 first, beginType3Char is called, with the charcode and the coordinates.
1043 if this function returns true, it already knew about the char and has now drawn it.
1044 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1045 called with some parameters.
1046 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1047 at the position first passed to beginType3Char). the char ends with endType3Char.
1049 The drawing operations between beginType3Char and endType3Char are somewhat different to
1050 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1051 color determines the color of a font)
1054 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1056 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1059 if(config_extrafontdata && current_fontinfo) {
1061 gfxmatrix_t m = this->current_font_matrix;
1062 state->transform(0, 0, &m.tx, &m.ty);
1063 m.m00*=INTERNAL_FONT_SIZE;
1064 m.m01*=INTERNAL_FONT_SIZE;
1065 m.m10*=INTERNAL_FONT_SIZE;
1066 m.m11*=INTERNAL_FONT_SIZE;
1067 m.tx += user_movex + clipmovex;
1068 m.ty += user_movey + clipmovey;
1070 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1071 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1074 gfxcolor_t col={0,0,0,0};
1075 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1076 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1080 /* the character itself is going to be passed using the draw functions */
1081 return gFalse; /* gTrue= is_in_cache? */
1084 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1086 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1089 void GFXOutputDev::endType3Char(GfxState *state)
1092 msg("<debug> endType3Char");
1095 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1097 this->currentpage = pageNum;
1099 int rot = doc->getPageRotate(1);
1102 gfxline_t clippath[5];
1104 white.r = white.g = white.b = white.a = 255;
1106 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1107 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1108 Use CropBox, not MediaBox, as page size
1115 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1116 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1118 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1119 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1121 this->clipmovex = -(int)x1;
1122 this->clipmovey = -(int)y1;
1124 /* apply user clip box */
1125 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1126 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1127 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1128 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1129 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1130 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1133 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1135 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);
1137 msg("<verbose> page is rotated %d degrees\n", rot);
1139 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1140 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1141 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1142 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1143 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1144 device->startclip(device, clippath); outer_clip_box = 1;
1145 if(!config_transparent) {
1146 device->fill(device, clippath, &white);
1151 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1153 double x1, y1, x2, y2, w;
1154 gfxline_t points[5];
1157 msg("<debug> drawlink\n");
1159 link->getRect(&x1, &y1, &x2, &y2);
1160 cvtUserToDev(x1, y1, &x, &y);
1161 points[0].type = gfx_moveTo;
1162 points[0].x = points[4].x = x + user_movex + clipmovex;
1163 points[0].y = points[4].y = y + user_movey + clipmovey;
1164 points[0].next = &points[1];
1165 cvtUserToDev(x2, y1, &x, &y);
1166 points[1].type = gfx_lineTo;
1167 points[1].x = x + user_movex + clipmovex;
1168 points[1].y = y + user_movey + clipmovey;
1169 points[1].next = &points[2];
1170 cvtUserToDev(x2, y2, &x, &y);
1171 points[2].type = gfx_lineTo;
1172 points[2].x = x + user_movex + clipmovex;
1173 points[2].y = y + user_movey + clipmovey;
1174 points[2].next = &points[3];
1175 cvtUserToDev(x1, y2, &x, &y);
1176 points[3].type = gfx_lineTo;
1177 points[3].x = x + user_movex + clipmovex;
1178 points[3].y = y + user_movey + clipmovey;
1179 points[3].next = &points[4];
1180 cvtUserToDev(x1, y1, &x, &y);
1181 points[4].type = gfx_lineTo;
1182 points[4].x = x + user_movex + clipmovex;
1183 points[4].y = y + user_movey + clipmovey;
1186 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1187 points[0].x, points[0].y,
1188 points[1].x, points[1].y,
1189 points[2].x, points[2].y,
1190 points[3].x, points[3].y);
1192 LinkAction*action=link->getAction();
1195 const char*type = "-?-";
1198 msg("<trace> drawlink action=%d\n", action->getKind());
1199 switch(action->getKind())
1203 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1204 LinkDest *dest=NULL;
1205 if (ha->getDest()==NULL)
1206 dest=catalog->findDest(ha->getNamedDest());
1207 else dest=ha->getDest();
1209 if (dest->isPageRef()){
1210 Ref pageref=dest->getPageRef();
1211 page=catalog->findPage(pageref.num,pageref.gen);
1213 else page=dest->getPageNum();
1214 sprintf(buf, "%d", page);
1221 LinkGoToR*l = (LinkGoToR*)action;
1222 GString*g = l->getFileName();
1224 s = strdup(g->getCString());
1226 /* if the GoToR link has no filename, then
1227 try to find a refernce in the *local*
1229 GString*g = l->getNamedDest();
1231 s = strdup(g->getCString());
1237 LinkNamed*l = (LinkNamed*)action;
1238 GString*name = l->getName();
1240 s = strdup(name->lowerCase()->getCString());
1241 named = name->getCString();
1244 if(strstr(s, "next") || strstr(s, "forward"))
1246 page = currentpage + 1;
1248 else if(strstr(s, "prev") || strstr(s, "back"))
1250 page = currentpage - 1;
1252 else if(strstr(s, "last") || strstr(s, "end"))
1254 if(pages && pagepos>0)
1255 page = pages[pagepos-1];
1257 else if(strstr(s, "first") || strstr(s, "top"))
1265 case actionLaunch: {
1267 LinkLaunch*l = (LinkLaunch*)action;
1268 GString * str = new GString(l->getFileName());
1269 GString * params = l->getParams();
1271 str->append(params);
1272 s = strdup(str->getCString());
1279 LinkURI*l = (LinkURI*)action;
1280 GString*g = l->getURI();
1282 url = g->getCString();
1287 case actionUnknown: {
1289 LinkUnknown*l = (LinkUnknown*)action;
1294 msg("<error> Unknown link type!\n");
1299 if(!s) s = strdup("-?-");
1301 msg("<trace> drawlink s=%s\n", s);
1303 if(!linkinfo && (page || s))
1305 msg("<notice> File contains links");
1313 for(t=1;t<=pagepos;t++) {
1314 if(pages[t]==page) {
1323 sprintf(buf, "page%d", lpage);
1324 device->drawlink(device, points, buf);
1328 device->drawlink(device, points, s);
1331 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1335 void GFXOutputDev::saveState(GfxState *state) {
1336 dbg("saveState");dbgindent+=2;
1338 msg("<trace> saveState\n");
1341 msg("<error> Too many nested states in pdf.");
1345 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1346 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1347 states[statepos].clipping = 0;
1350 void GFXOutputDev::restoreState(GfxState *state) {
1351 dbgindent-=2; dbg("restoreState");
1354 msg("<error> Invalid restoreState");
1357 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1358 states[statepos].clipping?" (end clipping)":"");
1359 if(states[statepos].softmask) {
1360 clearSoftMask(state);
1363 while(states[statepos].clipping) {
1364 device->endclip(device);
1365 states[statepos].clipping--;
1370 void GFXOutputDev::updateLineWidth(GfxState *state)
1372 double width = state->getTransformedLineWidth();
1373 //swfoutput_setlinewidth(&device, width);
1376 void GFXOutputDev::updateLineCap(GfxState *state)
1378 int c = state->getLineCap();
1381 void GFXOutputDev::updateLineJoin(GfxState *state)
1383 int j = state->getLineJoin();
1386 void GFXOutputDev::updateFillColor(GfxState *state)
1389 double opaq = state->getFillOpacity();
1390 state->getFillRGB(&rgb);
1392 void GFXOutputDev::updateFillOpacity(GfxState *state)
1395 double opaq = state->getFillOpacity();
1396 state->getFillRGB(&rgb);
1397 dbg("update fillopaq %f", opaq);
1399 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1401 double opaq = state->getFillOpacity();
1402 dbg("update strokeopaq %f", opaq);
1404 void GFXOutputDev::updateFillOverprint(GfxState *state)
1406 double opaq = state->getFillOverprint();
1407 dbg("update filloverprint %f", opaq);
1409 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1411 double opaq = state->getStrokeOverprint();
1412 dbg("update strokeoverprint %f", opaq);
1414 void GFXOutputDev::updateTransfer(GfxState *state)
1416 dbg("update transfer");
1420 void GFXOutputDev::updateStrokeColor(GfxState *state)
1423 double opaq = state->getStrokeOpacity();
1424 state->getStrokeRGB(&rgb);
1428 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1430 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1431 memset(font, 0, sizeof(gfxfont_t));
1433 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1434 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1435 font->id = strdup(getFontID(xpdffont));
1437 double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
1439 //printf("%d glyphs\n", font->num_glyphs);
1440 font->num_glyphs = 0;
1441 for(t=0;t<src->num_glyphs;t++) {
1442 if(src->glyphs[t]) {
1443 SplashPath*path = src->glyphs[t]->path;
1444 int len = path?path->getLength():0;
1445 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1446 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1447 src->glyphs[t]->glyphid = font->num_glyphs;
1448 glyph->unicode = src->glyphs[t]->unicode;
1449 if(glyph->unicode >= font->max_unicode)
1450 font->max_unicode = glyph->unicode+1;
1452 gfxdrawer_target_gfxline(&drawer);
1456 for(s=0;s<len;s++) {
1459 path->getPoint(s, &x, &y, &f);
1462 if(f&splashPathFirst) {
1463 drawer.moveTo(&drawer, x*scale, y*scale);
1465 if(f&splashPathCurve) {
1467 path->getPoint(++s, &x2, &y2, &f);
1468 if(f&splashPathCurve) {
1470 path->getPoint(++s, &x3, &y3, &f);
1471 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1473 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1476 drawer.lineTo(&drawer, x*scale, y*scale);
1478 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1479 // (f&splashPathFirst)?"first":"",
1480 // (f&splashPathLast)?"last":"");
1482 glyph->line = (gfxline_t*)drawer.result(&drawer);
1483 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1487 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1488 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1489 for(t=0;t<font->num_glyphs;t++) {
1490 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1491 font->unicode2glyph[font->glyphs[t].unicode] = t;
1495 msg("<trace> %d glyphs.", t, font->num_glyphs);
1499 void GFXOutputDev::updateFont(GfxState *state)
1501 GfxFont* gfxFont = state->getFont();
1505 char*id = getFontID(gfxFont);
1506 msg("<verbose> Updating font to %s", id);
1507 if(gfxFont->getType() == fontType3) {
1508 infofeature("Type3 fonts");
1509 if(!config_extrafontdata) {
1514 msg("<error> Internal Error: FontID is null");
1518 this->current_fontinfo = this->info->getFont(id);
1519 if(!this->current_fontinfo) {
1520 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1522 if(!this->current_fontinfo->seen) {
1523 dumpFontInfo("<verbose>", gfxFont);
1526 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1528 font = createGfxFont(gfxFont, current_fontinfo);
1529 font->id = strdup(id);
1530 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1531 device->addfont(device, font);
1533 current_gfxfont = font;
1536 updateFontMatrix(state);
1539 #define SQR(x) ((x)*(x))
1541 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1543 if((newwidth<2 || newheight<2) ||
1544 (width<=newwidth || height<=newheight))
1546 unsigned char*newdata;
1548 newdata= (unsigned char*)malloc(newwidth*newheight);
1550 double fx = (double)(width)/newwidth;
1551 double fy = (double)(height)/newheight;
1553 int blocksize = (int)(8192/(fx*fy));
1554 int r = 8192*256/palettesize;
1555 for(x=0;x<newwidth;x++) {
1556 double ex = px + fx;
1557 int fromx = (int)px;
1559 int xweight1 = (int)(((fromx+1)-px)*256);
1560 int xweight2 = (int)((ex-tox)*256);
1562 for(y=0;y<newheight;y++) {
1563 double ey = py + fy;
1564 int fromy = (int)py;
1566 int yweight1 = (int)(((fromy+1)-py)*256);
1567 int yweight2 = (int)((ey-toy)*256);
1570 for(xx=fromx;xx<=tox;xx++)
1571 for(yy=fromy;yy<=toy;yy++) {
1572 int b = 1-data[width*yy+xx];
1574 if(xx==fromx) weight = (weight*xweight1)/256;
1575 if(xx==tox) weight = (weight*xweight2)/256;
1576 if(yy==fromy) weight = (weight*yweight1)/256;
1577 if(yy==toy) weight = (weight*yweight2)/256;
1580 //if(a) a=(palettesize-1)*r/blocksize;
1581 newdata[y*newwidth+x] = (a*blocksize)/r;
1589 #define IMAGE_TYPE_JPEG 0
1590 #define IMAGE_TYPE_LOSSLESS 1
1592 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1593 double x1,double y1,
1594 double x2,double y2,
1595 double x3,double y3,
1596 double x4,double y4, int type)
1598 gfxcolor_t*newpic=0;
1600 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1601 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1603 gfxline_t p1,p2,p3,p4,p5;
1604 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1605 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1606 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1607 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1608 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1610 {p1.x = (int)(p1.x*20)/20.0;
1611 p1.y = (int)(p1.y*20)/20.0;
1612 p2.x = (int)(p2.x*20)/20.0;
1613 p2.y = (int)(p2.y*20)/20.0;
1614 p3.x = (int)(p3.x*20)/20.0;
1615 p3.y = (int)(p3.y*20)/20.0;
1616 p4.x = (int)(p4.x*20)/20.0;
1617 p4.y = (int)(p4.y*20)/20.0;
1618 p5.x = (int)(p5.x*20)/20.0;
1619 p5.y = (int)(p5.y*20)/20.0;
1626 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1627 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1632 img.data = (gfxcolor_t*)data;
1636 if(type == IMAGE_TYPE_JPEG)
1637 /* TODO: pass image_dpi to device instead */
1638 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1640 dev->fillbitmap(dev, &p1, &img, &m, 0);
1643 void drawimagejpeg(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_JPEG);
1649 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1650 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1652 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1656 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1657 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1658 GBool inlineImg, int mask, int*maskColors,
1659 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1661 double x1,y1,x2,y2,x3,y3,x4,y4;
1662 ImageStream *imgStr;
1667 unsigned char* maskbitmap = 0;
1670 ncomps = colorMap->getNumPixelComps();
1671 bits = colorMap->getBits();
1676 unsigned char buf[8];
1677 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1679 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1680 imgMaskStr->reset();
1681 unsigned char pal[256];
1682 int n = 1 << colorMap->getBits();
1687 maskColorMap->getGray(pixBuf, &gray);
1688 pal[t] = colToByte(gray);
1690 for (y = 0; y < maskHeight; y++) {
1691 for (x = 0; x < maskWidth; x++) {
1692 imgMaskStr->getPixel(buf);
1693 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1698 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1699 imgMaskStr->reset();
1700 for (y = 0; y < maskHeight; y++) {
1701 for (x = 0; x < maskWidth; x++) {
1702 imgMaskStr->getPixel(buf);
1704 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1712 imgStr = new ImageStream(str, width, ncomps,bits);
1715 if(!width || !height || (height<=1 && width<=1))
1717 msg("<verbose> Ignoring %d by %d image", width, height);
1718 unsigned char buf[8];
1720 for (y = 0; y < height; ++y)
1721 for (x = 0; x < width; ++x) {
1722 imgStr->getPixel(buf);
1730 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1731 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1732 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1733 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1735 if(!pbminfo && !(str->getKind()==strDCT)) {
1737 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1741 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1743 if(!jpeginfo && (str->getKind()==strDCT)) {
1744 msg("<notice> file contains jpeg pictures");
1750 unsigned char buf[8];
1752 unsigned char*pic = new unsigned char[width*height];
1753 gfxcolor_t pal[256];
1755 state->getFillRGB(&rgb);
1757 memset(pal,255,sizeof(pal));
1758 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1759 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1760 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1761 pal[0].a = 255; pal[1].a = 0;
1764 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1765 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1766 for (y = 0; y < height; ++y)
1767 for (x = 0; x < width; ++x)
1769 imgStr->getPixel(buf);
1772 pic[width*y+x] = buf[0];
1775 /* the size of the drawn image is added to the identifier
1776 as the same image may require different bitmaps if displayed
1777 at different sizes (due to antialiasing): */
1780 unsigned char*pic2 = 0;
1783 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1792 height = realheight;
1796 /* make a black/white palette */
1798 float r = 255/(numpalette-1);
1800 for(t=0;t<numpalette;t++) {
1801 pal[t].r = colToByte(rgb.r);
1802 pal[t].g = colToByte(rgb.g);
1803 pal[t].b = colToByte(rgb.b);
1804 pal[t].a = (unsigned char)(t*r);
1808 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1809 for (y = 0; y < height; ++y) {
1810 for (x = 0; x < width; ++x) {
1811 pic2[width*y+x] = pal[pic[y*width+x]];
1814 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1818 if(maskbitmap) free(maskbitmap);
1824 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1825 gfxcolor_t*pic=new gfxcolor_t[width*height];
1826 for (y = 0; y < height; ++y) {
1827 for (x = 0; x < width; ++x) {
1828 imgStr->getPixel(pixBuf);
1829 colorMap->getRGB(pixBuf, &rgb);
1830 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1831 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1832 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1833 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1835 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1839 if(str->getKind()==strDCT)
1840 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1842 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1845 if(maskbitmap) free(maskbitmap);
1848 gfxcolor_t*pic=new gfxcolor_t[width*height];
1849 gfxcolor_t pal[256];
1850 int n = 1 << colorMap->getBits();
1852 for(t=0;t<256;t++) {
1854 colorMap->getRGB(pixBuf, &rgb);
1856 {/*if(maskColors && *maskColors==t) {
1857 msg("<notice> Color %d is transparent", t);
1858 if (imgData->maskColors) {
1860 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1861 if (pix[i] < imgData->maskColors[2*i] ||
1862 pix[i] > imgData->maskColors[2*i+1]) {
1877 pal[t].r = (unsigned char)(colToByte(rgb.r));
1878 pal[t].g = (unsigned char)(colToByte(rgb.g));
1879 pal[t].b = (unsigned char)(colToByte(rgb.b));
1880 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1883 for (y = 0; y < height; ++y) {
1884 for (x = 0; x < width; ++x) {
1885 imgStr->getPixel(pixBuf);
1886 pic[width*y+x] = pal[pixBuf[0]];
1888 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1892 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1896 if(maskbitmap) free(maskbitmap);
1901 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1902 int width, int height, GBool invert,
1905 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1906 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1907 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1910 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1911 int width, int height, GfxImageColorMap *colorMap,
1912 int *maskColors, GBool inlineImg)
1914 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1915 colorMap?"colorMap":"no colorMap",
1916 maskColors?"maskColors":"no maskColors",
1918 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1919 colorMap?"colorMap":"no colorMap",
1920 maskColors?"maskColors":"no maskColors",
1923 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1924 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1925 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1928 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1929 int width, int height,
1930 GfxImageColorMap *colorMap,
1931 Stream *maskStr, int maskWidth, int maskHeight,
1934 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1935 colorMap?"colorMap":"no colorMap",
1936 maskWidth, maskHeight);
1937 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1938 colorMap?"colorMap":"no colorMap",
1939 maskWidth, maskHeight);
1941 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1942 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1943 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1946 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1947 int width, int height,
1948 GfxImageColorMap *colorMap,
1950 int maskWidth, int maskHeight,
1951 GfxImageColorMap *maskColorMap)
1953 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1954 colorMap?"colorMap":"no colorMap",
1955 maskWidth, maskHeight);
1956 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1957 colorMap?"colorMap":"no colorMap",
1958 maskWidth, maskHeight);
1960 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1961 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1962 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1965 void GFXOutputDev::stroke(GfxState *state)
1969 GfxPath * path = state->getPath();
1970 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1971 strokeGfxline(state, line, 0);
1975 void GFXOutputDev::fill(GfxState *state)
1977 gfxcolor_t col = getFillColor(state);
1978 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1980 GfxPath * path = state->getPath();
1981 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1982 fillGfxLine(state, line);
1986 void GFXOutputDev::eoFill(GfxState *state)
1988 gfxcolor_t col = getFillColor(state);
1989 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1991 GfxPath * path = state->getPath();
1992 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1993 fillGfxLine(state, line);
1998 static const char* dirseparator()
2007 void addGlobalFont(const char*filename)
2010 memset(&f, 0, sizeof(fontfile_t));
2011 f.filename = filename;
2012 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2013 msg("<notice> Adding font \"%s\".", filename);
2014 fonts[fontnum++] = f;
2016 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2020 void addGlobalLanguageDir(const char*dir)
2022 msg("<notice> Adding %s to language pack directories", dir);
2026 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2027 strcpy(config_file, dir);
2028 strcat(config_file, dirseparator());
2029 strcat(config_file, "add-to-xpdfrc");
2031 fi = fopen(config_file, "rb");
2033 msg("<error> Could not open %s", config_file);
2036 globalParams->parseFile(new GString(config_file), fi);
2040 void addGlobalFontDir(const char*dirname)
2042 #ifdef HAVE_DIRENT_H
2043 msg("<notice> Adding %s to font directories", dirname);
2044 lastfontdir = strdup(dirname);
2045 DIR*dir = opendir(dirname);
2047 msg("<warning> Couldn't open directory %s\n", dirname);
2052 ent = readdir (dir);
2056 char*name = ent->d_name;
2062 if(!strncasecmp(&name[l-4], ".pfa", 4))
2064 if(!strncasecmp(&name[l-4], ".pfb", 4))
2066 if(!strncasecmp(&name[l-4], ".ttf", 4))
2069 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2070 strcpy(fontname, dirname);
2071 strcat(fontname, dirseparator());
2072 strcat(fontname, name);
2073 addGlobalFont(fontname);
2078 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2082 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2088 this->pagebuflen = 1024;
2089 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2090 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2092 while(pdfpage >= this->pagebuflen)
2094 int oldlen = this->pagebuflen;
2095 this->pagebuflen+=1024;
2096 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2097 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2100 this->pages[pdfpage] = outputpage;
2101 if(pdfpage>this->pagepos)
2102 this->pagepos = pdfpage;
2108 double width,height;
2111 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2113 double xMin, yMin, xMax, yMax, x, y;
2114 double tx, ty, w, h;
2115 // transform the bbox
2116 state->transform(bbox[0], bbox[1], &x, &y);
2119 state->transform(bbox[0], bbox[3], &x, &y);
2122 } else if (x > xMax) {
2127 } else if (y > yMax) {
2130 state->transform(bbox[2], bbox[1], &x, &y);
2133 } else if (x > xMax) {
2138 } else if (y > yMax) {
2141 state->transform(bbox[2], bbox[3], &x, &y);
2144 } else if (x > xMax) {
2149 } else if (y > yMax) {
2152 tx = (int)floor(xMin);
2155 } else if (tx > width) {
2158 ty = (int)floor(yMin);
2161 } else if (ty > height) {
2164 w = (int)ceil(xMax) - tx + 1;
2165 if (tx + w > width) {
2171 h = (int)ceil(yMax) - ty + 1;
2172 if (ty + h > height) {
2186 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2187 GfxColorSpace *blendingColorSpace,
2188 GBool isolated, GBool knockout,
2191 const char*colormodename = "";
2193 if(blendingColorSpace) {
2194 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2196 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);
2197 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);
2199 states[statepos].createsoftmask |= forSoftMask;
2200 states[statepos].transparencygroup = !forSoftMask;
2201 states[statepos].isolated = isolated;
2203 states[statepos].olddevice = this->device;
2204 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2206 gfxdevice_record_init(this->device);
2208 /*if(!forSoftMask) { ////???
2209 state->setFillOpacity(0.0);
2214 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2217 dbg("endTransparencyGroup");
2218 msg("<verbose> endTransparencyGroup");
2220 gfxdevice_t*r = this->device;
2222 this->device = states[statepos].olddevice;
2224 if(states[statepos].createsoftmask) {
2225 states[statepos-1].softmaskrecording = r->finish(r);
2227 states[statepos-1].grouprecording = r->finish(r);
2230 states[statepos].createsoftmask = 0;
2231 states[statepos].transparencygroup = 0;
2235 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2237 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2238 "colordodge","colorburn","hardlight","softlight","difference",
2239 "exclusion","hue","saturation","color","luminosity"};
2241 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2242 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2244 if(state->getBlendMode() == gfxBlendNormal)
2245 infofeature("transparency groups");
2248 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2249 warnfeature(buffer, 0);
2252 gfxresult_t*grouprecording = states[statepos].grouprecording;
2254 if(state->getBlendMode() == gfxBlendNormal) {
2256 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2257 gfxresult_record_replay(grouprecording, &ops);
2260 grouprecording->destroy(grouprecording);
2262 states[statepos].grouprecording = 0;
2265 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2267 /* alpha = 1: retrieve mask values from alpha layer
2268 alpha = 0: retrieve mask values from luminance */
2269 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2270 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2271 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2272 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2274 infofeature("soft masks");
2276 warnfeature("soft masks from alpha channel",0);
2278 states[statepos].olddevice = this->device;
2279 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2280 gfxdevice_record_init(this->device);
2282 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2284 states[statepos].softmask = 1;
2285 states[statepos].softmask_alpha = alpha;
2288 static inline Guchar div255(int x) {
2289 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2292 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2294 if(c < min) c = min;
2295 if(c > max) c = max;
2299 void GFXOutputDev::clearSoftMask(GfxState *state)
2301 if(!states[statepos].softmask)
2303 states[statepos].softmask = 0;
2304 dbg("clearSoftMask statepos=%d", statepos);
2305 msg("<verbose> clearSoftMask");
2307 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2308 msg("<error> Error in softmask/tgroup ordering");
2312 gfxresult_t*mask = states[statepos].softmaskrecording;
2313 gfxresult_t*below = this->device->finish(this->device);
2314 this->device = states[statepos].olddevice;
2316 /* get outline of all objects below the soft mask */
2317 gfxdevice_t uniondev;
2318 gfxdevice_union_init(&uniondev, 0);
2319 gfxresult_record_replay(below, &uniondev);
2320 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2321 uniondev.finish(&uniondev);
2323 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2325 this->device->startclip(this->device, belowoutline);
2326 gfxresult_record_replay(below, this->device);
2327 gfxresult_record_replay(mask, this->device);
2328 this->device->endclip(this->device);
2329 gfxline_free(belowoutline);
2332 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2333 if(width<=0 || height<=0)
2336 gfxdevice_t belowrender;
2337 gfxdevice_render_init(&belowrender);
2338 if(states[statepos+1].isolated) {
2339 belowrender.setparameter(&belowrender, "fillwhite", "1");
2341 belowrender.setparameter(&belowrender, "antialize", "2");
2342 belowrender.startpage(&belowrender, width, height);
2343 gfxresult_record_replay(below, &belowrender);
2344 belowrender.endpage(&belowrender);
2345 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2346 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2347 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2349 gfxdevice_t maskrender;
2350 gfxdevice_render_init(&maskrender);
2351 maskrender.startpage(&maskrender, width, height);
2352 gfxresult_record_replay(mask, &maskrender);
2353 maskrender.endpage(&maskrender);
2354 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2355 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2357 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2358 msg("<fatal> Internal error in mask drawing");
2363 for(y=0;y<height;y++) {
2364 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2365 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2366 for(x=0;x<width;x++) {
2368 if(states[statepos].softmask_alpha) {
2371 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2374 l2->a = div255(alpha*l2->a);
2376 /* DON'T premultiply alpha- this is done by fillbitmap,
2377 depending on the output device */
2378 //l2->r = div255(alpha*l2->r);
2379 //l2->g = div255(alpha*l2->g);
2380 //l2->b = div255(alpha*l2->b);
2386 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2389 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2390 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2392 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2394 mask->destroy(mask);
2395 below->destroy(below);
2396 maskresult->destroy(maskresult);
2397 belowresult->destroy(belowresult);
2398 states[statepos].softmaskrecording = 0;
2403 // public: ~MemCheck()
2405 // delete globalParams;globalParams=0;
2406 // Object::memCheck(stderr);
2407 // gMemReport(stderr);