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 */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
51 #include "OutputDev.h"
54 #include "CharCodeToUnicode.h"
55 #include "NameToUnicodeTable.h"
56 #include "GlobalParams.h"
57 #include "FoFiType1C.h"
58 #include "FoFiTrueType.h"
60 #include "GFXOutputDev.h"
62 // swftools header files
64 #include "../gfxdevice.h"
65 #include "../gfxtools.h"
66 #include "../gfxfont.h"
67 #include "../devices/record.h"
68 #include "../devices/ops.h"
69 #include "../devices/arts.h"
70 #include "../devices/render.h"
72 #include "../art/libart.h"
73 #include "../devices/artsutils.h"
80 typedef struct _fontfile
87 static fontfile_t fonts[2048];
88 static int fontnum = 0;
92 static char* lastfontdir = 0;
103 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
104 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
105 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
106 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
107 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
108 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
109 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
110 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
111 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
112 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
113 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
114 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
115 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
116 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
119 static int verbose = 0;
120 static int dbgindent = 0;
121 static void dbg(const char*format, ...)
128 va_start(arglist, format);
129 vsprintf(buf, format, arglist);
132 while(l && buf[l-1]=='\n') {
137 int indent = dbgindent;
147 typedef struct _feature
150 struct _feature*next;
152 feature_t*featurewarnings = 0;
154 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
156 feature_t*f = featurewarnings;
158 if(!strcmp(feature, f->string))
162 f = (feature_t*)malloc(sizeof(feature_t));
163 f->string = strdup(feature);
164 f->next = featurewarnings;
167 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
168 if(this->config_break_on_warning) {
169 msg("<fatal> Aborting conversion due to unsupported feature");
173 msg("<notice> File contains %s",feature);
176 void GFXOutputDev::warnfeature(const char*feature,char fully)
178 showfeature(feature,fully,1);
180 void GFXOutputDev::infofeature(const char*feature)
182 showfeature(feature,0,0);
185 GFXOutputState::GFXOutputState() {
187 this->createsoftmask = 0;
188 this->transparencygroup = 0;
190 this->grouprecording = 0;
194 GBool GFXOutputDev::interpretType3Chars()
199 typedef struct _drawnchar
217 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
218 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
223 free(chars);chars = 0;
230 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
234 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
237 chars[num_chars].x = x;
238 chars[num_chars].y = y;
239 chars[num_chars].color = color;
240 chars[num_chars].charid = charid;
244 char* writeOutStdFont(fontentry* f)
249 char* tmpFileName = mktmpname(namebuf1);
251 sprintf(namebuf2, "%s.afm", tmpFileName);
252 fi = fopen(namebuf2, "wb");
255 fwrite(f->afm, 1, f->afmlen, fi);
258 sprintf(namebuf2, "%s.pfb", tmpFileName);
259 fi = fopen(namebuf2, "wb");
262 fwrite(f->pfb, 1, f->pfblen, fi);
264 return strdup(namebuf2);
266 void unlinkfont(char* filename)
271 msg("<verbose> Removing temporary font file %s", filename);
274 if(!strncmp(&filename[l-4],".afm",4)) {
275 memcpy(&filename[l-4],".pfb",4); unlink(filename);
276 memcpy(&filename[l-4],".pfa",4); unlink(filename);
277 memcpy(&filename[l-4],".afm",4);
280 if(!strncmp(&filename[l-4],".pfa",4)) {
281 memcpy(&filename[l-4],".afm",4); unlink(filename);
282 memcpy(&filename[l-4],".pfa",4);
285 if(!strncmp(&filename[l-4],".pfb",4)) {
286 memcpy(&filename[l-4],".afm",4); unlink(filename);
287 memcpy(&filename[l-4],".pfb",4);
293 GFXGlobalParams::GFXGlobalParams()
296 //setupBaseFonts(char *dir); //not tested yet
298 GFXGlobalParams::~GFXGlobalParams()
300 msg("<verbose> Performing cleanups");
302 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
303 if(pdf2t1map[t].fullfilename) {
304 unlinkfont(pdf2t1map[t].fullfilename);
308 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
310 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
312 char*name = fontName->getCString();
313 /* see if it is a pdf standard font */
315 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
316 if(!strcmp(name, pdf2t1map[t].pdffont)) {
317 if(!pdf2t1map[t].fullfilename) {
318 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
319 if(!pdf2t1map[t].fullfilename) {
320 msg("<error> Couldn't save default font- is the Temp Directory writable?");
322 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
325 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
326 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
330 for(t=0;t<fontnum;t++) {
331 if(strstr(fonts[t].filename, name)) {
332 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
333 dfp->t1.fileName = new GString(fonts[t].filename);
337 return GlobalParams::getDisplayFont(fontName);
340 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
344 this->xref = doc->getXRef();
347 this->textmodeinfo = 0;
350 this->type3active = 0;
353 this->substitutepos = 0;
354 this->type3Warning = 0;
355 this->user_movex = 0;
356 this->user_movey = 0;
359 this->user_clipx1 = 0;
360 this->user_clipy1 = 0;
361 this->user_clipx2 = 0;
362 this->user_clipy2 = 0;
363 this->current_text_stroke = 0;
364 this->current_text_clip = 0;
365 this->outer_clip_box = 0;
367 this->pagebuflen = 0;
369 this->config_break_on_warning=0;
370 this->config_remapunicode=0;
371 this->config_transparent=0;
372 this->config_extrafontdata = 0;
373 this->config_fontquality = 10;
375 this->gfxfontlist = gfxfontlist_create();
377 memset(states, 0, sizeof(states));
380 void GFXOutputDev::setParameter(const char*key, const char*value)
382 if(!strcmp(key,"breakonwarning")) {
383 this->config_break_on_warning = atoi(value);
384 } else if(!strcmp(key,"remapunicode")) {
385 this->config_remapunicode = atoi(value);
386 } else if(!strcmp(key,"transparent")) {
387 this->config_transparent = atoi(value);
388 } else if(!strcmp(key,"extrafontdata")) {
389 this->config_extrafontdata = atoi(value);
390 } else if(!strcmp(key,"fontquality")) {
391 this->config_fontquality = atof(value);
392 if(this->config_fontquality<=1)
393 this->config_fontquality=1;
394 } else if(!strcmp(key,"help")) {
395 printf("\nPDF layer options:\n");
396 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
397 printf(" are not 100%% supported\n");
398 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
399 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
400 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
405 void GFXOutputDev::setDevice(gfxdevice_t*dev)
410 void GFXOutputDev::setMove(int x,int y)
412 this->user_movex = x;
413 this->user_movey = y;
416 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
418 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
419 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
421 this->user_clipx1 = x1;
422 this->user_clipy1 = y1;
423 this->user_clipx2 = x2;
424 this->user_clipy2 = y2;
427 static char*getFontName(GfxFont*font)
430 GString*gstr = font->getName();
431 char* fname = gstr==0?0:gstr->getCString();
435 sprintf(buf, "UFONT%d", r->num);
436 fontid = strdup(buf);
438 fontid = strdup(fname);
442 char* plus = strchr(fontid, '+');
443 if(plus && plus < &fontid[strlen(fontid)-1]) {
444 fontname = strdup(plus+1);
446 fontname = strdup(fontid);
452 static void dumpFontInfo(const char*loglevel, GfxFont*font);
453 static int lastdumps[1024];
454 static int lastdumppos = 0;
459 static void showFontError(GfxFont*font, int nr)
463 for(t=0;t<lastdumppos;t++)
464 if(lastdumps[t] == r->num)
468 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
469 lastdumps[lastdumppos++] = r->num;
471 msg("<warning> The following font caused problems:");
473 msg("<warning> The following font caused problems (substituting):");
475 msg("<warning> The following Type 3 Font will be rendered as graphics:");
476 dumpFontInfo("<warning>", font);
479 static void dumpFontInfo(const char*loglevel, GfxFont*font)
481 char* id = getFontID(font);
482 char* name = getFontName(font);
483 Ref* r=font->getID();
484 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
486 GString*gstr = font->getTag();
488 msg("%s| Tag: %s\n", loglevel, id);
490 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
492 GfxFontType type=font->getType();
494 case fontUnknownType:
495 msg("%s| Type: unknown\n",loglevel);
498 msg("%s| Type: 1\n",loglevel);
501 msg("%s| Type: 1C\n",loglevel);
504 msg("%s| Type: 3\n",loglevel);
507 msg("%s| Type: TrueType\n",loglevel);
510 msg("%s| Type: CIDType0\n",loglevel);
513 msg("%s| Type: CIDType0C\n",loglevel);
516 msg("%s| Type: CIDType2\n",loglevel);
521 GBool embedded = font->getEmbeddedFontID(&embRef);
523 if(font->getEmbeddedFontName()) {
524 embeddedName = font->getEmbeddedFontName()->getCString();
527 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
529 gstr = font->getExtFontFile();
531 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
533 // Get font descriptor flags.
534 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
535 if(font->isSerif()) msg("%s| is serif\n", loglevel);
536 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
537 if(font->isItalic()) msg("%s| is italic\n", loglevel);
538 if(font->isBold()) msg("%s| is bold\n", loglevel);
544 //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");}
545 //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");}
547 void dump_outline(gfxline_t*line)
550 if(line->type == gfx_moveTo) {
551 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
552 } else if(line->type == gfx_lineTo) {
553 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
554 } else if(line->type == gfx_splineTo) {
555 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
561 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
563 int num = path->getNumSubpaths();
566 double lastx=0,lasty=0,posx=0,posy=0;
569 msg("<warning> empty path");
573 gfxdrawer_target_gfxline(&draw);
575 for(t = 0; t < num; t++) {
576 GfxSubpath *subpath = path->getSubpath(t);
577 int subnum = subpath->getNumPoints();
578 double bx=0,by=0,cx=0,cy=0;
580 for(s=0;s<subnum;s++) {
583 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
588 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
589 draw.lineTo(&draw, lastx, lasty);
591 draw.moveTo(&draw, x,y);
596 } else if(subpath->getCurve(s) && cpos==0) {
600 } else if(subpath->getCurve(s) && cpos==1) {
608 draw.lineTo(&draw, x,y);
610 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
617 /* fix non-closed lines */
618 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
619 draw.lineTo(&draw, lastx, lasty);
621 gfxline_t*result = (gfxline_t*)draw.result(&draw);
623 gfxline_optimize(result);
628 GBool GFXOutputDev::useTilingPatternFill()
630 infofeature("tiled patterns");
634 GBool GFXOutputDev::useShadedFills()
636 infofeature("shaded fills");
640 GBool GFXOutputDev::useDrawForm()
642 infofeature("forms");
645 void GFXOutputDev::drawForm(Ref id)
647 msg("<error> drawForm not implemented");
649 GBool GFXOutputDev::needNonText()
653 void GFXOutputDev::endPage()
655 msg("<verbose> endPage");
657 device->endclip(device);
662 #define STROKE_FILL 1
663 #define STROKE_CLIP 2
664 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
666 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
667 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
668 double miterLimit = state->getMiterLimit();
669 double width = state->getTransformedLineWidth();
672 double opaq = state->getStrokeOpacity();
674 state->getFillRGB(&rgb);
676 state->getStrokeRGB(&rgb);
678 col.r = colToByte(rgb.r);
679 col.g = colToByte(rgb.g);
680 col.b = colToByte(rgb.b);
681 col.a = (unsigned char)(opaq*255);
683 gfx_capType capType = gfx_capRound;
684 if(lineCap == 0) capType = gfx_capButt;
685 else if(lineCap == 1) capType = gfx_capRound;
686 else if(lineCap == 2) capType = gfx_capSquare;
688 gfx_joinType joinType = gfx_joinRound;
689 if(lineJoin == 0) joinType = gfx_joinMiter;
690 else if(lineJoin == 1) joinType = gfx_joinRound;
691 else if(lineJoin == 2) joinType = gfx_joinBevel;
694 double dashphase = 0;
696 state->getLineDash(&ldash, &dashnum, &dashphase);
700 if(dashnum && ldash) {
701 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
703 msg("<trace> %d dashes", dashnum);
704 msg("<trace> | phase: %f", dashphase);
705 for(t=0;t<dashnum;t++) {
706 dash[t] = (float)ldash[t];
707 msg("<trace> | d%-3d: %f", t, ldash[t]);
710 if(getLogLevel() >= LOGLEVEL_TRACE) {
714 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
717 msg("<trace> After dashing:");
720 if(getLogLevel() >= LOGLEVEL_TRACE) {
721 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
723 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
724 lineCap==0?"butt": (lineJoin==1?"round":"square"),
726 col.r,col.g,col.b,col.a
731 if(flags&STROKE_FILL) {
732 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
733 gfxline_t*gfxline = SVPtogfxline(svp);
734 if(getLogLevel() >= LOGLEVEL_TRACE) {
735 dump_outline(gfxline);
738 msg("<warning> Empty polygon (resulting from stroked line)");
740 if(flags&STROKE_CLIP) {
741 device->startclip(device, gfxline);
742 states[statepos].clipping++;
744 device->fill(device, gfxline, &col);
749 if(flags&STROKE_CLIP)
750 msg("<error> Stroke&clip not supported at the same time");
751 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
758 gfxcolor_t getFillColor(GfxState * state)
761 double opaq = state->getFillOpacity();
762 state->getFillRGB(&rgb);
764 col.r = colToByte(rgb.r);
765 col.g = colToByte(rgb.g);
766 col.b = colToByte(rgb.b);
767 col.a = (unsigned char)(opaq*255);
771 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
773 gfxcolor_t col = getFillColor(state);
775 if(getLogLevel() >= LOGLEVEL_TRACE) {
776 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
779 device->fill(device, line, &col);
782 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
784 if(getLogLevel() >= LOGLEVEL_TRACE) {
785 msg("<trace> clip\n");
789 device->startclip(device, line);
790 states[statepos].clipping++;
793 void GFXOutputDev::clip(GfxState *state)
795 GfxPath * path = state->getPath();
796 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
797 clipToGfxLine(state, line);
801 void GFXOutputDev::eoClip(GfxState *state)
803 GfxPath * path = state->getPath();
804 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
806 if(getLogLevel() >= LOGLEVEL_TRACE) {
807 msg("<trace> eoclip\n");
811 device->startclip(device, line);
812 states[statepos].clipping++;
815 void GFXOutputDev::clipToStrokePath(GfxState *state)
817 GfxPath * path = state->getPath();
818 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
820 if(getLogLevel() >= LOGLEVEL_TRACE) {
821 msg("<trace> cliptostrokepath\n");
825 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
829 void GFXOutputDev::finish()
833 device->endclip(device);
839 GFXOutputDev::~GFXOutputDev()
844 free(this->pages); this->pages = 0;
847 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
849 GBool GFXOutputDev::upsideDown()
853 GBool GFXOutputDev::useDrawChar()
858 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
859 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
861 static char tmp_printstr[4096];
862 char* makeStringPrintable(char*str)
864 int len = strlen(str);
879 tmp_printstr[len++] = '.';
880 tmp_printstr[len++] = '.';
881 tmp_printstr[len++] = '.';
883 tmp_printstr[len] = 0;
886 #define INTERNAL_FONT_SIZE 1024.0
887 void GFXOutputDev::updateFontMatrix(GfxState*state)
889 double* ctm = state->getCTM();
890 double fontSize = state->getFontSize();
891 double*textMat = state->getTextMat();
893 /* taking the absolute value of horizScaling seems to be required for
894 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
895 double hscale = fabs(state->getHorizScaling());
897 // from xpdf-3.02/SplashOutputDev:updateFont
898 double mm11 = textMat[0] * fontSize * hscale;
899 double mm12 = textMat[1] * fontSize * hscale;
900 double mm21 = textMat[2] * fontSize;
901 double mm22 = textMat[3] * fontSize;
903 // multiply with ctm, like state->getFontTransMat() does
904 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
905 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
906 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
907 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
908 this->current_font_matrix.tx = 0;
909 this->current_font_matrix.ty = 0;
912 void GFXOutputDev::beginString(GfxState *state, GString *s)
914 int render = state->getRender();
915 if(current_text_stroke) {
916 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
919 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
922 static gfxline_t* mkEmptyGfxShape(double x, double y)
924 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
925 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
929 static char isValidUnicode(int c)
931 if(c>=32 && c<0x2fffe)
936 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
937 double dx, double dy,
938 double originX, double originY,
939 CharCode charid, int nBytes, Unicode *_u, int uLen)
941 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
942 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
946 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
948 int render = state->getRender();
949 gfxcolor_t col = getFillColor(state);
951 // check for invisible text -- this is used by Acrobat Capture
952 if (render == RENDER_INVISIBLE) {
954 if(!config_extrafontdata)
958 GfxFont*font = state->getFont();
960 if(font->getType() == fontType3) {
961 /* type 3 chars are passed as graphics */
962 msg("<debug> type3 char at %f/%f", x, y);
966 Unicode u = uLen?(_u[0]):0;
967 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);
969 gfxmatrix_t m = this->current_font_matrix;
970 state->transform(x, y, &m.tx, &m.ty);
971 m.tx += user_movex + clipmovex;
972 m.ty += user_movey + clipmovey;
974 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
975 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
977 msg("<debug> Drawing glyph %d as shape", charid);
979 msg("<notice> Some texts will be rendered as shape");
982 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
983 gfxline_t*tglyph = gfxline_clone(glyph);
984 gfxline_transform(tglyph, &m);
985 if((render&3) != RENDER_INVISIBLE) {
986 gfxline_t*add = gfxline_clone(tglyph);
987 current_text_stroke = gfxline_append(current_text_stroke, add);
989 if(render&RENDER_CLIP) {
990 gfxline_t*add = gfxline_clone(tglyph);
991 current_text_clip = gfxline_append(current_text_clip, add);
992 if(!current_text_clip) {
993 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
996 gfxline_free(tglyph);
1000 void GFXOutputDev::endString(GfxState *state)
1002 int render = state->getRender();
1003 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1005 if(current_text_stroke) {
1006 /* fillstroke and stroke text rendering objects we can process right
1007 now (as there may be texts of other rendering modes in this
1008 text object)- clipping objects have to wait until endTextObject,
1010 device->setparameter(device, "mark","TXT");
1011 if((render&3) == RENDER_FILL) {
1012 fillGfxLine(state, current_text_stroke);
1013 gfxline_free(current_text_stroke);
1014 current_text_stroke = 0;
1015 } else if((render&3) == RENDER_FILLSTROKE) {
1016 fillGfxLine(state, current_text_stroke);
1017 strokeGfxline(state, current_text_stroke,0);
1018 gfxline_free(current_text_stroke);
1019 current_text_stroke = 0;
1020 } else if((render&3) == RENDER_STROKE) {
1021 strokeGfxline(state, current_text_stroke,0);
1022 gfxline_free(current_text_stroke);
1023 current_text_stroke = 0;
1025 device->setparameter(device, "mark","");
1029 void GFXOutputDev::endTextObject(GfxState *state)
1031 int render = state->getRender();
1032 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1034 if(current_text_clip) {
1035 device->setparameter(device, "mark","TXT");
1036 clipToGfxLine(state, current_text_clip);
1037 device->setparameter(device, "mark","");
1038 gfxline_free(current_text_clip);
1039 current_text_clip = 0;
1043 /* the logic seems to be as following:
1044 first, beginType3Char is called, with the charcode and the coordinates.
1045 if this function returns true, it already knew about the char and has now drawn it.
1046 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1047 called with some parameters.
1048 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1049 at the position first passed to beginType3Char). the char ends with endType3Char.
1051 The drawing operations between beginType3Char and endType3Char are somewhat different to
1052 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1053 color determines the color of a font)
1056 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1058 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1061 if(config_extrafontdata && current_fontinfo) {
1063 gfxmatrix_t m = this->current_font_matrix;
1064 state->transform(0, 0, &m.tx, &m.ty);
1065 m.m00*=INTERNAL_FONT_SIZE;
1066 m.m01*=INTERNAL_FONT_SIZE;
1067 m.m10*=INTERNAL_FONT_SIZE;
1068 m.m11*=INTERNAL_FONT_SIZE;
1069 m.tx += user_movex + clipmovex;
1070 m.ty += user_movey + clipmovey;
1072 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1073 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1076 gfxcolor_t col={0,0,0,0};
1077 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1078 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1082 /* the character itself is going to be passed using the draw functions */
1083 return gFalse; /* gTrue= is_in_cache? */
1086 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1088 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1091 void GFXOutputDev::endType3Char(GfxState *state)
1094 msg("<debug> endType3Char");
1097 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1099 this->currentpage = pageNum;
1101 int rot = doc->getPageRotate(1);
1102 gfxcolor_t white = {255,255,255,255};
1103 gfxcolor_t black = {255,0,0,0};
1105 gfxline_t clippath[5];
1107 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1108 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1109 Use CropBox, not MediaBox, as page size
1116 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1117 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1119 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1120 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1122 this->clipmovex = -(int)x1;
1123 this->clipmovey = -(int)y1;
1125 /* apply user clip box */
1126 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1127 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1128 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1129 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1130 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1131 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1134 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1136 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);
1138 msg("<verbose> page is rotated %d degrees\n", rot);
1140 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1141 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1142 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1143 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1144 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1145 device->startclip(device, clippath); outer_clip_box = 1;
1146 if(!config_transparent) {
1147 device->fill(device, clippath, &white);
1152 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1154 double x1, y1, x2, y2;
1155 gfxline_t points[5];
1158 msg("<debug> drawlink\n");
1160 link->getRect(&x1, &y1, &x2, &y2);
1161 cvtUserToDev(x1, y1, &x, &y);
1162 points[0].type = gfx_moveTo;
1163 points[0].x = points[4].x = x + user_movex + clipmovex;
1164 points[0].y = points[4].y = y + user_movey + clipmovey;
1165 points[0].next = &points[1];
1166 cvtUserToDev(x2, y1, &x, &y);
1167 points[1].type = gfx_lineTo;
1168 points[1].x = x + user_movex + clipmovex;
1169 points[1].y = y + user_movey + clipmovey;
1170 points[1].next = &points[2];
1171 cvtUserToDev(x2, y2, &x, &y);
1172 points[2].type = gfx_lineTo;
1173 points[2].x = x + user_movex + clipmovex;
1174 points[2].y = y + user_movey + clipmovey;
1175 points[2].next = &points[3];
1176 cvtUserToDev(x1, y2, &x, &y);
1177 points[3].type = gfx_lineTo;
1178 points[3].x = x + user_movex + clipmovex;
1179 points[3].y = y + user_movey + clipmovey;
1180 points[3].next = &points[4];
1181 cvtUserToDev(x1, y1, &x, &y);
1182 points[4].type = gfx_lineTo;
1183 points[4].x = x + user_movex + clipmovex;
1184 points[4].y = y + user_movey + clipmovey;
1187 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1188 points[0].x, points[0].y,
1189 points[1].x, points[1].y,
1190 points[2].x, points[2].y,
1191 points[3].x, points[3].y);
1193 LinkAction*action=link->getAction();
1196 const char*type = "-?-";
1199 msg("<trace> drawlink action=%d\n", action->getKind());
1200 switch(action->getKind())
1204 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1205 LinkDest *dest=NULL;
1206 if (ha->getDest()==NULL)
1207 dest=catalog->findDest(ha->getNamedDest());
1208 else dest=ha->getDest();
1210 if (dest->isPageRef()){
1211 Ref pageref=dest->getPageRef();
1212 page=catalog->findPage(pageref.num,pageref.gen);
1214 else page=dest->getPageNum();
1215 sprintf(buf, "%d", page);
1222 LinkGoToR*l = (LinkGoToR*)action;
1223 GString*g = l->getFileName();
1225 s = strdup(g->getCString());
1227 /* if the GoToR link has no filename, then
1228 try to find a refernce in the *local*
1230 GString*g = l->getNamedDest();
1232 s = strdup(g->getCString());
1238 LinkNamed*l = (LinkNamed*)action;
1239 GString*name = l->getName();
1241 s = strdup(name->lowerCase()->getCString());
1242 named = name->getCString();
1245 if(strstr(s, "next") || strstr(s, "forward"))
1247 page = currentpage + 1;
1249 else if(strstr(s, "prev") || strstr(s, "back"))
1251 page = currentpage - 1;
1253 else if(strstr(s, "last") || strstr(s, "end"))
1255 if(pages && pagepos>0)
1256 page = pages[pagepos-1];
1258 else if(strstr(s, "first") || strstr(s, "top"))
1266 case actionLaunch: {
1268 LinkLaunch*l = (LinkLaunch*)action;
1269 GString * str = new GString(l->getFileName());
1270 GString * params = l->getParams();
1272 str->append(params);
1273 s = strdup(str->getCString());
1280 LinkURI*l = (LinkURI*)action;
1281 GString*g = l->getURI();
1283 url = g->getCString();
1288 case actionUnknown: {
1290 LinkUnknown*l = (LinkUnknown*)action;
1295 msg("<error> Unknown link type!\n");
1300 if(!s) s = strdup("-?-");
1302 msg("<trace> drawlink s=%s\n", s);
1304 if(!linkinfo && (page || s))
1306 msg("<notice> File contains links");
1314 for(t=1;t<=pagepos;t++) {
1315 if(pages[t]==page) {
1324 sprintf(buf, "page%d", lpage);
1325 device->drawlink(device, points, buf);
1329 device->drawlink(device, points, s);
1332 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1336 void GFXOutputDev::saveState(GfxState *state) {
1337 dbg("saveState");dbgindent+=2;
1339 msg("<trace> saveState\n");
1342 msg("<error> Too many nested states in pdf.");
1346 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1347 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1348 states[statepos].clipping = 0;
1351 void GFXOutputDev::restoreState(GfxState *state) {
1352 dbgindent-=2; dbg("restoreState");
1355 msg("<error> Invalid restoreState");
1358 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1359 states[statepos].clipping?" (end clipping)":"");
1360 if(states[statepos].softmask) {
1361 clearSoftMask(state);
1364 while(states[statepos].clipping) {
1365 device->endclip(device);
1366 states[statepos].clipping--;
1371 void GFXOutputDev::updateLineWidth(GfxState *state)
1373 double width = state->getTransformedLineWidth();
1374 //swfoutput_setlinewidth(&device, width);
1377 void GFXOutputDev::updateLineCap(GfxState *state)
1379 int c = state->getLineCap();
1382 void GFXOutputDev::updateLineJoin(GfxState *state)
1384 int j = state->getLineJoin();
1387 void GFXOutputDev::updateFillColor(GfxState *state)
1390 double opaq = state->getFillOpacity();
1391 state->getFillRGB(&rgb);
1393 void GFXOutputDev::updateFillOpacity(GfxState *state)
1396 double opaq = state->getFillOpacity();
1397 state->getFillRGB(&rgb);
1398 dbg("update fillopaq %f", opaq);
1400 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1402 double opaq = state->getFillOpacity();
1403 dbg("update strokeopaq %f", opaq);
1405 void GFXOutputDev::updateFillOverprint(GfxState *state)
1407 double opaq = state->getFillOverprint();
1408 dbg("update filloverprint %f", opaq);
1410 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1412 double opaq = state->getStrokeOverprint();
1413 dbg("update strokeoverprint %f", opaq);
1415 void GFXOutputDev::updateTransfer(GfxState *state)
1417 dbg("update transfer");
1421 void GFXOutputDev::updateStrokeColor(GfxState *state)
1424 double opaq = state->getStrokeOpacity();
1425 state->getStrokeRGB(&rgb);
1429 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1431 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1432 memset(font, 0, sizeof(gfxfont_t));
1434 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1435 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1436 font->id = strdup(getFontID(xpdffont));
1439 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1441 //printf("%d glyphs\n", font->num_glyphs);
1442 font->num_glyphs = 0;
1443 for(t=0;t<src->num_glyphs;t++) {
1444 if(src->glyphs[t]) {
1445 SplashPath*path = src->glyphs[t]->path;
1446 int len = path?path->getLength():0;
1447 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1448 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1449 src->glyphs[t]->glyphid = font->num_glyphs;
1450 glyph->unicode = src->glyphs[t]->unicode;
1451 if(glyph->unicode >= font->max_unicode)
1452 font->max_unicode = glyph->unicode+1;
1454 gfxdrawer_target_gfxline(&drawer);
1458 for(s=0;s<len;s++) {
1461 path->getPoint(s, &x, &y, &f);
1464 if(f&splashPathFirst) {
1465 drawer.moveTo(&drawer, x*scale, y*scale);
1467 if(f&splashPathCurve) {
1469 path->getPoint(++s, &x2, &y2, &f);
1470 if(f&splashPathCurve) {
1472 path->getPoint(++s, &x3, &y3, &f);
1473 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1475 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1478 drawer.lineTo(&drawer, x*scale, y*scale);
1480 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1481 // (f&splashPathFirst)?"first":"",
1482 // (f&splashPathLast)?"last":"");
1484 glyph->line = (gfxline_t*)drawer.result(&drawer);
1485 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1489 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1490 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1491 for(t=0;t<font->num_glyphs;t++) {
1492 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1493 font->unicode2glyph[font->glyphs[t].unicode] = t;
1497 msg("<trace> %d glyphs.", t, font->num_glyphs);
1501 void GFXOutputDev::updateFont(GfxState *state)
1503 GfxFont* gfxFont = state->getFont();
1507 char*id = getFontID(gfxFont);
1508 msg("<verbose> Updating font to %s", id);
1509 if(gfxFont->getType() == fontType3) {
1510 infofeature("Type3 fonts");
1511 if(!config_extrafontdata) {
1516 msg("<error> Internal Error: FontID is null");
1520 this->current_fontinfo = this->info->getFont(id);
1521 if(!this->current_fontinfo) {
1522 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1525 if(!this->current_fontinfo->seen) {
1526 dumpFontInfo("<verbose>", gfxFont);
1529 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1531 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1532 font->id = strdup(id);
1533 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1534 device->addfont(device, font);
1536 current_gfxfont = font;
1539 updateFontMatrix(state);
1542 #define SQR(x) ((x)*(x))
1544 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1546 if((newwidth<2 || newheight<2) ||
1547 (width<=newwidth || height<=newheight))
1549 unsigned char*newdata;
1551 newdata= (unsigned char*)malloc(newwidth*newheight);
1552 double fx = (double)(width)/newwidth;
1553 double fy = (double)(height)/newheight;
1555 int blocksize = (int)(8192/(fx*fy));
1556 int r = 8192*256/palettesize;
1557 for(x=0;x<newwidth;x++) {
1558 double ex = px + fx;
1559 int fromx = (int)px;
1561 int xweight1 = (int)(((fromx+1)-px)*256);
1562 int xweight2 = (int)((ex-tox)*256);
1564 for(y=0;y<newheight;y++) {
1565 double ey = py + fy;
1566 int fromy = (int)py;
1568 int yweight1 = (int)(((fromy+1)-py)*256);
1569 int yweight2 = (int)((ey-toy)*256);
1572 for(xx=fromx;xx<=tox;xx++)
1573 for(yy=fromy;yy<=toy;yy++) {
1574 int b = 1-data[width*yy+xx];
1576 if(xx==fromx) weight = (weight*xweight1)/256;
1577 if(xx==tox) weight = (weight*xweight2)/256;
1578 if(yy==fromy) weight = (weight*yweight1)/256;
1579 if(yy==toy) weight = (weight*yweight2)/256;
1582 //if(a) a=(palettesize-1)*r/blocksize;
1583 newdata[y*newwidth+x] = (a*blocksize)/r;
1591 #define IMAGE_TYPE_JPEG 0
1592 #define IMAGE_TYPE_LOSSLESS 1
1594 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1595 double x1,double y1,
1596 double x2,double y2,
1597 double x3,double y3,
1598 double x4,double y4, int type)
1600 gfxcolor_t*newpic=0;
1602 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1603 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1605 gfxline_t p1,p2,p3,p4,p5;
1606 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1607 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1608 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1609 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1610 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1612 {p1.x = (int)(p1.x*20)/20.0;
1613 p1.y = (int)(p1.y*20)/20.0;
1614 p2.x = (int)(p2.x*20)/20.0;
1615 p2.y = (int)(p2.y*20)/20.0;
1616 p3.x = (int)(p3.x*20)/20.0;
1617 p3.y = (int)(p3.y*20)/20.0;
1618 p4.x = (int)(p4.x*20)/20.0;
1619 p4.y = (int)(p4.y*20)/20.0;
1620 p5.x = (int)(p5.x*20)/20.0;
1621 p5.y = (int)(p5.y*20)/20.0;
1625 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1626 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1631 img.data = (gfxcolor_t*)data;
1635 if(type == IMAGE_TYPE_JPEG)
1636 /* TODO: pass image_dpi to device instead */
1637 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1639 dev->fillbitmap(dev, &p1, &img, &m, 0);
1642 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1643 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1645 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1648 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1649 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1651 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1655 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1656 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1657 GBool inlineImg, int mask, int*maskColors,
1658 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1660 double x1,y1,x2,y2,x3,y3,x4,y4;
1661 ImageStream *imgStr;
1666 unsigned char* maskbitmap = 0;
1669 ncomps = colorMap->getNumPixelComps();
1670 bits = colorMap->getBits();
1675 unsigned char buf[8];
1676 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1678 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1679 imgMaskStr->reset();
1680 unsigned char pal[256];
1681 int n = 1 << colorMap->getBits();
1686 maskColorMap->getGray(pixBuf, &gray);
1687 pal[t] = colToByte(gray);
1689 for (y = 0; y < maskHeight; y++) {
1690 for (x = 0; x < maskWidth; x++) {
1691 imgMaskStr->getPixel(buf);
1692 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1697 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1698 imgMaskStr->reset();
1699 for (y = 0; y < maskHeight; y++) {
1700 for (x = 0; x < maskWidth; x++) {
1701 imgMaskStr->getPixel(buf);
1703 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1711 imgStr = new ImageStream(str, width, ncomps,bits);
1714 if(!width || !height || (height<=1 && width<=1))
1716 msg("<verbose> Ignoring %d by %d image", width, height);
1717 unsigned char buf[8];
1719 for (y = 0; y < height; ++y)
1720 for (x = 0; x < width; ++x) {
1721 imgStr->getPixel(buf);
1729 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1730 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1731 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1732 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1734 if(!pbminfo && !(str->getKind()==strDCT)) {
1736 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1740 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1742 if(!jpeginfo && (str->getKind()==strDCT)) {
1743 msg("<notice> file contains jpeg pictures");
1748 unsigned char buf[8];
1750 unsigned char*pic = new unsigned char[width*height];
1751 gfxcolor_t pal[256];
1753 state->getFillRGB(&rgb);
1755 memset(pal,255,sizeof(pal));
1756 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1757 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1758 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1759 pal[0].a = 255; pal[1].a = 0;
1762 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1763 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1764 for (y = 0; y < height; ++y)
1765 for (x = 0; x < width; ++x)
1767 imgStr->getPixel(buf);
1770 pic[width*y+x] = buf[0];
1773 /* the size of the drawn image is added to the identifier
1774 as the same image may require different bitmaps if displayed
1775 at different sizes (due to antialiasing): */
1778 unsigned char*pic2 = 0;
1781 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1790 height = realheight;
1794 /* make a black/white palette */
1796 float r = 255./(float)(numpalette-1);
1798 for(t=0;t<numpalette;t++) {
1799 pal[t].r = colToByte(rgb.r);
1800 pal[t].g = colToByte(rgb.g);
1801 pal[t].b = colToByte(rgb.b);
1802 pal[t].a = (unsigned char)(t*r);
1806 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1807 for (y = 0; y < height; ++y) {
1808 for (x = 0; x < width; ++x) {
1809 pic2[width*y+x] = pal[pic[y*width+x]];
1812 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1816 if(maskbitmap) free(maskbitmap);
1822 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1823 gfxcolor_t*pic=new gfxcolor_t[width*height];
1824 for (y = 0; y < height; ++y) {
1825 for (x = 0; x < width; ++x) {
1826 imgStr->getPixel(pixBuf);
1827 colorMap->getRGB(pixBuf, &rgb);
1828 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1829 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1830 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1831 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1833 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1837 if(str->getKind()==strDCT)
1838 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1840 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1843 if(maskbitmap) free(maskbitmap);
1846 gfxcolor_t*pic=new gfxcolor_t[width*height];
1847 gfxcolor_t pal[256];
1848 int n = 1 << colorMap->getBits();
1850 for(t=0;t<256;t++) {
1852 colorMap->getRGB(pixBuf, &rgb);
1854 {/*if(maskColors && *maskColors==t) {
1855 msg("<notice> Color %d is transparent", t);
1856 if (imgData->maskColors) {
1858 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1859 if (pix[i] < imgData->maskColors[2*i] ||
1860 pix[i] > imgData->maskColors[2*i+1]) {
1875 pal[t].r = (unsigned char)(colToByte(rgb.r));
1876 pal[t].g = (unsigned char)(colToByte(rgb.g));
1877 pal[t].b = (unsigned char)(colToByte(rgb.b));
1878 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1881 for (y = 0; y < height; ++y) {
1882 for (x = 0; x < width; ++x) {
1883 imgStr->getPixel(pixBuf);
1884 pic[width*y+x] = pal[pixBuf[0]];
1886 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1890 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1894 if(maskbitmap) free(maskbitmap);
1899 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1900 int width, int height, GBool invert,
1903 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1904 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1905 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1908 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1909 int width, int height, GfxImageColorMap *colorMap,
1910 int *maskColors, GBool inlineImg)
1912 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1913 colorMap?"colorMap":"no colorMap",
1914 maskColors?"maskColors":"no maskColors",
1916 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1917 colorMap?"colorMap":"no colorMap",
1918 maskColors?"maskColors":"no maskColors",
1921 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1922 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1923 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1926 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1927 int width, int height,
1928 GfxImageColorMap *colorMap,
1929 Stream *maskStr, int maskWidth, int maskHeight,
1932 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1933 colorMap?"colorMap":"no colorMap",
1934 maskWidth, maskHeight);
1935 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1936 colorMap?"colorMap":"no colorMap",
1937 maskWidth, maskHeight);
1939 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1940 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1941 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1944 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1945 int width, int height,
1946 GfxImageColorMap *colorMap,
1948 int maskWidth, int maskHeight,
1949 GfxImageColorMap *maskColorMap)
1951 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1952 colorMap?"colorMap":"no colorMap",
1953 maskWidth, maskHeight);
1954 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1955 colorMap?"colorMap":"no colorMap",
1956 maskWidth, maskHeight);
1958 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1959 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1960 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1963 void GFXOutputDev::stroke(GfxState *state)
1967 GfxPath * path = state->getPath();
1968 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1969 strokeGfxline(state, line, 0);
1973 void GFXOutputDev::fill(GfxState *state)
1975 gfxcolor_t col = getFillColor(state);
1976 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1978 GfxPath * path = state->getPath();
1979 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1980 fillGfxLine(state, line);
1984 void GFXOutputDev::eoFill(GfxState *state)
1986 gfxcolor_t col = getFillColor(state);
1987 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1989 GfxPath * path = state->getPath();
1990 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1991 fillGfxLine(state, line);
1996 static const char* dirseparator()
2005 void addGlobalFont(const char*filename)
2008 memset(&f, 0, sizeof(fontfile_t));
2009 f.filename = filename;
2010 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2011 msg("<notice> Adding font \"%s\".", filename);
2012 fonts[fontnum++] = f;
2014 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2018 void addGlobalLanguageDir(const char*dir)
2020 msg("<notice> Adding %s to language pack directories", dir);
2023 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2024 strcpy(config_file, dir);
2025 strcat(config_file, dirseparator());
2026 strcat(config_file, "add-to-xpdfrc");
2028 fi = fopen(config_file, "rb");
2030 msg("<error> Could not open %s", config_file);
2033 globalParams->parseFile(new GString(config_file), fi);
2037 void addGlobalFontDir(const char*dirname)
2039 #ifdef HAVE_DIRENT_H
2040 msg("<notice> Adding %s to font directories", dirname);
2041 lastfontdir = strdup(dirname);
2042 DIR*dir = opendir(dirname);
2044 msg("<warning> Couldn't open directory %s\n", dirname);
2049 ent = readdir (dir);
2053 char*name = ent->d_name;
2059 if(!strncasecmp(&name[l-4], ".pfa", 4))
2061 if(!strncasecmp(&name[l-4], ".pfb", 4))
2063 if(!strncasecmp(&name[l-4], ".ttf", 4))
2066 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2067 strcpy(fontname, dirname);
2068 strcat(fontname, dirseparator());
2069 strcat(fontname, name);
2070 addGlobalFont(fontname);
2075 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2079 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2085 this->pagebuflen = 1024;
2086 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2087 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2089 while(pdfpage >= this->pagebuflen)
2091 int oldlen = this->pagebuflen;
2092 this->pagebuflen+=1024;
2093 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2094 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2097 this->pages[pdfpage] = outputpage;
2098 if(pdfpage>this->pagepos)
2099 this->pagepos = pdfpage;
2105 double width,height;
2108 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2110 double xMin, yMin, xMax, yMax, x, y;
2111 double tx, ty, w, h;
2112 // transform the bbox
2113 state->transform(bbox[0], bbox[1], &x, &y);
2116 state->transform(bbox[0], bbox[3], &x, &y);
2119 } else if (x > xMax) {
2124 } else if (y > yMax) {
2127 state->transform(bbox[2], bbox[1], &x, &y);
2130 } else if (x > xMax) {
2135 } else if (y > yMax) {
2138 state->transform(bbox[2], bbox[3], &x, &y);
2141 } else if (x > xMax) {
2146 } else if (y > yMax) {
2149 tx = (int)floor(xMin);
2152 } else if (tx > width) {
2155 ty = (int)floor(yMin);
2158 } else if (ty > height) {
2161 w = (int)ceil(xMax) - tx + 1;
2162 if (tx + w > width) {
2168 h = (int)ceil(yMax) - ty + 1;
2169 if (ty + h > height) {
2183 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2184 GfxColorSpace *blendingColorSpace,
2185 GBool isolated, GBool knockout,
2188 const char*colormodename = "";
2190 if(blendingColorSpace) {
2191 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2193 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);
2194 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);
2196 states[statepos].createsoftmask |= forSoftMask;
2197 states[statepos].transparencygroup = !forSoftMask;
2198 states[statepos].isolated = isolated;
2200 states[statepos].olddevice = this->device;
2201 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2203 gfxdevice_record_init(this->device);
2205 /*if(!forSoftMask) { ////???
2206 state->setFillOpacity(0.0);
2211 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2214 dbg("endTransparencyGroup");
2215 msg("<verbose> endTransparencyGroup");
2217 gfxdevice_t*r = this->device;
2219 this->device = states[statepos].olddevice;
2221 if(states[statepos].createsoftmask) {
2222 states[statepos-1].softmaskrecording = r->finish(r);
2224 states[statepos-1].grouprecording = r->finish(r);
2227 states[statepos].createsoftmask = 0;
2228 states[statepos].transparencygroup = 0;
2232 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2234 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2235 "colordodge","colorburn","hardlight","softlight","difference",
2236 "exclusion","hue","saturation","color","luminosity"};
2238 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2239 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2241 if(state->getBlendMode() == gfxBlendNormal)
2242 infofeature("transparency groups");
2245 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2246 warnfeature(buffer, 0);
2249 gfxresult_t*grouprecording = states[statepos].grouprecording;
2251 if(state->getBlendMode() == gfxBlendNormal) {
2253 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2254 gfxresult_record_replay(grouprecording, &ops);
2257 grouprecording->destroy(grouprecording);
2259 states[statepos].grouprecording = 0;
2262 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2264 /* alpha = 1: retrieve mask values from alpha layer
2265 alpha = 0: retrieve mask values from luminance */
2266 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2267 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2268 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2269 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2271 infofeature("soft masks");
2273 warnfeature("soft masks from alpha channel",0);
2275 states[statepos].olddevice = this->device;
2276 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2277 gfxdevice_record_init(this->device);
2279 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2281 states[statepos].softmask = 1;
2282 states[statepos].softmask_alpha = alpha;
2285 static inline Guchar div255(int x) {
2286 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2289 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2291 if(c < min) c = min;
2292 if(c > max) c = max;
2296 void GFXOutputDev::clearSoftMask(GfxState *state)
2298 if(!states[statepos].softmask)
2300 states[statepos].softmask = 0;
2301 dbg("clearSoftMask statepos=%d", statepos);
2302 msg("<verbose> clearSoftMask");
2304 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2305 msg("<error> Error in softmask/tgroup ordering");
2309 gfxresult_t*mask = states[statepos].softmaskrecording;
2310 gfxresult_t*below = this->device->finish(this->device);
2311 this->device = states[statepos].olddevice;
2313 /* get outline of all objects below the soft mask */
2314 gfxdevice_t uniondev;
2315 gfxdevice_union_init(&uniondev, 0);
2316 gfxresult_record_replay(below, &uniondev);
2317 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2318 uniondev.finish(&uniondev);
2320 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2322 this->device->startclip(this->device, belowoutline);
2323 gfxresult_record_replay(below, this->device);
2324 gfxresult_record_replay(mask, this->device);
2325 this->device->endclip(this->device);
2326 gfxline_free(belowoutline);
2329 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2330 if(width<=0 || height<=0)
2333 gfxdevice_t belowrender;
2334 gfxdevice_render_init(&belowrender);
2335 if(states[statepos+1].isolated) {
2336 belowrender.setparameter(&belowrender, "fillwhite", "1");
2338 belowrender.setparameter(&belowrender, "antialize", "2");
2339 belowrender.startpage(&belowrender, width, height);
2340 gfxresult_record_replay(below, &belowrender);
2341 belowrender.endpage(&belowrender);
2342 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2343 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2344 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2346 gfxdevice_t maskrender;
2347 gfxdevice_render_init(&maskrender);
2348 maskrender.startpage(&maskrender, width, height);
2349 gfxresult_record_replay(mask, &maskrender);
2350 maskrender.endpage(&maskrender);
2351 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2352 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2354 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2355 msg("<fatal> Internal error in mask drawing");
2360 for(y=0;y<height;y++) {
2361 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2362 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2363 for(x=0;x<width;x++) {
2365 if(states[statepos].softmask_alpha) {
2368 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2371 l2->a = div255(alpha*l2->a);
2373 /* DON'T premultiply alpha- this is done by fillbitmap,
2374 depending on the output device */
2375 //l2->r = div255(alpha*l2->r);
2376 //l2->g = div255(alpha*l2->g);
2377 //l2->b = div255(alpha*l2->b);
2383 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2386 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2387 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2389 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2391 mask->destroy(mask);
2392 below->destroy(below);
2393 maskresult->destroy(maskresult);
2394 belowresult->destroy(belowresult);
2395 states[statepos].softmaskrecording = 0;
2400 // public: ~MemCheck()
2402 // delete globalParams;globalParams=0;
2403 // Object::memCheck(stderr);
2404 // gMemReport(stderr);