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
83 int len; // basename length
85 struct _fontfile*next;
90 static fontfile_t* global_fonts = 0;
91 static fontfile_t* global_fonts_next = 0;
93 static int fontnum = 0;
97 static char* lastfontdir = 0;
108 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
109 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
110 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
111 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
112 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
113 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
114 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
115 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
116 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
117 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
118 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
119 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
120 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
121 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
124 static int verbose = 0;
125 static int dbgindent = 0;
126 static void dbg(const char*format, ...)
133 va_start(arglist, format);
134 vsprintf(buf, format, arglist);
137 while(l && buf[l-1]=='\n') {
142 int indent = dbgindent;
152 typedef struct _feature
155 struct _feature*next;
157 feature_t*featurewarnings = 0;
159 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
161 feature_t*f = featurewarnings;
163 if(!strcmp(feature, f->string))
167 f = (feature_t*)malloc(sizeof(feature_t));
168 f->string = strdup(feature);
169 f->next = featurewarnings;
172 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
173 if(this->config_break_on_warning) {
174 msg("<fatal> Aborting conversion due to unsupported feature");
178 msg("<notice> File contains %s",feature);
181 void GFXOutputDev::warnfeature(const char*feature,char fully)
183 showfeature(feature,fully,1);
185 void GFXOutputDev::infofeature(const char*feature)
187 showfeature(feature,0,0);
190 GFXOutputState::GFXOutputState() {
192 this->createsoftmask = 0;
193 this->transparencygroup = 0;
195 this->grouprecording = 0;
199 GBool GFXOutputDev::interpretType3Chars()
204 typedef struct _drawnchar
222 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
223 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
228 free(chars);chars = 0;
235 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
239 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
242 chars[num_chars].x = x;
243 chars[num_chars].y = y;
244 chars[num_chars].color = color;
245 chars[num_chars].charid = charid;
249 char* writeOutStdFont(fontentry* f)
254 char* tmpFileName = mktmpname(namebuf1);
256 sprintf(namebuf2, "%s.afm", tmpFileName);
257 fi = fopen(namebuf2, "wb");
260 fwrite(f->afm, 1, f->afmlen, fi);
263 sprintf(namebuf2, "%s.pfb", tmpFileName);
264 fi = fopen(namebuf2, "wb");
267 fwrite(f->pfb, 1, f->pfblen, fi);
269 return strdup(namebuf2);
271 void unlinkfont(char* filename)
276 msg("<verbose> Removing temporary font file %s", filename);
279 if(!strncmp(&filename[l-4],".afm",4)) {
280 memcpy(&filename[l-4],".pfb",4); unlink(filename);
281 memcpy(&filename[l-4],".pfa",4); unlink(filename);
282 memcpy(&filename[l-4],".afm",4);
285 if(!strncmp(&filename[l-4],".pfa",4)) {
286 memcpy(&filename[l-4],".afm",4); unlink(filename);
287 memcpy(&filename[l-4],".pfa",4);
290 if(!strncmp(&filename[l-4],".pfb",4)) {
291 memcpy(&filename[l-4],".afm",4); unlink(filename);
292 memcpy(&filename[l-4],".pfb",4);
298 GFXGlobalParams::GFXGlobalParams()
301 //setupBaseFonts(char *dir); //not tested yet
303 GFXGlobalParams::~GFXGlobalParams()
305 msg("<verbose> Performing cleanups");
307 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
308 if(pdf2t1map[t].fullfilename) {
309 unlinkfont(pdf2t1map[t].fullfilename);
313 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
315 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
317 char*name = fontName->getCString();
318 /* see if it is a pdf standard font */
320 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
321 if(!strcmp(name, pdf2t1map[t].pdffont)) {
322 if(!pdf2t1map[t].fullfilename) {
323 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
324 if(!pdf2t1map[t].fullfilename) {
325 msg("<error> Couldn't save default font- is the Temp Directory writable?");
327 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
330 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
331 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
336 int bestlen = 0x7fffffff;
337 const char*bestfilename = 0;
341 if(strstr(f->filename, name)) {
342 if(f->len < bestlen) {
344 bestfilename = f->filename;
350 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
351 dfp->t1.fileName = new GString(bestfilename);
354 return GlobalParams::getDisplayFont(fontName);
357 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
361 this->xref = doc->getXRef();
364 this->textmodeinfo = 0;
367 this->type3active = 0;
370 this->substitutepos = 0;
371 this->type3Warning = 0;
372 this->user_movex = 0;
373 this->user_movey = 0;
376 this->user_clipx1 = 0;
377 this->user_clipy1 = 0;
378 this->user_clipx2 = 0;
379 this->user_clipy2 = 0;
380 this->current_text_stroke = 0;
381 this->current_text_clip = 0;
382 this->outer_clip_box = 0;
384 this->pagebuflen = 0;
386 this->config_break_on_warning=0;
387 this->config_remapunicode=0;
388 this->config_transparent=0;
389 this->config_extrafontdata = 0;
390 this->config_fontquality = 10;
392 this->gfxfontlist = gfxfontlist_create();
394 memset(states, 0, sizeof(states));
397 void GFXOutputDev::setParameter(const char*key, const char*value)
399 if(!strcmp(key,"breakonwarning")) {
400 this->config_break_on_warning = atoi(value);
401 } else if(!strcmp(key,"remapunicode")) {
402 this->config_remapunicode = atoi(value);
403 } else if(!strcmp(key,"transparent")) {
404 this->config_transparent = atoi(value);
405 } else if(!strcmp(key,"extrafontdata")) {
406 this->config_extrafontdata = atoi(value);
407 } else if(!strcmp(key,"fontquality")) {
408 this->config_fontquality = atof(value);
409 if(this->config_fontquality<=1)
410 this->config_fontquality=1;
411 } else if(!strcmp(key,"help")) {
412 printf("\nPDF layer options:\n");
413 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
414 printf(" are not 100%% supported\n");
415 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
416 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
417 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
422 void GFXOutputDev::setDevice(gfxdevice_t*dev)
427 void GFXOutputDev::setMove(int x,int y)
429 this->user_movex = x;
430 this->user_movey = y;
433 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
435 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
436 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
438 this->user_clipx1 = x1;
439 this->user_clipy1 = y1;
440 this->user_clipx2 = x2;
441 this->user_clipy2 = y2;
444 static char*getFontName(GfxFont*font)
447 GString*gstr = font->getName();
448 char* fname = gstr==0?0:gstr->getCString();
452 sprintf(buf, "UFONT%d", r->num);
453 fontid = strdup(buf);
455 fontid = strdup(fname);
459 char* plus = strchr(fontid, '+');
460 if(plus && plus < &fontid[strlen(fontid)-1]) {
461 fontname = strdup(plus+1);
463 fontname = strdup(fontid);
469 static void dumpFontInfo(const char*loglevel, GfxFont*font);
470 static int lastdumps[1024];
471 static int lastdumppos = 0;
476 static void showFontError(GfxFont*font, int nr)
480 for(t=0;t<lastdumppos;t++)
481 if(lastdumps[t] == r->num)
485 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
486 lastdumps[lastdumppos++] = r->num;
488 msg("<warning> The following font caused problems:");
490 msg("<warning> The following font caused problems (substituting):");
492 msg("<warning> The following Type 3 Font will be rendered as graphics:");
493 dumpFontInfo("<warning>", font);
496 static void dumpFontInfo(const char*loglevel, GfxFont*font)
498 char* id = getFontID(font);
499 char* name = getFontName(font);
500 Ref* r=font->getID();
501 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
503 GString*gstr = font->getTag();
505 msg("%s| Tag: %s\n", loglevel, id);
507 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
509 GfxFontType type=font->getType();
511 case fontUnknownType:
512 msg("%s| Type: unknown\n",loglevel);
515 msg("%s| Type: 1\n",loglevel);
518 msg("%s| Type: 1C\n",loglevel);
521 msg("%s| Type: 3\n",loglevel);
524 msg("%s| Type: TrueType\n",loglevel);
527 msg("%s| Type: CIDType0\n",loglevel);
530 msg("%s| Type: CIDType0C\n",loglevel);
533 msg("%s| Type: CIDType2\n",loglevel);
538 GBool embedded = font->getEmbeddedFontID(&embRef);
540 if(font->getEmbeddedFontName()) {
541 embeddedName = font->getEmbeddedFontName()->getCString();
544 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
546 gstr = font->getExtFontFile();
548 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
550 // Get font descriptor flags.
551 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
552 if(font->isSerif()) msg("%s| is serif\n", loglevel);
553 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
554 if(font->isItalic()) msg("%s| is italic\n", loglevel);
555 if(font->isBold()) msg("%s| is bold\n", loglevel);
561 //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");}
562 //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");}
564 void dump_outline(gfxline_t*line)
567 if(line->type == gfx_moveTo) {
568 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
569 } else if(line->type == gfx_lineTo) {
570 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
571 } else if(line->type == gfx_splineTo) {
572 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
578 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
580 int num = path->getNumSubpaths();
583 double lastx=0,lasty=0,posx=0,posy=0;
586 msg("<warning> empty path");
590 gfxdrawer_target_gfxline(&draw);
592 for(t = 0; t < num; t++) {
593 GfxSubpath *subpath = path->getSubpath(t);
594 int subnum = subpath->getNumPoints();
595 double bx=0,by=0,cx=0,cy=0;
597 for(s=0;s<subnum;s++) {
600 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
605 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
606 draw.lineTo(&draw, lastx, lasty);
608 draw.moveTo(&draw, x,y);
613 } else if(subpath->getCurve(s) && cpos==0) {
617 } else if(subpath->getCurve(s) && cpos==1) {
625 draw.lineTo(&draw, x,y);
627 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
634 /* fix non-closed lines */
635 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
636 draw.lineTo(&draw, lastx, lasty);
638 gfxline_t*result = (gfxline_t*)draw.result(&draw);
640 gfxline_optimize(result);
645 GBool GFXOutputDev::useTilingPatternFill()
647 infofeature("tiled patterns");
651 GBool GFXOutputDev::useShadedFills()
653 infofeature("shaded fills");
657 GBool GFXOutputDev::useDrawForm()
659 infofeature("forms");
662 void GFXOutputDev::drawForm(Ref id)
664 msg("<error> drawForm not implemented");
666 GBool GFXOutputDev::needNonText()
670 void GFXOutputDev::endPage()
672 msg("<verbose> endPage");
674 device->endclip(device);
679 #define STROKE_FILL 1
680 #define STROKE_CLIP 2
681 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
683 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
684 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
685 double miterLimit = state->getMiterLimit();
686 double width = state->getTransformedLineWidth();
689 double opaq = state->getStrokeOpacity();
691 state->getFillRGB(&rgb);
693 state->getStrokeRGB(&rgb);
695 col.r = colToByte(rgb.r);
696 col.g = colToByte(rgb.g);
697 col.b = colToByte(rgb.b);
698 col.a = (unsigned char)(opaq*255);
700 gfx_capType capType = gfx_capRound;
701 if(lineCap == 0) capType = gfx_capButt;
702 else if(lineCap == 1) capType = gfx_capRound;
703 else if(lineCap == 2) capType = gfx_capSquare;
705 gfx_joinType joinType = gfx_joinRound;
706 if(lineJoin == 0) joinType = gfx_joinMiter;
707 else if(lineJoin == 1) joinType = gfx_joinRound;
708 else if(lineJoin == 2) joinType = gfx_joinBevel;
711 double dashphase = 0;
713 state->getLineDash(&ldash, &dashnum, &dashphase);
717 if(dashnum && ldash) {
718 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
720 msg("<trace> %d dashes", dashnum);
721 msg("<trace> | phase: %f", dashphase);
722 for(t=0;t<dashnum;t++) {
723 dash[t] = (float)ldash[t];
724 msg("<trace> | d%-3d: %f", t, ldash[t]);
727 if(getLogLevel() >= LOGLEVEL_TRACE) {
731 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
734 msg("<trace> After dashing:");
737 if(getLogLevel() >= LOGLEVEL_TRACE) {
738 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
740 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
741 lineCap==0?"butt": (lineJoin==1?"round":"square"),
743 col.r,col.g,col.b,col.a
748 if(flags&STROKE_FILL) {
749 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
750 gfxline_t*gfxline = SVPtogfxline(svp);
751 if(getLogLevel() >= LOGLEVEL_TRACE) {
752 dump_outline(gfxline);
755 msg("<warning> Empty polygon (resulting from stroked line)");
757 if(flags&STROKE_CLIP) {
758 device->startclip(device, gfxline);
759 states[statepos].clipping++;
761 device->fill(device, gfxline, &col);
766 if(flags&STROKE_CLIP)
767 msg("<error> Stroke&clip not supported at the same time");
768 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
775 gfxcolor_t getFillColor(GfxState * state)
778 double opaq = state->getFillOpacity();
779 state->getFillRGB(&rgb);
781 col.r = colToByte(rgb.r);
782 col.g = colToByte(rgb.g);
783 col.b = colToByte(rgb.b);
784 col.a = (unsigned char)(opaq*255);
788 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
790 gfxcolor_t col = getFillColor(state);
792 if(getLogLevel() >= LOGLEVEL_TRACE) {
793 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
796 device->fill(device, line, &col);
799 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
801 if(getLogLevel() >= LOGLEVEL_TRACE) {
802 msg("<trace> clip\n");
806 device->startclip(device, line);
807 states[statepos].clipping++;
810 void GFXOutputDev::clip(GfxState *state)
812 GfxPath * path = state->getPath();
813 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
814 clipToGfxLine(state, line);
818 void GFXOutputDev::eoClip(GfxState *state)
820 GfxPath * path = state->getPath();
821 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
823 if(getLogLevel() >= LOGLEVEL_TRACE) {
824 msg("<trace> eoclip\n");
828 device->startclip(device, line);
829 states[statepos].clipping++;
832 void GFXOutputDev::clipToStrokePath(GfxState *state)
834 GfxPath * path = state->getPath();
835 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
837 if(getLogLevel() >= LOGLEVEL_TRACE) {
838 msg("<trace> cliptostrokepath\n");
842 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
846 void GFXOutputDev::finish()
850 device->endclip(device);
856 GFXOutputDev::~GFXOutputDev()
861 free(this->pages); this->pages = 0;
864 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
866 GBool GFXOutputDev::upsideDown()
870 GBool GFXOutputDev::useDrawChar()
875 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
876 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
878 static char tmp_printstr[4096];
879 char* makeStringPrintable(char*str)
881 int len = strlen(str);
896 tmp_printstr[len++] = '.';
897 tmp_printstr[len++] = '.';
898 tmp_printstr[len++] = '.';
900 tmp_printstr[len] = 0;
903 #define INTERNAL_FONT_SIZE 1024.0
904 void GFXOutputDev::updateFontMatrix(GfxState*state)
906 double* ctm = state->getCTM();
907 double fontSize = state->getFontSize();
908 double*textMat = state->getTextMat();
910 /* taking the absolute value of horizScaling seems to be required for
911 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
912 double hscale = fabs(state->getHorizScaling());
914 // from xpdf-3.02/SplashOutputDev:updateFont
915 double mm11 = textMat[0] * fontSize * hscale;
916 double mm12 = textMat[1] * fontSize * hscale;
917 double mm21 = textMat[2] * fontSize;
918 double mm22 = textMat[3] * fontSize;
920 // multiply with ctm, like state->getFontTransMat() does
921 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
922 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
923 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
924 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
925 this->current_font_matrix.tx = 0;
926 this->current_font_matrix.ty = 0;
929 void GFXOutputDev::beginString(GfxState *state, GString *s)
931 int render = state->getRender();
932 if(current_text_stroke) {
933 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
936 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
939 static gfxline_t* mkEmptyGfxShape(double x, double y)
941 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
942 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
946 static char isValidUnicode(int c)
948 if(c>=32 && c<0x2fffe)
953 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
954 double dx, double dy,
955 double originX, double originY,
956 CharCode charid, int nBytes, Unicode *_u, int uLen)
958 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
959 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
963 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
965 int render = state->getRender();
966 gfxcolor_t col = getFillColor(state);
968 // check for invisible text -- this is used by Acrobat Capture
969 if (render == RENDER_INVISIBLE) {
971 if(!config_extrafontdata)
975 GfxFont*font = state->getFont();
977 if(font->getType() == fontType3) {
978 /* type 3 chars are passed as graphics */
979 msg("<debug> type3 char at %f/%f", x, y);
983 Unicode u = uLen?(_u[0]):0;
984 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);
986 gfxmatrix_t m = this->current_font_matrix;
987 state->transform(x, y, &m.tx, &m.ty);
988 m.tx += user_movex + clipmovex;
989 m.ty += user_movey + clipmovey;
991 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
992 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
994 msg("<debug> Drawing glyph %d as shape", charid);
996 msg("<notice> Some texts will be rendered as shape");
999 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1000 gfxline_t*tglyph = gfxline_clone(glyph);
1001 gfxline_transform(tglyph, &m);
1002 if((render&3) != RENDER_INVISIBLE) {
1003 gfxline_t*add = gfxline_clone(tglyph);
1004 current_text_stroke = gfxline_append(current_text_stroke, add);
1006 if(render&RENDER_CLIP) {
1007 gfxline_t*add = gfxline_clone(tglyph);
1008 current_text_clip = gfxline_append(current_text_clip, add);
1009 if(!current_text_clip) {
1010 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1013 gfxline_free(tglyph);
1017 void GFXOutputDev::endString(GfxState *state)
1019 int render = state->getRender();
1020 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1022 if(current_text_stroke) {
1023 /* fillstroke and stroke text rendering objects we can process right
1024 now (as there may be texts of other rendering modes in this
1025 text object)- clipping objects have to wait until endTextObject,
1027 device->setparameter(device, "mark","TXT");
1028 if((render&3) == RENDER_FILL) {
1029 fillGfxLine(state, current_text_stroke);
1030 gfxline_free(current_text_stroke);
1031 current_text_stroke = 0;
1032 } else if((render&3) == RENDER_FILLSTROKE) {
1033 fillGfxLine(state, current_text_stroke);
1034 strokeGfxline(state, current_text_stroke,0);
1035 gfxline_free(current_text_stroke);
1036 current_text_stroke = 0;
1037 } else if((render&3) == RENDER_STROKE) {
1038 strokeGfxline(state, current_text_stroke,0);
1039 gfxline_free(current_text_stroke);
1040 current_text_stroke = 0;
1042 device->setparameter(device, "mark","");
1046 void GFXOutputDev::endTextObject(GfxState *state)
1048 int render = state->getRender();
1049 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1051 if(current_text_clip) {
1052 device->setparameter(device, "mark","TXT");
1053 clipToGfxLine(state, current_text_clip);
1054 device->setparameter(device, "mark","");
1055 gfxline_free(current_text_clip);
1056 current_text_clip = 0;
1060 /* the logic seems to be as following:
1061 first, beginType3Char is called, with the charcode and the coordinates.
1062 if this function returns true, it already knew about the char and has now drawn it.
1063 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1064 called with some parameters.
1065 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1066 at the position first passed to beginType3Char). the char ends with endType3Char.
1068 The drawing operations between beginType3Char and endType3Char are somewhat different to
1069 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1070 color determines the color of a font)
1073 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1075 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1078 if(config_extrafontdata && current_fontinfo) {
1080 gfxmatrix_t m = this->current_font_matrix;
1081 state->transform(0, 0, &m.tx, &m.ty);
1082 m.m00*=INTERNAL_FONT_SIZE;
1083 m.m01*=INTERNAL_FONT_SIZE;
1084 m.m10*=INTERNAL_FONT_SIZE;
1085 m.m11*=INTERNAL_FONT_SIZE;
1086 m.tx += user_movex + clipmovex;
1087 m.ty += user_movey + clipmovey;
1089 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1090 msg("<error> Invalid charid %d for font", charid);
1093 gfxcolor_t col={0,0,0,0};
1094 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1095 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1099 /* the character itself is going to be passed using the draw functions */
1100 return gFalse; /* gTrue= is_in_cache? */
1103 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1105 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1108 void GFXOutputDev::endType3Char(GfxState *state)
1111 msg("<debug> endType3Char");
1114 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1116 this->currentpage = pageNum;
1118 int rot = doc->getPageRotate(1);
1119 gfxcolor_t white = {255,255,255,255};
1120 gfxcolor_t black = {255,0,0,0};
1122 gfxline_t clippath[5];
1124 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1125 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1126 Use CropBox, not MediaBox, as page size
1133 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1134 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1136 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1137 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1139 this->clipmovex = -(int)x1;
1140 this->clipmovey = -(int)y1;
1142 /* apply user clip box */
1143 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1144 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1145 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1146 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1147 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1148 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1151 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1153 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);
1155 msg("<verbose> page is rotated %d degrees\n", rot);
1157 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1158 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1159 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1160 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1161 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1162 device->startclip(device, clippath); outer_clip_box = 1;
1163 if(!config_transparent) {
1164 device->fill(device, clippath, &white);
1169 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1171 double x1, y1, x2, y2;
1172 gfxline_t points[5];
1175 msg("<debug> drawlink\n");
1177 link->getRect(&x1, &y1, &x2, &y2);
1178 cvtUserToDev(x1, y1, &x, &y);
1179 points[0].type = gfx_moveTo;
1180 points[0].x = points[4].x = x + user_movex + clipmovex;
1181 points[0].y = points[4].y = y + user_movey + clipmovey;
1182 points[0].next = &points[1];
1183 cvtUserToDev(x2, y1, &x, &y);
1184 points[1].type = gfx_lineTo;
1185 points[1].x = x + user_movex + clipmovex;
1186 points[1].y = y + user_movey + clipmovey;
1187 points[1].next = &points[2];
1188 cvtUserToDev(x2, y2, &x, &y);
1189 points[2].type = gfx_lineTo;
1190 points[2].x = x + user_movex + clipmovex;
1191 points[2].y = y + user_movey + clipmovey;
1192 points[2].next = &points[3];
1193 cvtUserToDev(x1, y2, &x, &y);
1194 points[3].type = gfx_lineTo;
1195 points[3].x = x + user_movex + clipmovex;
1196 points[3].y = y + user_movey + clipmovey;
1197 points[3].next = &points[4];
1198 cvtUserToDev(x1, y1, &x, &y);
1199 points[4].type = gfx_lineTo;
1200 points[4].x = x + user_movex + clipmovex;
1201 points[4].y = y + user_movey + clipmovey;
1204 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1205 points[0].x, points[0].y,
1206 points[1].x, points[1].y,
1207 points[2].x, points[2].y,
1208 points[3].x, points[3].y);
1210 LinkAction*action=link->getAction();
1213 const char*type = "-?-";
1216 msg("<trace> drawlink action=%d\n", action->getKind());
1217 switch(action->getKind())
1221 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1222 LinkDest *dest=NULL;
1223 if (ha->getDest()==NULL)
1224 dest=catalog->findDest(ha->getNamedDest());
1225 else dest=ha->getDest();
1227 if (dest->isPageRef()){
1228 Ref pageref=dest->getPageRef();
1229 page=catalog->findPage(pageref.num,pageref.gen);
1231 else page=dest->getPageNum();
1232 sprintf(buf, "%d", page);
1239 LinkGoToR*l = (LinkGoToR*)action;
1240 GString*g = l->getFileName();
1242 s = strdup(g->getCString());
1244 /* if the GoToR link has no filename, then
1245 try to find a refernce in the *local*
1247 GString*g = l->getNamedDest();
1249 s = strdup(g->getCString());
1255 LinkNamed*l = (LinkNamed*)action;
1256 GString*name = l->getName();
1258 s = strdup(name->lowerCase()->getCString());
1259 named = name->getCString();
1262 if(strstr(s, "next") || strstr(s, "forward"))
1264 page = currentpage + 1;
1266 else if(strstr(s, "prev") || strstr(s, "back"))
1268 page = currentpage - 1;
1270 else if(strstr(s, "last") || strstr(s, "end"))
1272 if(pages && pagepos>0)
1273 page = pages[pagepos-1];
1275 else if(strstr(s, "first") || strstr(s, "top"))
1283 case actionLaunch: {
1285 LinkLaunch*l = (LinkLaunch*)action;
1286 GString * str = new GString(l->getFileName());
1287 GString * params = l->getParams();
1289 str->append(params);
1290 s = strdup(str->getCString());
1297 LinkURI*l = (LinkURI*)action;
1298 GString*g = l->getURI();
1300 url = g->getCString();
1305 case actionUnknown: {
1307 LinkUnknown*l = (LinkUnknown*)action;
1312 msg("<error> Unknown link type!\n");
1317 if(!s) s = strdup("-?-");
1319 msg("<trace> drawlink s=%s\n", s);
1321 if(!linkinfo && (page || s))
1323 msg("<notice> File contains links");
1331 for(t=1;t<=pagepos;t++) {
1332 if(pages[t]==page) {
1341 sprintf(buf, "page%d", lpage);
1342 device->drawlink(device, points, buf);
1346 device->drawlink(device, points, s);
1349 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1353 void GFXOutputDev::saveState(GfxState *state) {
1354 dbg("saveState");dbgindent+=2;
1356 msg("<trace> saveState\n");
1359 msg("<error> Too many nested states in pdf.");
1363 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1364 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1365 states[statepos].clipping = 0;
1368 void GFXOutputDev::restoreState(GfxState *state) {
1369 dbgindent-=2; dbg("restoreState");
1372 msg("<error> Invalid restoreState");
1375 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1376 states[statepos].clipping?" (end clipping)":"");
1377 if(states[statepos].softmask) {
1378 clearSoftMask(state);
1381 while(states[statepos].clipping) {
1382 device->endclip(device);
1383 states[statepos].clipping--;
1388 void GFXOutputDev::updateLineWidth(GfxState *state)
1390 double width = state->getTransformedLineWidth();
1391 //swfoutput_setlinewidth(&device, width);
1394 void GFXOutputDev::updateLineCap(GfxState *state)
1396 int c = state->getLineCap();
1399 void GFXOutputDev::updateLineJoin(GfxState *state)
1401 int j = state->getLineJoin();
1404 void GFXOutputDev::updateFillColor(GfxState *state)
1407 double opaq = state->getFillOpacity();
1408 state->getFillRGB(&rgb);
1410 void GFXOutputDev::updateFillOpacity(GfxState *state)
1413 double opaq = state->getFillOpacity();
1414 state->getFillRGB(&rgb);
1415 dbg("update fillopaq %f", opaq);
1417 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1419 double opaq = state->getFillOpacity();
1420 dbg("update strokeopaq %f", opaq);
1422 void GFXOutputDev::updateFillOverprint(GfxState *state)
1424 double opaq = state->getFillOverprint();
1425 dbg("update filloverprint %f", opaq);
1427 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1429 double opaq = state->getStrokeOverprint();
1430 dbg("update strokeoverprint %f", opaq);
1432 void GFXOutputDev::updateTransfer(GfxState *state)
1434 dbg("update transfer");
1438 void GFXOutputDev::updateStrokeColor(GfxState *state)
1441 double opaq = state->getStrokeOpacity();
1442 state->getStrokeRGB(&rgb);
1446 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1448 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1449 memset(font, 0, sizeof(gfxfont_t));
1451 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1452 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1453 font->id = strdup(getFontID(xpdffont));
1456 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1458 //printf("%d glyphs\n", font->num_glyphs);
1459 font->num_glyphs = 0;
1460 for(t=0;t<src->num_glyphs;t++) {
1461 if(src->glyphs[t]) {
1462 SplashPath*path = src->glyphs[t]->path;
1463 int len = path?path->getLength():0;
1464 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1465 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1466 src->glyphs[t]->glyphid = font->num_glyphs;
1467 glyph->unicode = src->glyphs[t]->unicode;
1468 if(glyph->unicode >= font->max_unicode)
1469 font->max_unicode = glyph->unicode+1;
1471 gfxdrawer_target_gfxline(&drawer);
1475 for(s=0;s<len;s++) {
1478 path->getPoint(s, &x, &y, &f);
1481 if(f&splashPathFirst) {
1482 drawer.moveTo(&drawer, x*scale, y*scale);
1484 if(f&splashPathCurve) {
1486 path->getPoint(++s, &x2, &y2, &f);
1487 if(f&splashPathCurve) {
1489 path->getPoint(++s, &x3, &y3, &f);
1490 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1492 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1495 drawer.lineTo(&drawer, x*scale, y*scale);
1497 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1498 // (f&splashPathFirst)?"first":"",
1499 // (f&splashPathLast)?"last":"");
1501 glyph->line = (gfxline_t*)drawer.result(&drawer);
1502 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1506 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1507 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1508 for(t=0;t<font->num_glyphs;t++) {
1509 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1510 font->unicode2glyph[font->glyphs[t].unicode] = t;
1514 msg("<trace> %d glyphs.", t, font->num_glyphs);
1518 void GFXOutputDev::updateFont(GfxState *state)
1520 GfxFont* gfxFont = state->getFont();
1524 char*id = getFontID(gfxFont);
1525 msg("<verbose> Updating font to %s", id);
1526 if(gfxFont->getType() == fontType3) {
1527 infofeature("Type3 fonts");
1528 if(!config_extrafontdata) {
1533 msg("<error> Internal Error: FontID is null");
1537 this->current_fontinfo = this->info->getFont(id);
1538 if(!this->current_fontinfo) {
1539 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1542 if(!this->current_fontinfo->seen) {
1543 dumpFontInfo("<verbose>", gfxFont);
1546 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1548 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1549 font->id = strdup(id);
1550 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1551 device->addfont(device, font);
1553 current_gfxfont = font;
1556 updateFontMatrix(state);
1559 #define SQR(x) ((x)*(x))
1561 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1563 if((newwidth<2 || newheight<2) ||
1564 (width<=newwidth || height<=newheight))
1566 unsigned char*newdata;
1568 newdata= (unsigned char*)malloc(newwidth*newheight);
1569 double fx = (double)(width)/newwidth;
1570 double fy = (double)(height)/newheight;
1572 int blocksize = (int)(8192/(fx*fy));
1573 int r = 8192*256/palettesize;
1574 for(x=0;x<newwidth;x++) {
1575 double ex = px + fx;
1576 int fromx = (int)px;
1578 int xweight1 = (int)(((fromx+1)-px)*256);
1579 int xweight2 = (int)((ex-tox)*256);
1581 for(y=0;y<newheight;y++) {
1582 double ey = py + fy;
1583 int fromy = (int)py;
1585 int yweight1 = (int)(((fromy+1)-py)*256);
1586 int yweight2 = (int)((ey-toy)*256);
1589 for(xx=fromx;xx<=tox;xx++)
1590 for(yy=fromy;yy<=toy;yy++) {
1591 int b = 1-data[width*yy+xx];
1593 if(xx==fromx) weight = (weight*xweight1)/256;
1594 if(xx==tox) weight = (weight*xweight2)/256;
1595 if(yy==fromy) weight = (weight*yweight1)/256;
1596 if(yy==toy) weight = (weight*yweight2)/256;
1599 //if(a) a=(palettesize-1)*r/blocksize;
1600 newdata[y*newwidth+x] = (a*blocksize)/r;
1608 #define IMAGE_TYPE_JPEG 0
1609 #define IMAGE_TYPE_LOSSLESS 1
1611 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1612 double x1,double y1,
1613 double x2,double y2,
1614 double x3,double y3,
1615 double x4,double y4, int type)
1617 gfxcolor_t*newpic=0;
1619 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1620 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1622 gfxline_t p1,p2,p3,p4,p5;
1623 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1624 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1625 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1626 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1627 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1629 {p1.x = (int)(p1.x*20)/20.0;
1630 p1.y = (int)(p1.y*20)/20.0;
1631 p2.x = (int)(p2.x*20)/20.0;
1632 p2.y = (int)(p2.y*20)/20.0;
1633 p3.x = (int)(p3.x*20)/20.0;
1634 p3.y = (int)(p3.y*20)/20.0;
1635 p4.x = (int)(p4.x*20)/20.0;
1636 p4.y = (int)(p4.y*20)/20.0;
1637 p5.x = (int)(p5.x*20)/20.0;
1638 p5.y = (int)(p5.y*20)/20.0;
1642 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1643 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1648 img.data = (gfxcolor_t*)data;
1652 if(type == IMAGE_TYPE_JPEG)
1653 /* TODO: pass image_dpi to device instead */
1654 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1656 dev->fillbitmap(dev, &p1, &img, &m, 0);
1659 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1660 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1662 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1665 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1666 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1668 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1672 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1673 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1674 GBool inlineImg, int mask, int*maskColors,
1675 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1677 double x1,y1,x2,y2,x3,y3,x4,y4;
1678 ImageStream *imgStr;
1683 unsigned char* maskbitmap = 0;
1686 ncomps = colorMap->getNumPixelComps();
1687 bits = colorMap->getBits();
1692 unsigned char buf[8];
1693 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1695 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1696 imgMaskStr->reset();
1697 unsigned char pal[256];
1698 int n = 1 << colorMap->getBits();
1703 maskColorMap->getGray(pixBuf, &gray);
1704 pal[t] = colToByte(gray);
1706 for (y = 0; y < maskHeight; y++) {
1707 for (x = 0; x < maskWidth; x++) {
1708 imgMaskStr->getPixel(buf);
1709 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1714 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1715 imgMaskStr->reset();
1716 for (y = 0; y < maskHeight; y++) {
1717 for (x = 0; x < maskWidth; x++) {
1718 imgMaskStr->getPixel(buf);
1720 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1728 imgStr = new ImageStream(str, width, ncomps,bits);
1731 if(!width || !height || (height<=1 && width<=1))
1733 msg("<verbose> Ignoring %d by %d image", width, height);
1734 unsigned char buf[8];
1736 for (y = 0; y < height; ++y)
1737 for (x = 0; x < width; ++x) {
1738 imgStr->getPixel(buf);
1746 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1747 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1748 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1749 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1751 if(!pbminfo && !(str->getKind()==strDCT)) {
1753 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1757 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1759 if(!jpeginfo && (str->getKind()==strDCT)) {
1760 msg("<notice> file contains jpeg pictures");
1765 unsigned char buf[8];
1767 unsigned char*pic = new unsigned char[width*height];
1768 gfxcolor_t pal[256];
1770 state->getFillRGB(&rgb);
1772 memset(pal,255,sizeof(pal));
1773 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1774 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1775 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1776 pal[0].a = 255; pal[1].a = 0;
1779 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1780 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1781 for (y = 0; y < height; ++y)
1782 for (x = 0; x < width; ++x)
1784 imgStr->getPixel(buf);
1787 pic[width*y+x] = buf[0];
1790 /* the size of the drawn image is added to the identifier
1791 as the same image may require different bitmaps if displayed
1792 at different sizes (due to antialiasing): */
1795 unsigned char*pic2 = 0;
1798 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1807 height = realheight;
1811 /* make a black/white palette */
1813 float r = 255./(float)(numpalette-1);
1815 for(t=0;t<numpalette;t++) {
1816 pal[t].r = colToByte(rgb.r);
1817 pal[t].g = colToByte(rgb.g);
1818 pal[t].b = colToByte(rgb.b);
1819 pal[t].a = (unsigned char)(t*r);
1823 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1824 for (y = 0; y < height; ++y) {
1825 for (x = 0; x < width; ++x) {
1826 pic2[width*y+x] = pal[pic[y*width+x]];
1829 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1833 if(maskbitmap) free(maskbitmap);
1839 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1840 gfxcolor_t*pic=new gfxcolor_t[width*height];
1841 for (y = 0; y < height; ++y) {
1842 for (x = 0; x < width; ++x) {
1843 imgStr->getPixel(pixBuf);
1844 colorMap->getRGB(pixBuf, &rgb);
1845 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1846 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1847 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1848 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1850 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1854 if(str->getKind()==strDCT)
1855 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1857 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1860 if(maskbitmap) free(maskbitmap);
1863 gfxcolor_t*pic=new gfxcolor_t[width*height];
1864 gfxcolor_t pal[256];
1865 int n = 1 << colorMap->getBits();
1867 for(t=0;t<256;t++) {
1869 colorMap->getRGB(pixBuf, &rgb);
1871 {/*if(maskColors && *maskColors==t) {
1872 msg("<notice> Color %d is transparent", t);
1873 if (imgData->maskColors) {
1875 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1876 if (pix[i] < imgData->maskColors[2*i] ||
1877 pix[i] > imgData->maskColors[2*i+1]) {
1892 pal[t].r = (unsigned char)(colToByte(rgb.r));
1893 pal[t].g = (unsigned char)(colToByte(rgb.g));
1894 pal[t].b = (unsigned char)(colToByte(rgb.b));
1895 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1898 for (y = 0; y < height; ++y) {
1899 for (x = 0; x < width; ++x) {
1900 imgStr->getPixel(pixBuf);
1901 pic[width*y+x] = pal[pixBuf[0]];
1903 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1907 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1911 if(maskbitmap) free(maskbitmap);
1916 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1917 int width, int height, GBool invert,
1920 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1921 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1922 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1925 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1926 int width, int height, GfxImageColorMap *colorMap,
1927 int *maskColors, GBool inlineImg)
1929 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1930 colorMap?"colorMap":"no colorMap",
1931 maskColors?"maskColors":"no maskColors",
1933 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1934 colorMap?"colorMap":"no colorMap",
1935 maskColors?"maskColors":"no maskColors",
1938 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1939 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1940 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1943 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1944 int width, int height,
1945 GfxImageColorMap *colorMap,
1946 Stream *maskStr, int maskWidth, int maskHeight,
1949 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1950 colorMap?"colorMap":"no colorMap",
1951 maskWidth, maskHeight);
1952 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1953 colorMap?"colorMap":"no colorMap",
1954 maskWidth, maskHeight);
1956 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1957 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1958 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1961 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1962 int width, int height,
1963 GfxImageColorMap *colorMap,
1965 int maskWidth, int maskHeight,
1966 GfxImageColorMap *maskColorMap)
1968 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1969 colorMap?"colorMap":"no colorMap",
1970 maskWidth, maskHeight);
1971 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1972 colorMap?"colorMap":"no colorMap",
1973 maskWidth, maskHeight);
1975 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1976 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1977 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1980 void GFXOutputDev::stroke(GfxState *state)
1984 GfxPath * path = state->getPath();
1985 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1986 strokeGfxline(state, line, 0);
1990 void GFXOutputDev::fill(GfxState *state)
1992 gfxcolor_t col = getFillColor(state);
1993 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1995 GfxPath * path = state->getPath();
1996 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1997 fillGfxLine(state, line);
2001 void GFXOutputDev::eoFill(GfxState *state)
2003 gfxcolor_t col = getFillColor(state);
2004 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2006 GfxPath * path = state->getPath();
2007 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2008 fillGfxLine(state, line);
2013 static const char* dirseparator()
2022 void addGlobalFont(const char*filename)
2024 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2025 memset(f, 0, sizeof(fontfile_t));
2026 f->filename = filename;
2027 int len = strlen(filename);
2028 char*r1 = strrchr(filename, '/');
2029 char*r2 = strrchr(filename, '\\');
2037 msg("<notice> Adding font \"%s\".", filename);
2038 if(global_fonts_next) {
2039 global_fonts_next->next = f;
2040 global_fonts_next = global_fonts_next->next;
2042 global_fonts_next = global_fonts = f;
2046 void addGlobalLanguageDir(const char*dir)
2048 msg("<notice> Adding %s to language pack directories", dir);
2051 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2052 strcpy(config_file, dir);
2053 strcat(config_file, dirseparator());
2054 strcat(config_file, "add-to-xpdfrc");
2056 fi = fopen(config_file, "rb");
2058 msg("<error> Could not open %s", config_file);
2061 globalParams->parseFile(new GString(config_file), fi);
2065 void addGlobalFontDir(const char*dirname)
2067 #ifdef HAVE_DIRENT_H
2068 msg("<notice> Adding %s to font directories", dirname);
2069 lastfontdir = strdup(dirname);
2070 DIR*dir = opendir(dirname);
2072 msg("<warning> Couldn't open directory %s\n", dirname);
2077 ent = readdir (dir);
2081 char*name = ent->d_name;
2087 if(!strncasecmp(&name[l-4], ".pfa", 4))
2089 if(!strncasecmp(&name[l-4], ".pfb", 4))
2091 if(!strncasecmp(&name[l-4], ".ttf", 4))
2094 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2095 strcpy(fontname, dirname);
2096 strcat(fontname, dirseparator());
2097 strcat(fontname, name);
2098 addGlobalFont(fontname);
2103 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2107 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2113 this->pagebuflen = 1024;
2114 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2115 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2117 while(pdfpage >= this->pagebuflen)
2119 int oldlen = this->pagebuflen;
2120 this->pagebuflen+=1024;
2121 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2122 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2125 this->pages[pdfpage] = outputpage;
2126 if(pdfpage>this->pagepos)
2127 this->pagepos = pdfpage;
2133 double width,height;
2136 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2138 double xMin, yMin, xMax, yMax, x, y;
2139 double tx, ty, w, h;
2140 // transform the bbox
2141 state->transform(bbox[0], bbox[1], &x, &y);
2144 state->transform(bbox[0], bbox[3], &x, &y);
2147 } else if (x > xMax) {
2152 } else if (y > yMax) {
2155 state->transform(bbox[2], bbox[1], &x, &y);
2158 } else if (x > xMax) {
2163 } else if (y > yMax) {
2166 state->transform(bbox[2], bbox[3], &x, &y);
2169 } else if (x > xMax) {
2174 } else if (y > yMax) {
2177 tx = (int)floor(xMin);
2180 } else if (tx > width) {
2183 ty = (int)floor(yMin);
2186 } else if (ty > height) {
2189 w = (int)ceil(xMax) - tx + 1;
2190 if (tx + w > width) {
2196 h = (int)ceil(yMax) - ty + 1;
2197 if (ty + h > height) {
2211 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2212 GfxColorSpace *blendingColorSpace,
2213 GBool isolated, GBool knockout,
2216 const char*colormodename = "";
2218 if(blendingColorSpace) {
2219 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2221 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);
2222 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);
2224 states[statepos].createsoftmask |= forSoftMask;
2225 states[statepos].transparencygroup = !forSoftMask;
2226 states[statepos].isolated = isolated;
2228 states[statepos].olddevice = this->device;
2229 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2231 gfxdevice_record_init(this->device);
2233 /*if(!forSoftMask) { ////???
2234 state->setFillOpacity(0.0);
2239 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2242 dbg("endTransparencyGroup");
2243 msg("<verbose> endTransparencyGroup");
2245 gfxdevice_t*r = this->device;
2247 this->device = states[statepos].olddevice;
2249 if(states[statepos].createsoftmask) {
2250 states[statepos-1].softmaskrecording = r->finish(r);
2252 states[statepos-1].grouprecording = r->finish(r);
2255 states[statepos].createsoftmask = 0;
2256 states[statepos].transparencygroup = 0;
2260 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2262 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2263 "colordodge","colorburn","hardlight","softlight","difference",
2264 "exclusion","hue","saturation","color","luminosity"};
2266 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2267 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2269 if(state->getBlendMode() == gfxBlendNormal)
2270 infofeature("transparency groups");
2273 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2274 warnfeature(buffer, 0);
2277 gfxresult_t*grouprecording = states[statepos].grouprecording;
2279 if(state->getBlendMode() == gfxBlendNormal) {
2281 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2282 gfxresult_record_replay(grouprecording, &ops);
2285 grouprecording->destroy(grouprecording);
2287 states[statepos].grouprecording = 0;
2290 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2292 /* alpha = 1: retrieve mask values from alpha layer
2293 alpha = 0: retrieve mask values from luminance */
2294 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2295 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2296 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2297 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2299 infofeature("soft masks");
2301 warnfeature("soft masks from alpha channel",0);
2303 states[statepos].olddevice = this->device;
2304 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2305 gfxdevice_record_init(this->device);
2307 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2309 states[statepos].softmask = 1;
2310 states[statepos].softmask_alpha = alpha;
2313 static inline Guchar div255(int x) {
2314 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2317 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2319 if(c < min) c = min;
2320 if(c > max) c = max;
2324 void GFXOutputDev::clearSoftMask(GfxState *state)
2326 if(!states[statepos].softmask)
2328 states[statepos].softmask = 0;
2329 dbg("clearSoftMask statepos=%d", statepos);
2330 msg("<verbose> clearSoftMask");
2332 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2333 msg("<error> Error in softmask/tgroup ordering");
2337 gfxresult_t*mask = states[statepos].softmaskrecording;
2338 gfxresult_t*below = this->device->finish(this->device);
2339 this->device = states[statepos].olddevice;
2341 /* get outline of all objects below the soft mask */
2342 gfxdevice_t uniondev;
2343 gfxdevice_union_init(&uniondev, 0);
2344 gfxresult_record_replay(below, &uniondev);
2345 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2346 uniondev.finish(&uniondev);
2348 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2350 this->device->startclip(this->device, belowoutline);
2351 gfxresult_record_replay(below, this->device);
2352 gfxresult_record_replay(mask, this->device);
2353 this->device->endclip(this->device);
2354 gfxline_free(belowoutline);
2357 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2358 if(width<=0 || height<=0)
2361 gfxdevice_t belowrender;
2362 gfxdevice_render_init(&belowrender);
2363 if(states[statepos+1].isolated) {
2364 belowrender.setparameter(&belowrender, "fillwhite", "1");
2366 belowrender.setparameter(&belowrender, "antialize", "2");
2367 belowrender.startpage(&belowrender, width, height);
2368 gfxresult_record_replay(below, &belowrender);
2369 belowrender.endpage(&belowrender);
2370 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2371 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2372 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2374 gfxdevice_t maskrender;
2375 gfxdevice_render_init(&maskrender);
2376 maskrender.startpage(&maskrender, width, height);
2377 gfxresult_record_replay(mask, &maskrender);
2378 maskrender.endpage(&maskrender);
2379 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2380 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2382 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2383 msg("<fatal> Internal error in mask drawing");
2388 for(y=0;y<height;y++) {
2389 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2390 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2391 for(x=0;x<width;x++) {
2393 if(states[statepos].softmask_alpha) {
2396 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2399 l2->a = div255(alpha*l2->a);
2401 /* DON'T premultiply alpha- this is done by fillbitmap,
2402 depending on the output device */
2403 //l2->r = div255(alpha*l2->r);
2404 //l2->g = div255(alpha*l2->g);
2405 //l2->b = div255(alpha*l2->b);
2411 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2414 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2415 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2417 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2419 mask->destroy(mask);
2420 below->destroy(below);
2421 maskresult->destroy(maskresult);
2422 belowresult->destroy(belowresult);
2423 states[statepos].softmaskrecording = 0;
2428 // public: ~MemCheck()
2430 // delete globalParams;globalParams=0;
2431 // Object::memCheck(stderr);
2432 // gMemReport(stderr);