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
88 static fontfile_t fonts[2048];
89 static int fontnum = 0;
93 static char* lastfontdir = 0;
104 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
105 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
106 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
107 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
108 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
109 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
110 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
111 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
112 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
113 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
114 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
115 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
116 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
117 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
120 static int verbose = 0;
121 static int dbgindent = 0;
122 static void dbg(const char*format, ...)
129 va_start(arglist, format);
130 vsprintf(buf, format, arglist);
133 while(l && buf[l-1]=='\n') {
138 int indent = dbgindent;
148 typedef struct _feature
151 struct _feature*next;
153 feature_t*featurewarnings = 0;
155 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
157 feature_t*f = featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = featurewarnings;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
294 GFXGlobalParams::GFXGlobalParams()
297 //setupBaseFonts(char *dir); //not tested yet
299 GFXGlobalParams::~GFXGlobalParams()
301 msg("<verbose> Performing cleanups");
303 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
304 if(pdf2t1map[t].fullfilename) {
305 unlinkfont(pdf2t1map[t].fullfilename);
309 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
311 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
313 char*name = fontName->getCString();
314 /* see if it is a pdf standard font */
316 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
317 if(!strcmp(name, pdf2t1map[t].pdffont)) {
318 if(!pdf2t1map[t].fullfilename) {
319 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
320 if(!pdf2t1map[t].fullfilename) {
321 msg("<error> Couldn't save default font- is the Temp Directory writable?");
323 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
326 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
327 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
332 int bestlen = 0x7fffffff;
333 const char*bestfilename = 0;
334 for(t=0;t<fontnum;t++) {
335 if(strstr(fonts[t].filename, name)) {
336 if(fonts[t].len < bestlen) {
337 bestlen = fonts[t].len;
338 bestfilename = fonts[t].filename;
343 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
344 dfp->t1.fileName = new GString(bestfilename);
347 return GlobalParams::getDisplayFont(fontName);
350 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
354 this->xref = doc->getXRef();
357 this->textmodeinfo = 0;
360 this->type3active = 0;
363 this->substitutepos = 0;
364 this->type3Warning = 0;
365 this->user_movex = 0;
366 this->user_movey = 0;
369 this->user_clipx1 = 0;
370 this->user_clipy1 = 0;
371 this->user_clipx2 = 0;
372 this->user_clipy2 = 0;
373 this->current_text_stroke = 0;
374 this->current_text_clip = 0;
375 this->outer_clip_box = 0;
377 this->pagebuflen = 0;
379 this->config_break_on_warning=0;
380 this->config_remapunicode=0;
381 this->config_transparent=0;
382 this->config_extrafontdata = 0;
383 this->config_fontquality = 10;
385 this->gfxfontlist = gfxfontlist_create();
387 memset(states, 0, sizeof(states));
390 void GFXOutputDev::setParameter(const char*key, const char*value)
392 if(!strcmp(key,"breakonwarning")) {
393 this->config_break_on_warning = atoi(value);
394 } else if(!strcmp(key,"remapunicode")) {
395 this->config_remapunicode = atoi(value);
396 } else if(!strcmp(key,"transparent")) {
397 this->config_transparent = atoi(value);
398 } else if(!strcmp(key,"extrafontdata")) {
399 this->config_extrafontdata = atoi(value);
400 } else if(!strcmp(key,"fontquality")) {
401 this->config_fontquality = atof(value);
402 if(this->config_fontquality<=1)
403 this->config_fontquality=1;
404 } else if(!strcmp(key,"help")) {
405 printf("\nPDF layer options:\n");
406 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
407 printf(" are not 100%% supported\n");
408 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
409 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
410 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
415 void GFXOutputDev::setDevice(gfxdevice_t*dev)
420 void GFXOutputDev::setMove(int x,int y)
422 this->user_movex = x;
423 this->user_movey = y;
426 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
428 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
429 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
431 this->user_clipx1 = x1;
432 this->user_clipy1 = y1;
433 this->user_clipx2 = x2;
434 this->user_clipy2 = y2;
437 static char*getFontName(GfxFont*font)
440 GString*gstr = font->getName();
441 char* fname = gstr==0?0:gstr->getCString();
445 sprintf(buf, "UFONT%d", r->num);
446 fontid = strdup(buf);
448 fontid = strdup(fname);
452 char* plus = strchr(fontid, '+');
453 if(plus && plus < &fontid[strlen(fontid)-1]) {
454 fontname = strdup(plus+1);
456 fontname = strdup(fontid);
462 static void dumpFontInfo(const char*loglevel, GfxFont*font);
463 static int lastdumps[1024];
464 static int lastdumppos = 0;
469 static void showFontError(GfxFont*font, int nr)
473 for(t=0;t<lastdumppos;t++)
474 if(lastdumps[t] == r->num)
478 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
479 lastdumps[lastdumppos++] = r->num;
481 msg("<warning> The following font caused problems:");
483 msg("<warning> The following font caused problems (substituting):");
485 msg("<warning> The following Type 3 Font will be rendered as graphics:");
486 dumpFontInfo("<warning>", font);
489 static void dumpFontInfo(const char*loglevel, GfxFont*font)
491 char* id = getFontID(font);
492 char* name = getFontName(font);
493 Ref* r=font->getID();
494 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
496 GString*gstr = font->getTag();
498 msg("%s| Tag: %s\n", loglevel, id);
500 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
502 GfxFontType type=font->getType();
504 case fontUnknownType:
505 msg("%s| Type: unknown\n",loglevel);
508 msg("%s| Type: 1\n",loglevel);
511 msg("%s| Type: 1C\n",loglevel);
514 msg("%s| Type: 3\n",loglevel);
517 msg("%s| Type: TrueType\n",loglevel);
520 msg("%s| Type: CIDType0\n",loglevel);
523 msg("%s| Type: CIDType0C\n",loglevel);
526 msg("%s| Type: CIDType2\n",loglevel);
531 GBool embedded = font->getEmbeddedFontID(&embRef);
533 if(font->getEmbeddedFontName()) {
534 embeddedName = font->getEmbeddedFontName()->getCString();
537 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
539 gstr = font->getExtFontFile();
541 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
543 // Get font descriptor flags.
544 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
545 if(font->isSerif()) msg("%s| is serif\n", loglevel);
546 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
547 if(font->isItalic()) msg("%s| is italic\n", loglevel);
548 if(font->isBold()) msg("%s| is bold\n", loglevel);
554 //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");}
555 //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");}
557 void dump_outline(gfxline_t*line)
560 if(line->type == gfx_moveTo) {
561 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
562 } else if(line->type == gfx_lineTo) {
563 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
564 } else if(line->type == gfx_splineTo) {
565 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
571 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
573 int num = path->getNumSubpaths();
576 double lastx=0,lasty=0,posx=0,posy=0;
579 msg("<warning> empty path");
583 gfxdrawer_target_gfxline(&draw);
585 for(t = 0; t < num; t++) {
586 GfxSubpath *subpath = path->getSubpath(t);
587 int subnum = subpath->getNumPoints();
588 double bx=0,by=0,cx=0,cy=0;
590 for(s=0;s<subnum;s++) {
593 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
598 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
599 draw.lineTo(&draw, lastx, lasty);
601 draw.moveTo(&draw, x,y);
606 } else if(subpath->getCurve(s) && cpos==0) {
610 } else if(subpath->getCurve(s) && cpos==1) {
618 draw.lineTo(&draw, x,y);
620 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
627 /* fix non-closed lines */
628 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
629 draw.lineTo(&draw, lastx, lasty);
631 gfxline_t*result = (gfxline_t*)draw.result(&draw);
633 gfxline_optimize(result);
638 GBool GFXOutputDev::useTilingPatternFill()
640 infofeature("tiled patterns");
644 GBool GFXOutputDev::useShadedFills()
646 infofeature("shaded fills");
650 GBool GFXOutputDev::useDrawForm()
652 infofeature("forms");
655 void GFXOutputDev::drawForm(Ref id)
657 msg("<error> drawForm not implemented");
659 GBool GFXOutputDev::needNonText()
663 void GFXOutputDev::endPage()
665 msg("<verbose> endPage");
667 device->endclip(device);
672 #define STROKE_FILL 1
673 #define STROKE_CLIP 2
674 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
676 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
677 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
678 double miterLimit = state->getMiterLimit();
679 double width = state->getTransformedLineWidth();
682 double opaq = state->getStrokeOpacity();
684 state->getFillRGB(&rgb);
686 state->getStrokeRGB(&rgb);
688 col.r = colToByte(rgb.r);
689 col.g = colToByte(rgb.g);
690 col.b = colToByte(rgb.b);
691 col.a = (unsigned char)(opaq*255);
693 gfx_capType capType = gfx_capRound;
694 if(lineCap == 0) capType = gfx_capButt;
695 else if(lineCap == 1) capType = gfx_capRound;
696 else if(lineCap == 2) capType = gfx_capSquare;
698 gfx_joinType joinType = gfx_joinRound;
699 if(lineJoin == 0) joinType = gfx_joinMiter;
700 else if(lineJoin == 1) joinType = gfx_joinRound;
701 else if(lineJoin == 2) joinType = gfx_joinBevel;
704 double dashphase = 0;
706 state->getLineDash(&ldash, &dashnum, &dashphase);
710 if(dashnum && ldash) {
711 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
713 msg("<trace> %d dashes", dashnum);
714 msg("<trace> | phase: %f", dashphase);
715 for(t=0;t<dashnum;t++) {
716 dash[t] = (float)ldash[t];
717 msg("<trace> | d%-3d: %f", t, ldash[t]);
720 if(getLogLevel() >= LOGLEVEL_TRACE) {
724 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
727 msg("<trace> After dashing:");
730 if(getLogLevel() >= LOGLEVEL_TRACE) {
731 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
733 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
734 lineCap==0?"butt": (lineJoin==1?"round":"square"),
736 col.r,col.g,col.b,col.a
741 if(flags&STROKE_FILL) {
742 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
743 gfxline_t*gfxline = SVPtogfxline(svp);
744 if(getLogLevel() >= LOGLEVEL_TRACE) {
745 dump_outline(gfxline);
748 msg("<warning> Empty polygon (resulting from stroked line)");
750 if(flags&STROKE_CLIP) {
751 device->startclip(device, gfxline);
752 states[statepos].clipping++;
754 device->fill(device, gfxline, &col);
759 if(flags&STROKE_CLIP)
760 msg("<error> Stroke&clip not supported at the same time");
761 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
768 gfxcolor_t getFillColor(GfxState * state)
771 double opaq = state->getFillOpacity();
772 state->getFillRGB(&rgb);
774 col.r = colToByte(rgb.r);
775 col.g = colToByte(rgb.g);
776 col.b = colToByte(rgb.b);
777 col.a = (unsigned char)(opaq*255);
781 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
783 gfxcolor_t col = getFillColor(state);
785 if(getLogLevel() >= LOGLEVEL_TRACE) {
786 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
789 device->fill(device, line, &col);
792 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
794 if(getLogLevel() >= LOGLEVEL_TRACE) {
795 msg("<trace> clip\n");
799 device->startclip(device, line);
800 states[statepos].clipping++;
803 void GFXOutputDev::clip(GfxState *state)
805 GfxPath * path = state->getPath();
806 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
807 clipToGfxLine(state, line);
811 void GFXOutputDev::eoClip(GfxState *state)
813 GfxPath * path = state->getPath();
814 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
816 if(getLogLevel() >= LOGLEVEL_TRACE) {
817 msg("<trace> eoclip\n");
821 device->startclip(device, line);
822 states[statepos].clipping++;
825 void GFXOutputDev::clipToStrokePath(GfxState *state)
827 GfxPath * path = state->getPath();
828 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
830 if(getLogLevel() >= LOGLEVEL_TRACE) {
831 msg("<trace> cliptostrokepath\n");
835 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
839 void GFXOutputDev::finish()
843 device->endclip(device);
849 GFXOutputDev::~GFXOutputDev()
854 free(this->pages); this->pages = 0;
857 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
859 GBool GFXOutputDev::upsideDown()
863 GBool GFXOutputDev::useDrawChar()
868 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
869 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
871 static char tmp_printstr[4096];
872 char* makeStringPrintable(char*str)
874 int len = strlen(str);
889 tmp_printstr[len++] = '.';
890 tmp_printstr[len++] = '.';
891 tmp_printstr[len++] = '.';
893 tmp_printstr[len] = 0;
896 #define INTERNAL_FONT_SIZE 1024.0
897 void GFXOutputDev::updateFontMatrix(GfxState*state)
899 double* ctm = state->getCTM();
900 double fontSize = state->getFontSize();
901 double*textMat = state->getTextMat();
903 /* taking the absolute value of horizScaling seems to be required for
904 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
905 double hscale = fabs(state->getHorizScaling());
907 // from xpdf-3.02/SplashOutputDev:updateFont
908 double mm11 = textMat[0] * fontSize * hscale;
909 double mm12 = textMat[1] * fontSize * hscale;
910 double mm21 = textMat[2] * fontSize;
911 double mm22 = textMat[3] * fontSize;
913 // multiply with ctm, like state->getFontTransMat() does
914 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
915 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
916 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
917 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
918 this->current_font_matrix.tx = 0;
919 this->current_font_matrix.ty = 0;
922 void GFXOutputDev::beginString(GfxState *state, GString *s)
924 int render = state->getRender();
925 if(current_text_stroke) {
926 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
929 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
932 static gfxline_t* mkEmptyGfxShape(double x, double y)
934 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
935 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
939 static char isValidUnicode(int c)
941 if(c>=32 && c<0x2fffe)
946 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
947 double dx, double dy,
948 double originX, double originY,
949 CharCode charid, int nBytes, Unicode *_u, int uLen)
951 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
952 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
956 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
958 int render = state->getRender();
959 gfxcolor_t col = getFillColor(state);
961 // check for invisible text -- this is used by Acrobat Capture
962 if (render == RENDER_INVISIBLE) {
964 if(!config_extrafontdata)
968 GfxFont*font = state->getFont();
970 if(font->getType() == fontType3) {
971 /* type 3 chars are passed as graphics */
972 msg("<debug> type3 char at %f/%f", x, y);
976 Unicode u = uLen?(_u[0]):0;
977 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);
979 gfxmatrix_t m = this->current_font_matrix;
980 state->transform(x, y, &m.tx, &m.ty);
981 m.tx += user_movex + clipmovex;
982 m.ty += user_movey + clipmovey;
984 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
985 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
987 msg("<debug> Drawing glyph %d as shape", charid);
989 msg("<notice> Some texts will be rendered as shape");
992 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
993 gfxline_t*tglyph = gfxline_clone(glyph);
994 gfxline_transform(tglyph, &m);
995 if((render&3) != RENDER_INVISIBLE) {
996 gfxline_t*add = gfxline_clone(tglyph);
997 current_text_stroke = gfxline_append(current_text_stroke, add);
999 if(render&RENDER_CLIP) {
1000 gfxline_t*add = gfxline_clone(tglyph);
1001 current_text_clip = gfxline_append(current_text_clip, add);
1002 if(!current_text_clip) {
1003 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1006 gfxline_free(tglyph);
1010 void GFXOutputDev::endString(GfxState *state)
1012 int render = state->getRender();
1013 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1015 if(current_text_stroke) {
1016 /* fillstroke and stroke text rendering objects we can process right
1017 now (as there may be texts of other rendering modes in this
1018 text object)- clipping objects have to wait until endTextObject,
1020 device->setparameter(device, "mark","TXT");
1021 if((render&3) == RENDER_FILL) {
1022 fillGfxLine(state, current_text_stroke);
1023 gfxline_free(current_text_stroke);
1024 current_text_stroke = 0;
1025 } else if((render&3) == RENDER_FILLSTROKE) {
1026 fillGfxLine(state, current_text_stroke);
1027 strokeGfxline(state, current_text_stroke,0);
1028 gfxline_free(current_text_stroke);
1029 current_text_stroke = 0;
1030 } else if((render&3) == RENDER_STROKE) {
1031 strokeGfxline(state, current_text_stroke,0);
1032 gfxline_free(current_text_stroke);
1033 current_text_stroke = 0;
1035 device->setparameter(device, "mark","");
1039 void GFXOutputDev::endTextObject(GfxState *state)
1041 int render = state->getRender();
1042 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1044 if(current_text_clip) {
1045 device->setparameter(device, "mark","TXT");
1046 clipToGfxLine(state, current_text_clip);
1047 device->setparameter(device, "mark","");
1048 gfxline_free(current_text_clip);
1049 current_text_clip = 0;
1053 /* the logic seems to be as following:
1054 first, beginType3Char is called, with the charcode and the coordinates.
1055 if this function returns true, it already knew about the char and has now drawn it.
1056 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1057 called with some parameters.
1058 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1059 at the position first passed to beginType3Char). the char ends with endType3Char.
1061 The drawing operations between beginType3Char and endType3Char are somewhat different to
1062 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1063 color determines the color of a font)
1066 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1068 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1071 if(config_extrafontdata && current_fontinfo) {
1073 gfxmatrix_t m = this->current_font_matrix;
1074 state->transform(0, 0, &m.tx, &m.ty);
1075 m.m00*=INTERNAL_FONT_SIZE;
1076 m.m01*=INTERNAL_FONT_SIZE;
1077 m.m10*=INTERNAL_FONT_SIZE;
1078 m.m11*=INTERNAL_FONT_SIZE;
1079 m.tx += user_movex + clipmovex;
1080 m.ty += user_movey + clipmovey;
1082 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1083 msg("<error> Invalid charid %d for font", charid);
1086 gfxcolor_t col={0,0,0,0};
1087 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1088 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1092 /* the character itself is going to be passed using the draw functions */
1093 return gFalse; /* gTrue= is_in_cache? */
1096 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1098 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1101 void GFXOutputDev::endType3Char(GfxState *state)
1104 msg("<debug> endType3Char");
1107 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1109 this->currentpage = pageNum;
1111 int rot = doc->getPageRotate(1);
1112 gfxcolor_t white = {255,255,255,255};
1113 gfxcolor_t black = {255,0,0,0};
1115 gfxline_t clippath[5];
1117 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1118 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1119 Use CropBox, not MediaBox, as page size
1126 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1127 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1129 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1130 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1132 this->clipmovex = -(int)x1;
1133 this->clipmovey = -(int)y1;
1135 /* apply user clip box */
1136 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1137 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1138 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1139 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1140 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1141 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1144 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1146 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);
1148 msg("<verbose> page is rotated %d degrees\n", rot);
1150 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1151 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1152 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1153 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1154 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1155 device->startclip(device, clippath); outer_clip_box = 1;
1156 if(!config_transparent) {
1157 device->fill(device, clippath, &white);
1162 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1164 double x1, y1, x2, y2;
1165 gfxline_t points[5];
1168 msg("<debug> drawlink\n");
1170 link->getRect(&x1, &y1, &x2, &y2);
1171 cvtUserToDev(x1, y1, &x, &y);
1172 points[0].type = gfx_moveTo;
1173 points[0].x = points[4].x = x + user_movex + clipmovex;
1174 points[0].y = points[4].y = y + user_movey + clipmovey;
1175 points[0].next = &points[1];
1176 cvtUserToDev(x2, y1, &x, &y);
1177 points[1].type = gfx_lineTo;
1178 points[1].x = x + user_movex + clipmovex;
1179 points[1].y = y + user_movey + clipmovey;
1180 points[1].next = &points[2];
1181 cvtUserToDev(x2, y2, &x, &y);
1182 points[2].type = gfx_lineTo;
1183 points[2].x = x + user_movex + clipmovex;
1184 points[2].y = y + user_movey + clipmovey;
1185 points[2].next = &points[3];
1186 cvtUserToDev(x1, y2, &x, &y);
1187 points[3].type = gfx_lineTo;
1188 points[3].x = x + user_movex + clipmovex;
1189 points[3].y = y + user_movey + clipmovey;
1190 points[3].next = &points[4];
1191 cvtUserToDev(x1, y1, &x, &y);
1192 points[4].type = gfx_lineTo;
1193 points[4].x = x + user_movex + clipmovex;
1194 points[4].y = y + user_movey + clipmovey;
1197 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1198 points[0].x, points[0].y,
1199 points[1].x, points[1].y,
1200 points[2].x, points[2].y,
1201 points[3].x, points[3].y);
1203 LinkAction*action=link->getAction();
1206 const char*type = "-?-";
1209 msg("<trace> drawlink action=%d\n", action->getKind());
1210 switch(action->getKind())
1214 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1215 LinkDest *dest=NULL;
1216 if (ha->getDest()==NULL)
1217 dest=catalog->findDest(ha->getNamedDest());
1218 else dest=ha->getDest();
1220 if (dest->isPageRef()){
1221 Ref pageref=dest->getPageRef();
1222 page=catalog->findPage(pageref.num,pageref.gen);
1224 else page=dest->getPageNum();
1225 sprintf(buf, "%d", page);
1232 LinkGoToR*l = (LinkGoToR*)action;
1233 GString*g = l->getFileName();
1235 s = strdup(g->getCString());
1237 /* if the GoToR link has no filename, then
1238 try to find a refernce in the *local*
1240 GString*g = l->getNamedDest();
1242 s = strdup(g->getCString());
1248 LinkNamed*l = (LinkNamed*)action;
1249 GString*name = l->getName();
1251 s = strdup(name->lowerCase()->getCString());
1252 named = name->getCString();
1255 if(strstr(s, "next") || strstr(s, "forward"))
1257 page = currentpage + 1;
1259 else if(strstr(s, "prev") || strstr(s, "back"))
1261 page = currentpage - 1;
1263 else if(strstr(s, "last") || strstr(s, "end"))
1265 if(pages && pagepos>0)
1266 page = pages[pagepos-1];
1268 else if(strstr(s, "first") || strstr(s, "top"))
1276 case actionLaunch: {
1278 LinkLaunch*l = (LinkLaunch*)action;
1279 GString * str = new GString(l->getFileName());
1280 GString * params = l->getParams();
1282 str->append(params);
1283 s = strdup(str->getCString());
1290 LinkURI*l = (LinkURI*)action;
1291 GString*g = l->getURI();
1293 url = g->getCString();
1298 case actionUnknown: {
1300 LinkUnknown*l = (LinkUnknown*)action;
1305 msg("<error> Unknown link type!\n");
1310 if(!s) s = strdup("-?-");
1312 msg("<trace> drawlink s=%s\n", s);
1314 if(!linkinfo && (page || s))
1316 msg("<notice> File contains links");
1324 for(t=1;t<=pagepos;t++) {
1325 if(pages[t]==page) {
1334 sprintf(buf, "page%d", lpage);
1335 device->drawlink(device, points, buf);
1339 device->drawlink(device, points, s);
1342 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1346 void GFXOutputDev::saveState(GfxState *state) {
1347 dbg("saveState");dbgindent+=2;
1349 msg("<trace> saveState\n");
1352 msg("<error> Too many nested states in pdf.");
1356 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1357 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1358 states[statepos].clipping = 0;
1361 void GFXOutputDev::restoreState(GfxState *state) {
1362 dbgindent-=2; dbg("restoreState");
1365 msg("<error> Invalid restoreState");
1368 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1369 states[statepos].clipping?" (end clipping)":"");
1370 if(states[statepos].softmask) {
1371 clearSoftMask(state);
1374 while(states[statepos].clipping) {
1375 device->endclip(device);
1376 states[statepos].clipping--;
1381 void GFXOutputDev::updateLineWidth(GfxState *state)
1383 double width = state->getTransformedLineWidth();
1384 //swfoutput_setlinewidth(&device, width);
1387 void GFXOutputDev::updateLineCap(GfxState *state)
1389 int c = state->getLineCap();
1392 void GFXOutputDev::updateLineJoin(GfxState *state)
1394 int j = state->getLineJoin();
1397 void GFXOutputDev::updateFillColor(GfxState *state)
1400 double opaq = state->getFillOpacity();
1401 state->getFillRGB(&rgb);
1403 void GFXOutputDev::updateFillOpacity(GfxState *state)
1406 double opaq = state->getFillOpacity();
1407 state->getFillRGB(&rgb);
1408 dbg("update fillopaq %f", opaq);
1410 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1412 double opaq = state->getFillOpacity();
1413 dbg("update strokeopaq %f", opaq);
1415 void GFXOutputDev::updateFillOverprint(GfxState *state)
1417 double opaq = state->getFillOverprint();
1418 dbg("update filloverprint %f", opaq);
1420 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1422 double opaq = state->getStrokeOverprint();
1423 dbg("update strokeoverprint %f", opaq);
1425 void GFXOutputDev::updateTransfer(GfxState *state)
1427 dbg("update transfer");
1431 void GFXOutputDev::updateStrokeColor(GfxState *state)
1434 double opaq = state->getStrokeOpacity();
1435 state->getStrokeRGB(&rgb);
1439 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1441 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1442 memset(font, 0, sizeof(gfxfont_t));
1444 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1445 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1446 font->id = strdup(getFontID(xpdffont));
1449 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1451 //printf("%d glyphs\n", font->num_glyphs);
1452 font->num_glyphs = 0;
1453 for(t=0;t<src->num_glyphs;t++) {
1454 if(src->glyphs[t]) {
1455 SplashPath*path = src->glyphs[t]->path;
1456 int len = path?path->getLength():0;
1457 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1458 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1459 src->glyphs[t]->glyphid = font->num_glyphs;
1460 glyph->unicode = src->glyphs[t]->unicode;
1461 if(glyph->unicode >= font->max_unicode)
1462 font->max_unicode = glyph->unicode+1;
1464 gfxdrawer_target_gfxline(&drawer);
1468 for(s=0;s<len;s++) {
1471 path->getPoint(s, &x, &y, &f);
1474 if(f&splashPathFirst) {
1475 drawer.moveTo(&drawer, x*scale, y*scale);
1477 if(f&splashPathCurve) {
1479 path->getPoint(++s, &x2, &y2, &f);
1480 if(f&splashPathCurve) {
1482 path->getPoint(++s, &x3, &y3, &f);
1483 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1485 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1488 drawer.lineTo(&drawer, x*scale, y*scale);
1490 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1491 // (f&splashPathFirst)?"first":"",
1492 // (f&splashPathLast)?"last":"");
1494 glyph->line = (gfxline_t*)drawer.result(&drawer);
1495 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1499 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1500 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1501 for(t=0;t<font->num_glyphs;t++) {
1502 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1503 font->unicode2glyph[font->glyphs[t].unicode] = t;
1507 msg("<trace> %d glyphs.", t, font->num_glyphs);
1511 void GFXOutputDev::updateFont(GfxState *state)
1513 GfxFont* gfxFont = state->getFont();
1517 char*id = getFontID(gfxFont);
1518 msg("<verbose> Updating font to %s", id);
1519 if(gfxFont->getType() == fontType3) {
1520 infofeature("Type3 fonts");
1521 if(!config_extrafontdata) {
1526 msg("<error> Internal Error: FontID is null");
1530 this->current_fontinfo = this->info->getFont(id);
1531 if(!this->current_fontinfo) {
1532 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1535 if(!this->current_fontinfo->seen) {
1536 dumpFontInfo("<verbose>", gfxFont);
1539 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1541 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1542 font->id = strdup(id);
1543 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1544 device->addfont(device, font);
1546 current_gfxfont = font;
1549 updateFontMatrix(state);
1552 #define SQR(x) ((x)*(x))
1554 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1556 if((newwidth<2 || newheight<2) ||
1557 (width<=newwidth || height<=newheight))
1559 unsigned char*newdata;
1561 newdata= (unsigned char*)malloc(newwidth*newheight);
1562 double fx = (double)(width)/newwidth;
1563 double fy = (double)(height)/newheight;
1565 int blocksize = (int)(8192/(fx*fy));
1566 int r = 8192*256/palettesize;
1567 for(x=0;x<newwidth;x++) {
1568 double ex = px + fx;
1569 int fromx = (int)px;
1571 int xweight1 = (int)(((fromx+1)-px)*256);
1572 int xweight2 = (int)((ex-tox)*256);
1574 for(y=0;y<newheight;y++) {
1575 double ey = py + fy;
1576 int fromy = (int)py;
1578 int yweight1 = (int)(((fromy+1)-py)*256);
1579 int yweight2 = (int)((ey-toy)*256);
1582 for(xx=fromx;xx<=tox;xx++)
1583 for(yy=fromy;yy<=toy;yy++) {
1584 int b = 1-data[width*yy+xx];
1586 if(xx==fromx) weight = (weight*xweight1)/256;
1587 if(xx==tox) weight = (weight*xweight2)/256;
1588 if(yy==fromy) weight = (weight*yweight1)/256;
1589 if(yy==toy) weight = (weight*yweight2)/256;
1592 //if(a) a=(palettesize-1)*r/blocksize;
1593 newdata[y*newwidth+x] = (a*blocksize)/r;
1601 #define IMAGE_TYPE_JPEG 0
1602 #define IMAGE_TYPE_LOSSLESS 1
1604 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1605 double x1,double y1,
1606 double x2,double y2,
1607 double x3,double y3,
1608 double x4,double y4, int type)
1610 gfxcolor_t*newpic=0;
1612 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1613 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1615 gfxline_t p1,p2,p3,p4,p5;
1616 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1617 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1618 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1619 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1620 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1622 {p1.x = (int)(p1.x*20)/20.0;
1623 p1.y = (int)(p1.y*20)/20.0;
1624 p2.x = (int)(p2.x*20)/20.0;
1625 p2.y = (int)(p2.y*20)/20.0;
1626 p3.x = (int)(p3.x*20)/20.0;
1627 p3.y = (int)(p3.y*20)/20.0;
1628 p4.x = (int)(p4.x*20)/20.0;
1629 p4.y = (int)(p4.y*20)/20.0;
1630 p5.x = (int)(p5.x*20)/20.0;
1631 p5.y = (int)(p5.y*20)/20.0;
1635 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1636 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1641 img.data = (gfxcolor_t*)data;
1645 if(type == IMAGE_TYPE_JPEG)
1646 /* TODO: pass image_dpi to device instead */
1647 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1649 dev->fillbitmap(dev, &p1, &img, &m, 0);
1652 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1653 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1655 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1658 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1659 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1661 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1665 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1666 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1667 GBool inlineImg, int mask, int*maskColors,
1668 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1670 double x1,y1,x2,y2,x3,y3,x4,y4;
1671 ImageStream *imgStr;
1676 unsigned char* maskbitmap = 0;
1679 ncomps = colorMap->getNumPixelComps();
1680 bits = colorMap->getBits();
1685 unsigned char buf[8];
1686 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1688 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1689 imgMaskStr->reset();
1690 unsigned char pal[256];
1691 int n = 1 << colorMap->getBits();
1696 maskColorMap->getGray(pixBuf, &gray);
1697 pal[t] = colToByte(gray);
1699 for (y = 0; y < maskHeight; y++) {
1700 for (x = 0; x < maskWidth; x++) {
1701 imgMaskStr->getPixel(buf);
1702 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1707 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1708 imgMaskStr->reset();
1709 for (y = 0; y < maskHeight; y++) {
1710 for (x = 0; x < maskWidth; x++) {
1711 imgMaskStr->getPixel(buf);
1713 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1721 imgStr = new ImageStream(str, width, ncomps,bits);
1724 if(!width || !height || (height<=1 && width<=1))
1726 msg("<verbose> Ignoring %d by %d image", width, height);
1727 unsigned char buf[8];
1729 for (y = 0; y < height; ++y)
1730 for (x = 0; x < width; ++x) {
1731 imgStr->getPixel(buf);
1739 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1740 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1741 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1742 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1744 if(!pbminfo && !(str->getKind()==strDCT)) {
1746 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1750 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1752 if(!jpeginfo && (str->getKind()==strDCT)) {
1753 msg("<notice> file contains jpeg pictures");
1758 unsigned char buf[8];
1760 unsigned char*pic = new unsigned char[width*height];
1761 gfxcolor_t pal[256];
1763 state->getFillRGB(&rgb);
1765 memset(pal,255,sizeof(pal));
1766 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1767 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1768 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1769 pal[0].a = 255; pal[1].a = 0;
1772 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1773 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1774 for (y = 0; y < height; ++y)
1775 for (x = 0; x < width; ++x)
1777 imgStr->getPixel(buf);
1780 pic[width*y+x] = buf[0];
1783 /* the size of the drawn image is added to the identifier
1784 as the same image may require different bitmaps if displayed
1785 at different sizes (due to antialiasing): */
1788 unsigned char*pic2 = 0;
1791 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1800 height = realheight;
1804 /* make a black/white palette */
1806 float r = 255./(float)(numpalette-1);
1808 for(t=0;t<numpalette;t++) {
1809 pal[t].r = colToByte(rgb.r);
1810 pal[t].g = colToByte(rgb.g);
1811 pal[t].b = colToByte(rgb.b);
1812 pal[t].a = (unsigned char)(t*r);
1816 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1817 for (y = 0; y < height; ++y) {
1818 for (x = 0; x < width; ++x) {
1819 pic2[width*y+x] = pal[pic[y*width+x]];
1822 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1826 if(maskbitmap) free(maskbitmap);
1832 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1833 gfxcolor_t*pic=new gfxcolor_t[width*height];
1834 for (y = 0; y < height; ++y) {
1835 for (x = 0; x < width; ++x) {
1836 imgStr->getPixel(pixBuf);
1837 colorMap->getRGB(pixBuf, &rgb);
1838 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1839 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1840 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1841 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1843 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1847 if(str->getKind()==strDCT)
1848 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1850 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1853 if(maskbitmap) free(maskbitmap);
1856 gfxcolor_t*pic=new gfxcolor_t[width*height];
1857 gfxcolor_t pal[256];
1858 int n = 1 << colorMap->getBits();
1860 for(t=0;t<256;t++) {
1862 colorMap->getRGB(pixBuf, &rgb);
1864 {/*if(maskColors && *maskColors==t) {
1865 msg("<notice> Color %d is transparent", t);
1866 if (imgData->maskColors) {
1868 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1869 if (pix[i] < imgData->maskColors[2*i] ||
1870 pix[i] > imgData->maskColors[2*i+1]) {
1885 pal[t].r = (unsigned char)(colToByte(rgb.r));
1886 pal[t].g = (unsigned char)(colToByte(rgb.g));
1887 pal[t].b = (unsigned char)(colToByte(rgb.b));
1888 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1891 for (y = 0; y < height; ++y) {
1892 for (x = 0; x < width; ++x) {
1893 imgStr->getPixel(pixBuf);
1894 pic[width*y+x] = pal[pixBuf[0]];
1896 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1900 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1904 if(maskbitmap) free(maskbitmap);
1909 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1910 int width, int height, GBool invert,
1913 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1914 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1915 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1918 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1919 int width, int height, GfxImageColorMap *colorMap,
1920 int *maskColors, GBool inlineImg)
1922 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1923 colorMap?"colorMap":"no colorMap",
1924 maskColors?"maskColors":"no maskColors",
1926 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1927 colorMap?"colorMap":"no colorMap",
1928 maskColors?"maskColors":"no maskColors",
1931 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1932 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1933 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1936 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1937 int width, int height,
1938 GfxImageColorMap *colorMap,
1939 Stream *maskStr, int maskWidth, int maskHeight,
1942 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1943 colorMap?"colorMap":"no colorMap",
1944 maskWidth, maskHeight);
1945 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1946 colorMap?"colorMap":"no colorMap",
1947 maskWidth, maskHeight);
1949 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1950 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1951 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1954 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1955 int width, int height,
1956 GfxImageColorMap *colorMap,
1958 int maskWidth, int maskHeight,
1959 GfxImageColorMap *maskColorMap)
1961 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1962 colorMap?"colorMap":"no colorMap",
1963 maskWidth, maskHeight);
1964 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1965 colorMap?"colorMap":"no colorMap",
1966 maskWidth, maskHeight);
1968 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1969 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1970 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1973 void GFXOutputDev::stroke(GfxState *state)
1977 GfxPath * path = state->getPath();
1978 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1979 strokeGfxline(state, line, 0);
1983 void GFXOutputDev::fill(GfxState *state)
1985 gfxcolor_t col = getFillColor(state);
1986 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1988 GfxPath * path = state->getPath();
1989 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1990 fillGfxLine(state, line);
1994 void GFXOutputDev::eoFill(GfxState *state)
1996 gfxcolor_t col = getFillColor(state);
1997 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1999 GfxPath * path = state->getPath();
2000 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2001 fillGfxLine(state, line);
2006 static const char* dirseparator()
2015 void addGlobalFont(const char*filename)
2018 memset(&f, 0, sizeof(fontfile_t));
2019 f.filename = filename;
2020 int len = strlen(filename);
2021 char*r1 = strrchr(filename, '/');
2022 char*r2 = strrchr(filename, '\\');
2029 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2030 msg("<notice> Adding font \"%s\".", filename);
2031 fonts[fontnum++] = f;
2033 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2037 void addGlobalLanguageDir(const char*dir)
2039 msg("<notice> Adding %s to language pack directories", dir);
2042 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2043 strcpy(config_file, dir);
2044 strcat(config_file, dirseparator());
2045 strcat(config_file, "add-to-xpdfrc");
2047 fi = fopen(config_file, "rb");
2049 msg("<error> Could not open %s", config_file);
2052 globalParams->parseFile(new GString(config_file), fi);
2056 void addGlobalFontDir(const char*dirname)
2058 #ifdef HAVE_DIRENT_H
2059 msg("<notice> Adding %s to font directories", dirname);
2060 lastfontdir = strdup(dirname);
2061 DIR*dir = opendir(dirname);
2063 msg("<warning> Couldn't open directory %s\n", dirname);
2068 ent = readdir (dir);
2072 char*name = ent->d_name;
2078 if(!strncasecmp(&name[l-4], ".pfa", 4))
2080 if(!strncasecmp(&name[l-4], ".pfb", 4))
2082 if(!strncasecmp(&name[l-4], ".ttf", 4))
2085 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2086 strcpy(fontname, dirname);
2087 strcat(fontname, dirseparator());
2088 strcat(fontname, name);
2089 addGlobalFont(fontname);
2094 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2098 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2104 this->pagebuflen = 1024;
2105 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2106 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2108 while(pdfpage >= this->pagebuflen)
2110 int oldlen = this->pagebuflen;
2111 this->pagebuflen+=1024;
2112 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2113 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2116 this->pages[pdfpage] = outputpage;
2117 if(pdfpage>this->pagepos)
2118 this->pagepos = pdfpage;
2124 double width,height;
2127 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2129 double xMin, yMin, xMax, yMax, x, y;
2130 double tx, ty, w, h;
2131 // transform the bbox
2132 state->transform(bbox[0], bbox[1], &x, &y);
2135 state->transform(bbox[0], bbox[3], &x, &y);
2138 } else if (x > xMax) {
2143 } else if (y > yMax) {
2146 state->transform(bbox[2], bbox[1], &x, &y);
2149 } else if (x > xMax) {
2154 } else if (y > yMax) {
2157 state->transform(bbox[2], bbox[3], &x, &y);
2160 } else if (x > xMax) {
2165 } else if (y > yMax) {
2168 tx = (int)floor(xMin);
2171 } else if (tx > width) {
2174 ty = (int)floor(yMin);
2177 } else if (ty > height) {
2180 w = (int)ceil(xMax) - tx + 1;
2181 if (tx + w > width) {
2187 h = (int)ceil(yMax) - ty + 1;
2188 if (ty + h > height) {
2202 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2203 GfxColorSpace *blendingColorSpace,
2204 GBool isolated, GBool knockout,
2207 const char*colormodename = "";
2209 if(blendingColorSpace) {
2210 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2212 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);
2213 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);
2215 states[statepos].createsoftmask |= forSoftMask;
2216 states[statepos].transparencygroup = !forSoftMask;
2217 states[statepos].isolated = isolated;
2219 states[statepos].olddevice = this->device;
2220 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2222 gfxdevice_record_init(this->device);
2224 /*if(!forSoftMask) { ////???
2225 state->setFillOpacity(0.0);
2230 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2233 dbg("endTransparencyGroup");
2234 msg("<verbose> endTransparencyGroup");
2236 gfxdevice_t*r = this->device;
2238 this->device = states[statepos].olddevice;
2240 if(states[statepos].createsoftmask) {
2241 states[statepos-1].softmaskrecording = r->finish(r);
2243 states[statepos-1].grouprecording = r->finish(r);
2246 states[statepos].createsoftmask = 0;
2247 states[statepos].transparencygroup = 0;
2251 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2253 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2254 "colordodge","colorburn","hardlight","softlight","difference",
2255 "exclusion","hue","saturation","color","luminosity"};
2257 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2258 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2260 if(state->getBlendMode() == gfxBlendNormal)
2261 infofeature("transparency groups");
2264 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2265 warnfeature(buffer, 0);
2268 gfxresult_t*grouprecording = states[statepos].grouprecording;
2270 if(state->getBlendMode() == gfxBlendNormal) {
2272 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2273 gfxresult_record_replay(grouprecording, &ops);
2276 grouprecording->destroy(grouprecording);
2278 states[statepos].grouprecording = 0;
2281 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2283 /* alpha = 1: retrieve mask values from alpha layer
2284 alpha = 0: retrieve mask values from luminance */
2285 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2286 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2287 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2288 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2290 infofeature("soft masks");
2292 warnfeature("soft masks from alpha channel",0);
2294 states[statepos].olddevice = this->device;
2295 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2296 gfxdevice_record_init(this->device);
2298 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2300 states[statepos].softmask = 1;
2301 states[statepos].softmask_alpha = alpha;
2304 static inline Guchar div255(int x) {
2305 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2308 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2310 if(c < min) c = min;
2311 if(c > max) c = max;
2315 void GFXOutputDev::clearSoftMask(GfxState *state)
2317 if(!states[statepos].softmask)
2319 states[statepos].softmask = 0;
2320 dbg("clearSoftMask statepos=%d", statepos);
2321 msg("<verbose> clearSoftMask");
2323 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2324 msg("<error> Error in softmask/tgroup ordering");
2328 gfxresult_t*mask = states[statepos].softmaskrecording;
2329 gfxresult_t*below = this->device->finish(this->device);
2330 this->device = states[statepos].olddevice;
2332 /* get outline of all objects below the soft mask */
2333 gfxdevice_t uniondev;
2334 gfxdevice_union_init(&uniondev, 0);
2335 gfxresult_record_replay(below, &uniondev);
2336 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2337 uniondev.finish(&uniondev);
2339 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2341 this->device->startclip(this->device, belowoutline);
2342 gfxresult_record_replay(below, this->device);
2343 gfxresult_record_replay(mask, this->device);
2344 this->device->endclip(this->device);
2345 gfxline_free(belowoutline);
2348 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2349 if(width<=0 || height<=0)
2352 gfxdevice_t belowrender;
2353 gfxdevice_render_init(&belowrender);
2354 if(states[statepos+1].isolated) {
2355 belowrender.setparameter(&belowrender, "fillwhite", "1");
2357 belowrender.setparameter(&belowrender, "antialize", "2");
2358 belowrender.startpage(&belowrender, width, height);
2359 gfxresult_record_replay(below, &belowrender);
2360 belowrender.endpage(&belowrender);
2361 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2362 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2363 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2365 gfxdevice_t maskrender;
2366 gfxdevice_render_init(&maskrender);
2367 maskrender.startpage(&maskrender, width, height);
2368 gfxresult_record_replay(mask, &maskrender);
2369 maskrender.endpage(&maskrender);
2370 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2371 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2373 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2374 msg("<fatal> Internal error in mask drawing");
2379 for(y=0;y<height;y++) {
2380 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2381 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2382 for(x=0;x<width;x++) {
2384 if(states[statepos].softmask_alpha) {
2387 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2390 l2->a = div255(alpha*l2->a);
2392 /* DON'T premultiply alpha- this is done by fillbitmap,
2393 depending on the output device */
2394 //l2->r = div255(alpha*l2->r);
2395 //l2->g = div255(alpha*l2->g);
2396 //l2->b = div255(alpha*l2->b);
2402 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2405 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2406 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2408 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2410 mask->destroy(mask);
2411 below->destroy(below);
2412 maskresult->destroy(maskresult);
2413 belowresult->destroy(belowresult);
2414 states[statepos].softmaskrecording = 0;
2419 // public: ~MemCheck()
2421 // delete globalParams;globalParams=0;
2422 // Object::memCheck(stderr);
2423 // gMemReport(stderr);