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);
1100 gfxcolor_t white = {255,255,255,255};
1101 gfxcolor_t black = {255,0,0,0};
1103 gfxline_t clippath[5];
1105 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1106 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1107 Use CropBox, not MediaBox, as page size
1114 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1115 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1117 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1118 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1120 this->clipmovex = -(int)x1;
1121 this->clipmovey = -(int)y1;
1123 /* apply user clip box */
1124 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1125 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1126 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1127 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1128 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1129 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1132 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1134 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);
1136 msg("<verbose> page is rotated %d degrees\n", rot);
1138 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1139 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1140 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1141 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1142 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1143 device->startclip(device, clippath); outer_clip_box = 1;
1144 if(!config_transparent) {
1145 device->fill(device, clippath, &white);
1150 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1152 double x1, y1, x2, y2, w;
1153 gfxline_t points[5];
1156 msg("<debug> drawlink\n");
1158 link->getRect(&x1, &y1, &x2, &y2);
1159 cvtUserToDev(x1, y1, &x, &y);
1160 points[0].type = gfx_moveTo;
1161 points[0].x = points[4].x = x + user_movex + clipmovex;
1162 points[0].y = points[4].y = y + user_movey + clipmovey;
1163 points[0].next = &points[1];
1164 cvtUserToDev(x2, y1, &x, &y);
1165 points[1].type = gfx_lineTo;
1166 points[1].x = x + user_movex + clipmovex;
1167 points[1].y = y + user_movey + clipmovey;
1168 points[1].next = &points[2];
1169 cvtUserToDev(x2, y2, &x, &y);
1170 points[2].type = gfx_lineTo;
1171 points[2].x = x + user_movex + clipmovex;
1172 points[2].y = y + user_movey + clipmovey;
1173 points[2].next = &points[3];
1174 cvtUserToDev(x1, y2, &x, &y);
1175 points[3].type = gfx_lineTo;
1176 points[3].x = x + user_movex + clipmovex;
1177 points[3].y = y + user_movey + clipmovey;
1178 points[3].next = &points[4];
1179 cvtUserToDev(x1, y1, &x, &y);
1180 points[4].type = gfx_lineTo;
1181 points[4].x = x + user_movex + clipmovex;
1182 points[4].y = y + user_movey + clipmovey;
1185 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1186 points[0].x, points[0].y,
1187 points[1].x, points[1].y,
1188 points[2].x, points[2].y,
1189 points[3].x, points[3].y);
1191 LinkAction*action=link->getAction();
1194 const char*type = "-?-";
1197 msg("<trace> drawlink action=%d\n", action->getKind());
1198 switch(action->getKind())
1202 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1203 LinkDest *dest=NULL;
1204 if (ha->getDest()==NULL)
1205 dest=catalog->findDest(ha->getNamedDest());
1206 else dest=ha->getDest();
1208 if (dest->isPageRef()){
1209 Ref pageref=dest->getPageRef();
1210 page=catalog->findPage(pageref.num,pageref.gen);
1212 else page=dest->getPageNum();
1213 sprintf(buf, "%d", page);
1220 LinkGoToR*l = (LinkGoToR*)action;
1221 GString*g = l->getFileName();
1223 s = strdup(g->getCString());
1225 /* if the GoToR link has no filename, then
1226 try to find a refernce in the *local*
1228 GString*g = l->getNamedDest();
1230 s = strdup(g->getCString());
1236 LinkNamed*l = (LinkNamed*)action;
1237 GString*name = l->getName();
1239 s = strdup(name->lowerCase()->getCString());
1240 named = name->getCString();
1243 if(strstr(s, "next") || strstr(s, "forward"))
1245 page = currentpage + 1;
1247 else if(strstr(s, "prev") || strstr(s, "back"))
1249 page = currentpage - 1;
1251 else if(strstr(s, "last") || strstr(s, "end"))
1253 if(pages && pagepos>0)
1254 page = pages[pagepos-1];
1256 else if(strstr(s, "first") || strstr(s, "top"))
1264 case actionLaunch: {
1266 LinkLaunch*l = (LinkLaunch*)action;
1267 GString * str = new GString(l->getFileName());
1268 GString * params = l->getParams();
1270 str->append(params);
1271 s = strdup(str->getCString());
1278 LinkURI*l = (LinkURI*)action;
1279 GString*g = l->getURI();
1281 url = g->getCString();
1286 case actionUnknown: {
1288 LinkUnknown*l = (LinkUnknown*)action;
1293 msg("<error> Unknown link type!\n");
1298 if(!s) s = strdup("-?-");
1300 msg("<trace> drawlink s=%s\n", s);
1302 if(!linkinfo && (page || s))
1304 msg("<notice> File contains links");
1312 for(t=1;t<=pagepos;t++) {
1313 if(pages[t]==page) {
1322 sprintf(buf, "page%d", lpage);
1323 device->drawlink(device, points, buf);
1327 device->drawlink(device, points, s);
1330 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1334 void GFXOutputDev::saveState(GfxState *state) {
1335 dbg("saveState");dbgindent+=2;
1337 msg("<trace> saveState\n");
1340 msg("<error> Too many nested states in pdf.");
1344 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1345 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1346 states[statepos].clipping = 0;
1349 void GFXOutputDev::restoreState(GfxState *state) {
1350 dbgindent-=2; dbg("restoreState");
1353 msg("<error> Invalid restoreState");
1356 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1357 states[statepos].clipping?" (end clipping)":"");
1358 if(states[statepos].softmask) {
1359 clearSoftMask(state);
1362 while(states[statepos].clipping) {
1363 device->endclip(device);
1364 states[statepos].clipping--;
1369 void GFXOutputDev::updateLineWidth(GfxState *state)
1371 double width = state->getTransformedLineWidth();
1372 //swfoutput_setlinewidth(&device, width);
1375 void GFXOutputDev::updateLineCap(GfxState *state)
1377 int c = state->getLineCap();
1380 void GFXOutputDev::updateLineJoin(GfxState *state)
1382 int j = state->getLineJoin();
1385 void GFXOutputDev::updateFillColor(GfxState *state)
1388 double opaq = state->getFillOpacity();
1389 state->getFillRGB(&rgb);
1391 void GFXOutputDev::updateFillOpacity(GfxState *state)
1394 double opaq = state->getFillOpacity();
1395 state->getFillRGB(&rgb);
1396 dbg("update fillopaq %f", opaq);
1398 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1400 double opaq = state->getFillOpacity();
1401 dbg("update strokeopaq %f", opaq);
1403 void GFXOutputDev::updateFillOverprint(GfxState *state)
1405 double opaq = state->getFillOverprint();
1406 dbg("update filloverprint %f", opaq);
1408 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1410 double opaq = state->getStrokeOverprint();
1411 dbg("update strokeoverprint %f", opaq);
1413 void GFXOutputDev::updateTransfer(GfxState *state)
1415 dbg("update transfer");
1419 void GFXOutputDev::updateStrokeColor(GfxState *state)
1422 double opaq = state->getStrokeOpacity();
1423 state->getStrokeRGB(&rgb);
1427 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1429 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1430 memset(font, 0, sizeof(gfxfont_t));
1432 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1433 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1434 font->id = strdup(getFontID(xpdffont));
1437 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / 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, this->config_fontquality);
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);