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;
371 this->config_fontquality = 10;
373 this->gfxfontlist = gfxfontlist_create();
375 memset(states, 0, sizeof(states));
378 void GFXOutputDev::setParameter(const char*key, const char*value)
380 if(!strcmp(key,"breakonwarning")) {
381 this->config_break_on_warning = atoi(value);
382 } else if(!strcmp(key,"remapunicode")) {
383 this->config_remapunicode = atoi(value);
384 } else if(!strcmp(key,"transparent")) {
385 this->config_transparent = atoi(value);
386 } else if(!strcmp(key,"extrafontdata")) {
387 this->config_extrafontdata = atoi(value);
388 } else if(!strcmp(key,"fontquality")) {
389 this->config_fontquality = atof(value);
390 if(this->config_fontquality<=1)
391 this->config_fontquality=1;
392 } else if(!strcmp(key,"help")) {
393 printf("\nPDF layer options:\n");
394 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
395 printf(" are not 100%% supported\n");
396 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
397 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
398 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
403 void GFXOutputDev::setDevice(gfxdevice_t*dev)
408 void GFXOutputDev::setMove(int x,int y)
410 this->user_movex = x;
411 this->user_movey = y;
414 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
416 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
417 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
419 this->user_clipx1 = x1;
420 this->user_clipy1 = y1;
421 this->user_clipx2 = x2;
422 this->user_clipy2 = y2;
425 static char*getFontName(GfxFont*font)
428 GString*gstr = font->getName();
429 char* fname = gstr==0?0:gstr->getCString();
433 sprintf(buf, "UFONT%d", r->num);
434 fontid = strdup(buf);
436 fontid = strdup(fname);
440 char* plus = strchr(fontid, '+');
441 if(plus && plus < &fontid[strlen(fontid)-1]) {
442 fontname = strdup(plus+1);
444 fontname = strdup(fontid);
450 static void dumpFontInfo(const char*loglevel, GfxFont*font);
451 static int lastdumps[1024];
452 static int lastdumppos = 0;
457 static void showFontError(GfxFont*font, int nr)
461 for(t=0;t<lastdumppos;t++)
462 if(lastdumps[t] == r->num)
466 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
467 lastdumps[lastdumppos++] = r->num;
469 msg("<warning> The following font caused problems:");
471 msg("<warning> The following font caused problems (substituting):");
473 msg("<warning> The following Type 3 Font will be rendered as graphics:");
474 dumpFontInfo("<warning>", font);
477 static void dumpFontInfo(const char*loglevel, GfxFont*font)
479 char* id = getFontID(font);
480 char* name = getFontName(font);
481 Ref* r=font->getID();
482 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
484 GString*gstr = font->getTag();
486 msg("%s| Tag: %s\n", loglevel, id);
488 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
490 GfxFontType type=font->getType();
492 case fontUnknownType:
493 msg("%s| Type: unknown\n",loglevel);
496 msg("%s| Type: 1\n",loglevel);
499 msg("%s| Type: 1C\n",loglevel);
502 msg("%s| Type: 3\n",loglevel);
505 msg("%s| Type: TrueType\n",loglevel);
508 msg("%s| Type: CIDType0\n",loglevel);
511 msg("%s| Type: CIDType0C\n",loglevel);
514 msg("%s| Type: CIDType2\n",loglevel);
519 GBool embedded = font->getEmbeddedFontID(&embRef);
521 if(font->getEmbeddedFontName()) {
522 embeddedName = font->getEmbeddedFontName()->getCString();
525 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
527 gstr = font->getExtFontFile();
529 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
531 // Get font descriptor flags.
532 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
533 if(font->isSerif()) msg("%s| is serif\n", loglevel);
534 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
535 if(font->isItalic()) msg("%s| is italic\n", loglevel);
536 if(font->isBold()) msg("%s| is bold\n", loglevel);
542 //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");}
543 //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");}
545 void dump_outline(gfxline_t*line)
548 if(line->type == gfx_moveTo) {
549 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
550 } else if(line->type == gfx_lineTo) {
551 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
552 } else if(line->type == gfx_splineTo) {
553 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
559 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
561 int num = path->getNumSubpaths();
564 double lastx=0,lasty=0,posx=0,posy=0;
567 msg("<warning> empty path");
571 gfxdrawer_target_gfxline(&draw);
573 for(t = 0; t < num; t++) {
574 GfxSubpath *subpath = path->getSubpath(t);
575 int subnum = subpath->getNumPoints();
576 double bx=0,by=0,cx=0,cy=0;
578 for(s=0;s<subnum;s++) {
581 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
586 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
587 draw.lineTo(&draw, lastx, lasty);
589 draw.moveTo(&draw, x,y);
594 } else if(subpath->getCurve(s) && cpos==0) {
598 } else if(subpath->getCurve(s) && cpos==1) {
606 draw.lineTo(&draw, x,y);
608 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
615 /* fix non-closed lines */
616 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
617 draw.lineTo(&draw, lastx, lasty);
619 gfxline_t*result = (gfxline_t*)draw.result(&draw);
621 gfxline_optimize(result);
626 GBool GFXOutputDev::useTilingPatternFill()
628 infofeature("tiled patterns");
632 GBool GFXOutputDev::useShadedFills()
634 infofeature("shaded fills");
638 GBool GFXOutputDev::useDrawForm()
640 infofeature("forms");
643 void GFXOutputDev::drawForm(Ref id)
645 msg("<error> drawForm not implemented");
647 GBool GFXOutputDev::needNonText()
651 void GFXOutputDev::endPage()
653 msg("<verbose> endPage");
655 device->endclip(device);
660 #define STROKE_FILL 1
661 #define STROKE_CLIP 2
662 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
664 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
665 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
666 double miterLimit = state->getMiterLimit();
667 double width = state->getTransformedLineWidth();
670 double opaq = state->getStrokeOpacity();
672 state->getFillRGB(&rgb);
674 state->getStrokeRGB(&rgb);
676 col.r = colToByte(rgb.r);
677 col.g = colToByte(rgb.g);
678 col.b = colToByte(rgb.b);
679 col.a = (unsigned char)(opaq*255);
681 gfx_capType capType = gfx_capRound;
682 if(lineCap == 0) capType = gfx_capButt;
683 else if(lineCap == 1) capType = gfx_capRound;
684 else if(lineCap == 2) capType = gfx_capSquare;
686 gfx_joinType joinType = gfx_joinRound;
687 if(lineJoin == 0) joinType = gfx_joinMiter;
688 else if(lineJoin == 1) joinType = gfx_joinRound;
689 else if(lineJoin == 2) joinType = gfx_joinBevel;
692 double dashphase = 0;
694 state->getLineDash(&ldash, &dashnum, &dashphase);
698 if(dashnum && ldash) {
699 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
701 msg("<trace> %d dashes", dashnum);
702 msg("<trace> | phase: %f", dashphase);
703 for(t=0;t<dashnum;t++) {
705 msg("<trace> | d%-3d: %f", t, ldash[t]);
708 if(getLogLevel() >= LOGLEVEL_TRACE) {
712 line2 = gfxtool_dash_line(line, dash, dashphase);
715 msg("<trace> After dashing:");
718 if(getLogLevel() >= LOGLEVEL_TRACE) {
719 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
721 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
722 lineCap==0?"butt": (lineJoin==1?"round":"square"),
724 col.r,col.g,col.b,col.a
729 if(flags&STROKE_FILL) {
730 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
731 gfxline_t*gfxline = SVPtogfxline(svp);
732 if(getLogLevel() >= LOGLEVEL_TRACE) {
733 dump_outline(gfxline);
736 msg("<warning> Empty polygon (resulting from stroked line)");
738 if(flags&STROKE_CLIP) {
739 device->startclip(device, gfxline);
740 states[statepos].clipping++;
742 device->fill(device, gfxline, &col);
747 if(flags&STROKE_CLIP)
748 msg("<error> Stroke&clip not supported at the same time");
749 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
756 gfxcolor_t getFillColor(GfxState * state)
759 double opaq = state->getFillOpacity();
760 state->getFillRGB(&rgb);
762 col.r = colToByte(rgb.r);
763 col.g = colToByte(rgb.g);
764 col.b = colToByte(rgb.b);
765 col.a = (unsigned char)(opaq*255);
769 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
771 gfxcolor_t col = getFillColor(state);
773 if(getLogLevel() >= LOGLEVEL_TRACE) {
774 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
777 device->fill(device, line, &col);
780 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
782 if(getLogLevel() >= LOGLEVEL_TRACE) {
783 msg("<trace> clip\n");
787 device->startclip(device, line);
788 states[statepos].clipping++;
791 void GFXOutputDev::clip(GfxState *state)
793 GfxPath * path = state->getPath();
794 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
795 clipToGfxLine(state, line);
799 void GFXOutputDev::eoClip(GfxState *state)
801 GfxPath * path = state->getPath();
802 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
804 if(getLogLevel() >= LOGLEVEL_TRACE) {
805 msg("<trace> eoclip\n");
809 device->startclip(device, line);
810 states[statepos].clipping++;
813 void GFXOutputDev::clipToStrokePath(GfxState *state)
815 GfxPath * path = state->getPath();
816 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
818 if(getLogLevel() >= LOGLEVEL_TRACE) {
819 msg("<trace> cliptostrokepath\n");
823 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
827 void GFXOutputDev::finish()
831 device->endclip(device);
837 GFXOutputDev::~GFXOutputDev()
842 free(this->pages); this->pages = 0;
845 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
847 GBool GFXOutputDev::upsideDown()
851 GBool GFXOutputDev::useDrawChar()
856 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
857 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
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, double config_fontquality)
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));
1438 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1440 //printf("%d glyphs\n", font->num_glyphs);
1441 font->num_glyphs = 0;
1442 for(t=0;t<src->num_glyphs;t++) {
1443 if(src->glyphs[t]) {
1444 SplashPath*path = src->glyphs[t]->path;
1445 int len = path?path->getLength():0;
1446 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1447 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1448 src->glyphs[t]->glyphid = font->num_glyphs;
1449 glyph->unicode = src->glyphs[t]->unicode;
1450 if(glyph->unicode >= font->max_unicode)
1451 font->max_unicode = glyph->unicode+1;
1453 gfxdrawer_target_gfxline(&drawer);
1457 for(s=0;s<len;s++) {
1460 path->getPoint(s, &x, &y, &f);
1463 if(f&splashPathFirst) {
1464 drawer.moveTo(&drawer, x*scale, y*scale);
1466 if(f&splashPathCurve) {
1468 path->getPoint(++s, &x2, &y2, &f);
1469 if(f&splashPathCurve) {
1471 path->getPoint(++s, &x3, &y3, &f);
1472 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1474 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1477 drawer.lineTo(&drawer, x*scale, y*scale);
1479 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1480 // (f&splashPathFirst)?"first":"",
1481 // (f&splashPathLast)?"last":"");
1483 glyph->line = (gfxline_t*)drawer.result(&drawer);
1484 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1488 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1489 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1490 for(t=0;t<font->num_glyphs;t++) {
1491 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1492 font->unicode2glyph[font->glyphs[t].unicode] = t;
1496 msg("<trace> %d glyphs.", t, font->num_glyphs);
1500 void GFXOutputDev::updateFont(GfxState *state)
1502 GfxFont* gfxFont = state->getFont();
1506 char*id = getFontID(gfxFont);
1507 msg("<verbose> Updating font to %s", id);
1508 if(gfxFont->getType() == fontType3) {
1509 infofeature("Type3 fonts");
1510 if(!config_extrafontdata) {
1515 msg("<error> Internal Error: FontID is null");
1519 this->current_fontinfo = this->info->getFont(id);
1520 if(!this->current_fontinfo) {
1521 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1523 if(!this->current_fontinfo->seen) {
1524 dumpFontInfo("<verbose>", gfxFont);
1527 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1529 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1530 font->id = strdup(id);
1531 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1532 device->addfont(device, font);
1534 current_gfxfont = font;
1537 updateFontMatrix(state);
1540 #define SQR(x) ((x)*(x))
1542 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1544 if((newwidth<2 || newheight<2) ||
1545 (width<=newwidth || height<=newheight))
1547 unsigned char*newdata;
1549 newdata= (unsigned char*)malloc(newwidth*newheight);
1551 double fx = (double)(width)/newwidth;
1552 double fy = (double)(height)/newheight;
1554 int blocksize = (int)(8192/(fx*fy));
1555 int r = 8192*256/palettesize;
1556 for(x=0;x<newwidth;x++) {
1557 double ex = px + fx;
1558 int fromx = (int)px;
1560 int xweight1 = (int)(((fromx+1)-px)*256);
1561 int xweight2 = (int)((ex-tox)*256);
1563 for(y=0;y<newheight;y++) {
1564 double ey = py + fy;
1565 int fromy = (int)py;
1567 int yweight1 = (int)(((fromy+1)-py)*256);
1568 int yweight2 = (int)((ey-toy)*256);
1571 for(xx=fromx;xx<=tox;xx++)
1572 for(yy=fromy;yy<=toy;yy++) {
1573 int b = 1-data[width*yy+xx];
1575 if(xx==fromx) weight = (weight*xweight1)/256;
1576 if(xx==tox) weight = (weight*xweight2)/256;
1577 if(yy==fromy) weight = (weight*yweight1)/256;
1578 if(yy==toy) weight = (weight*yweight2)/256;
1581 //if(a) a=(palettesize-1)*r/blocksize;
1582 newdata[y*newwidth+x] = (a*blocksize)/r;
1590 #define IMAGE_TYPE_JPEG 0
1591 #define IMAGE_TYPE_LOSSLESS 1
1593 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1594 double x1,double y1,
1595 double x2,double y2,
1596 double x3,double y3,
1597 double x4,double y4, int type)
1599 gfxcolor_t*newpic=0;
1601 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1602 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1604 gfxline_t p1,p2,p3,p4,p5;
1605 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1606 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1607 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1608 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1609 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1611 {p1.x = (int)(p1.x*20)/20.0;
1612 p1.y = (int)(p1.y*20)/20.0;
1613 p2.x = (int)(p2.x*20)/20.0;
1614 p2.y = (int)(p2.y*20)/20.0;
1615 p3.x = (int)(p3.x*20)/20.0;
1616 p3.y = (int)(p3.y*20)/20.0;
1617 p4.x = (int)(p4.x*20)/20.0;
1618 p4.y = (int)(p4.y*20)/20.0;
1619 p5.x = (int)(p5.x*20)/20.0;
1620 p5.y = (int)(p5.y*20)/20.0;
1627 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1628 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1633 img.data = (gfxcolor_t*)data;
1637 if(type == IMAGE_TYPE_JPEG)
1638 /* TODO: pass image_dpi to device instead */
1639 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1641 dev->fillbitmap(dev, &p1, &img, &m, 0);
1644 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1645 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1647 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1650 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1651 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1653 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1657 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1658 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1659 GBool inlineImg, int mask, int*maskColors,
1660 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1662 double x1,y1,x2,y2,x3,y3,x4,y4;
1663 ImageStream *imgStr;
1668 unsigned char* maskbitmap = 0;
1671 ncomps = colorMap->getNumPixelComps();
1672 bits = colorMap->getBits();
1677 unsigned char buf[8];
1678 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1680 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1681 imgMaskStr->reset();
1682 unsigned char pal[256];
1683 int n = 1 << colorMap->getBits();
1688 maskColorMap->getGray(pixBuf, &gray);
1689 pal[t] = colToByte(gray);
1691 for (y = 0; y < maskHeight; y++) {
1692 for (x = 0; x < maskWidth; x++) {
1693 imgMaskStr->getPixel(buf);
1694 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1699 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1700 imgMaskStr->reset();
1701 for (y = 0; y < maskHeight; y++) {
1702 for (x = 0; x < maskWidth; x++) {
1703 imgMaskStr->getPixel(buf);
1705 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1713 imgStr = new ImageStream(str, width, ncomps,bits);
1716 if(!width || !height || (height<=1 && width<=1))
1718 msg("<verbose> Ignoring %d by %d image", width, height);
1719 unsigned char buf[8];
1721 for (y = 0; y < height; ++y)
1722 for (x = 0; x < width; ++x) {
1723 imgStr->getPixel(buf);
1731 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1732 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1733 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1734 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1736 if(!pbminfo && !(str->getKind()==strDCT)) {
1738 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1742 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1744 if(!jpeginfo && (str->getKind()==strDCT)) {
1745 msg("<notice> file contains jpeg pictures");
1751 unsigned char buf[8];
1753 unsigned char*pic = new unsigned char[width*height];
1754 gfxcolor_t pal[256];
1756 state->getFillRGB(&rgb);
1758 memset(pal,255,sizeof(pal));
1759 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1760 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1761 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1762 pal[0].a = 255; pal[1].a = 0;
1765 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1766 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1767 for (y = 0; y < height; ++y)
1768 for (x = 0; x < width; ++x)
1770 imgStr->getPixel(buf);
1773 pic[width*y+x] = buf[0];
1776 /* the size of the drawn image is added to the identifier
1777 as the same image may require different bitmaps if displayed
1778 at different sizes (due to antialiasing): */
1781 unsigned char*pic2 = 0;
1784 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1793 height = realheight;
1797 /* make a black/white palette */
1799 float r = 255/(numpalette-1);
1801 for(t=0;t<numpalette;t++) {
1802 pal[t].r = colToByte(rgb.r);
1803 pal[t].g = colToByte(rgb.g);
1804 pal[t].b = colToByte(rgb.b);
1805 pal[t].a = (unsigned char)(t*r);
1809 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1810 for (y = 0; y < height; ++y) {
1811 for (x = 0; x < width; ++x) {
1812 pic2[width*y+x] = pal[pic[y*width+x]];
1815 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1819 if(maskbitmap) free(maskbitmap);
1825 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1826 gfxcolor_t*pic=new gfxcolor_t[width*height];
1827 for (y = 0; y < height; ++y) {
1828 for (x = 0; x < width; ++x) {
1829 imgStr->getPixel(pixBuf);
1830 colorMap->getRGB(pixBuf, &rgb);
1831 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1832 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1833 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1834 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1836 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1840 if(str->getKind()==strDCT)
1841 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1843 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1846 if(maskbitmap) free(maskbitmap);
1849 gfxcolor_t*pic=new gfxcolor_t[width*height];
1850 gfxcolor_t pal[256];
1851 int n = 1 << colorMap->getBits();
1853 for(t=0;t<256;t++) {
1855 colorMap->getRGB(pixBuf, &rgb);
1857 {/*if(maskColors && *maskColors==t) {
1858 msg("<notice> Color %d is transparent", t);
1859 if (imgData->maskColors) {
1861 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1862 if (pix[i] < imgData->maskColors[2*i] ||
1863 pix[i] > imgData->maskColors[2*i+1]) {
1878 pal[t].r = (unsigned char)(colToByte(rgb.r));
1879 pal[t].g = (unsigned char)(colToByte(rgb.g));
1880 pal[t].b = (unsigned char)(colToByte(rgb.b));
1881 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1884 for (y = 0; y < height; ++y) {
1885 for (x = 0; x < width; ++x) {
1886 imgStr->getPixel(pixBuf);
1887 pic[width*y+x] = pal[pixBuf[0]];
1889 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1893 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1897 if(maskbitmap) free(maskbitmap);
1902 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1903 int width, int height, GBool invert,
1906 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1907 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1908 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1911 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1912 int width, int height, GfxImageColorMap *colorMap,
1913 int *maskColors, GBool inlineImg)
1915 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1916 colorMap?"colorMap":"no colorMap",
1917 maskColors?"maskColors":"no maskColors",
1919 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1920 colorMap?"colorMap":"no colorMap",
1921 maskColors?"maskColors":"no maskColors",
1924 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1925 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1926 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1929 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1930 int width, int height,
1931 GfxImageColorMap *colorMap,
1932 Stream *maskStr, int maskWidth, int maskHeight,
1935 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1936 colorMap?"colorMap":"no colorMap",
1937 maskWidth, maskHeight);
1938 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1939 colorMap?"colorMap":"no colorMap",
1940 maskWidth, maskHeight);
1942 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1943 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1944 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1947 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1948 int width, int height,
1949 GfxImageColorMap *colorMap,
1951 int maskWidth, int maskHeight,
1952 GfxImageColorMap *maskColorMap)
1954 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1955 colorMap?"colorMap":"no colorMap",
1956 maskWidth, maskHeight);
1957 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1958 colorMap?"colorMap":"no colorMap",
1959 maskWidth, maskHeight);
1961 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1962 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1963 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1966 void GFXOutputDev::stroke(GfxState *state)
1970 GfxPath * path = state->getPath();
1971 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1972 strokeGfxline(state, line, 0);
1976 void GFXOutputDev::fill(GfxState *state)
1978 gfxcolor_t col = getFillColor(state);
1979 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1981 GfxPath * path = state->getPath();
1982 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1983 fillGfxLine(state, line);
1987 void GFXOutputDev::eoFill(GfxState *state)
1989 gfxcolor_t col = getFillColor(state);
1990 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1992 GfxPath * path = state->getPath();
1993 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1994 fillGfxLine(state, line);
1999 static const char* dirseparator()
2008 void addGlobalFont(const char*filename)
2011 memset(&f, 0, sizeof(fontfile_t));
2012 f.filename = filename;
2013 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2014 msg("<notice> Adding font \"%s\".", filename);
2015 fonts[fontnum++] = f;
2017 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2021 void addGlobalLanguageDir(const char*dir)
2023 msg("<notice> Adding %s to language pack directories", dir);
2027 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2028 strcpy(config_file, dir);
2029 strcat(config_file, dirseparator());
2030 strcat(config_file, "add-to-xpdfrc");
2032 fi = fopen(config_file, "rb");
2034 msg("<error> Could not open %s", config_file);
2037 globalParams->parseFile(new GString(config_file), fi);
2041 void addGlobalFontDir(const char*dirname)
2043 #ifdef HAVE_DIRENT_H
2044 msg("<notice> Adding %s to font directories", dirname);
2045 lastfontdir = strdup(dirname);
2046 DIR*dir = opendir(dirname);
2048 msg("<warning> Couldn't open directory %s\n", dirname);
2053 ent = readdir (dir);
2057 char*name = ent->d_name;
2063 if(!strncasecmp(&name[l-4], ".pfa", 4))
2065 if(!strncasecmp(&name[l-4], ".pfb", 4))
2067 if(!strncasecmp(&name[l-4], ".ttf", 4))
2070 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2071 strcpy(fontname, dirname);
2072 strcat(fontname, dirseparator());
2073 strcat(fontname, name);
2074 addGlobalFont(fontname);
2079 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2083 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2089 this->pagebuflen = 1024;
2090 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2091 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2093 while(pdfpage >= this->pagebuflen)
2095 int oldlen = this->pagebuflen;
2096 this->pagebuflen+=1024;
2097 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2098 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2101 this->pages[pdfpage] = outputpage;
2102 if(pdfpage>this->pagepos)
2103 this->pagepos = pdfpage;
2109 double width,height;
2112 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2114 double xMin, yMin, xMax, yMax, x, y;
2115 double tx, ty, w, h;
2116 // transform the bbox
2117 state->transform(bbox[0], bbox[1], &x, &y);
2120 state->transform(bbox[0], bbox[3], &x, &y);
2123 } else if (x > xMax) {
2128 } else if (y > yMax) {
2131 state->transform(bbox[2], bbox[1], &x, &y);
2134 } else if (x > xMax) {
2139 } else if (y > yMax) {
2142 state->transform(bbox[2], bbox[3], &x, &y);
2145 } else if (x > xMax) {
2150 } else if (y > yMax) {
2153 tx = (int)floor(xMin);
2156 } else if (tx > width) {
2159 ty = (int)floor(yMin);
2162 } else if (ty > height) {
2165 w = (int)ceil(xMax) - tx + 1;
2166 if (tx + w > width) {
2172 h = (int)ceil(yMax) - ty + 1;
2173 if (ty + h > height) {
2187 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2188 GfxColorSpace *blendingColorSpace,
2189 GBool isolated, GBool knockout,
2192 const char*colormodename = "";
2194 if(blendingColorSpace) {
2195 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2197 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);
2198 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);
2200 states[statepos].createsoftmask |= forSoftMask;
2201 states[statepos].transparencygroup = !forSoftMask;
2202 states[statepos].isolated = isolated;
2204 states[statepos].olddevice = this->device;
2205 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2207 gfxdevice_record_init(this->device);
2209 /*if(!forSoftMask) { ////???
2210 state->setFillOpacity(0.0);
2215 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2218 dbg("endTransparencyGroup");
2219 msg("<verbose> endTransparencyGroup");
2221 gfxdevice_t*r = this->device;
2223 this->device = states[statepos].olddevice;
2225 if(states[statepos].createsoftmask) {
2226 states[statepos-1].softmaskrecording = r->finish(r);
2228 states[statepos-1].grouprecording = r->finish(r);
2231 states[statepos].createsoftmask = 0;
2232 states[statepos].transparencygroup = 0;
2236 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2238 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2239 "colordodge","colorburn","hardlight","softlight","difference",
2240 "exclusion","hue","saturation","color","luminosity"};
2242 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2243 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2245 if(state->getBlendMode() == gfxBlendNormal)
2246 infofeature("transparency groups");
2249 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2250 warnfeature(buffer, 0);
2253 gfxresult_t*grouprecording = states[statepos].grouprecording;
2255 if(state->getBlendMode() == gfxBlendNormal) {
2257 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2258 gfxresult_record_replay(grouprecording, &ops);
2261 grouprecording->destroy(grouprecording);
2263 states[statepos].grouprecording = 0;
2266 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2268 /* alpha = 1: retrieve mask values from alpha layer
2269 alpha = 0: retrieve mask values from luminance */
2270 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2271 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2272 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2273 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2275 infofeature("soft masks");
2277 warnfeature("soft masks from alpha channel",0);
2279 states[statepos].olddevice = this->device;
2280 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2281 gfxdevice_record_init(this->device);
2283 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2285 states[statepos].softmask = 1;
2286 states[statepos].softmask_alpha = alpha;
2289 static inline Guchar div255(int x) {
2290 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2293 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2295 if(c < min) c = min;
2296 if(c > max) c = max;
2300 void GFXOutputDev::clearSoftMask(GfxState *state)
2302 if(!states[statepos].softmask)
2304 states[statepos].softmask = 0;
2305 dbg("clearSoftMask statepos=%d", statepos);
2306 msg("<verbose> clearSoftMask");
2308 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2309 msg("<error> Error in softmask/tgroup ordering");
2313 gfxresult_t*mask = states[statepos].softmaskrecording;
2314 gfxresult_t*below = this->device->finish(this->device);
2315 this->device = states[statepos].olddevice;
2317 /* get outline of all objects below the soft mask */
2318 gfxdevice_t uniondev;
2319 gfxdevice_union_init(&uniondev, 0);
2320 gfxresult_record_replay(below, &uniondev);
2321 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2322 uniondev.finish(&uniondev);
2324 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2326 this->device->startclip(this->device, belowoutline);
2327 gfxresult_record_replay(below, this->device);
2328 gfxresult_record_replay(mask, this->device);
2329 this->device->endclip(this->device);
2330 gfxline_free(belowoutline);
2333 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2334 if(width<=0 || height<=0)
2337 gfxdevice_t belowrender;
2338 gfxdevice_render_init(&belowrender);
2339 if(states[statepos+1].isolated) {
2340 belowrender.setparameter(&belowrender, "fillwhite", "1");
2342 belowrender.setparameter(&belowrender, "antialize", "2");
2343 belowrender.startpage(&belowrender, width, height);
2344 gfxresult_record_replay(below, &belowrender);
2345 belowrender.endpage(&belowrender);
2346 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2347 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2348 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2350 gfxdevice_t maskrender;
2351 gfxdevice_render_init(&maskrender);
2352 maskrender.startpage(&maskrender, width, height);
2353 gfxresult_record_replay(mask, &maskrender);
2354 maskrender.endpage(&maskrender);
2355 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2356 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2358 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2359 msg("<fatal> Internal error in mask drawing");
2364 for(y=0;y<height;y++) {
2365 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2366 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2367 for(x=0;x<width;x++) {
2369 if(states[statepos].softmask_alpha) {
2372 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2375 l2->a = div255(alpha*l2->a);
2377 /* DON'T premultiply alpha- this is done by fillbitmap,
2378 depending on the output device */
2379 //l2->r = div255(alpha*l2->r);
2380 //l2->g = div255(alpha*l2->g);
2381 //l2->b = div255(alpha*l2->b);
2387 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2390 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2391 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2393 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2395 mask->destroy(mask);
2396 below->destroy(below);
2397 maskresult->destroy(maskresult);
2398 belowresult->destroy(belowresult);
2399 states[statepos].softmaskrecording = 0;
2404 // public: ~MemCheck()
2406 // delete globalParams;globalParams=0;
2407 // Object::memCheck(stderr);
2408 // gMemReport(stderr);