2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
26 #include "../../config.h"
31 #ifdef HAVE_SYS_STAT_H
34 #ifdef HAVE_FONTCONFIG
35 #include <fontconfig.h>
52 #include "OutputDev.h"
55 #include "CharCodeToUnicode.h"
56 #include "NameToUnicodeTable.h"
57 #include "GlobalParams.h"
58 #include "FoFiType1C.h"
59 #include "FoFiTrueType.h"
61 #include "GFXOutputDev.h"
63 //swftools header files
65 #include "../gfxdevice.h"
66 #include "../gfxtools.h"
67 #include "../gfxfont.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/arts.h"
71 #include "../devices/render.h"
73 #include "../art/libart.h"
74 #include "../devices/artsutils.c"
81 typedef struct _fontfile
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);
331 for(t=0;t<fontnum;t++) {
332 if(strstr(fonts[t].filename, name)) {
333 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
334 dfp->t1.fileName = new GString(fonts[t].filename);
338 return GlobalParams::getDisplayFont(fontName);
341 GFXOutputDev::GFXOutputDev(parameter_t*p)
344 this->textmodeinfo = 0;
347 this->type3active = 0;
350 this->substitutepos = 0;
351 this->type3Warning = 0;
352 this->user_movex = 0;
353 this->user_movey = 0;
356 this->user_clipx1 = 0;
357 this->user_clipy1 = 0;
358 this->user_clipx2 = 0;
359 this->user_clipy2 = 0;
360 this->current_text_stroke = 0;
361 this->current_text_clip = 0;
362 this->outer_clip_box = 0;
364 this->pagebuflen = 0;
366 this->config_use_fontconfig=1;
367 this->config_break_on_warning=0;
368 this->config_remapunicode=0;
369 this->config_transparent=0;
370 this->config_extrafontdata = 0;
372 this->parameters = p;
374 this->gfxfontlist = gfxfontlist_create();
376 memset(states, 0, sizeof(states));
378 /* configure device */
380 setParameter(p->name, p->value);
385 void GFXOutputDev::setParameter(const char*key, const char*value)
387 if(!strcmp(key,"breakonwarning")) {
388 this->config_break_on_warning = atoi(value);
389 } else if(!strcmp(key,"fontconfig")) {
390 this->config_use_fontconfig = atoi(value);
391 } else if(!strcmp(key,"remapunicode")) {
392 this->config_remapunicode = atoi(value);
393 } else if(!strcmp(key,"transparent")) {
394 this->config_transparent = atoi(value);
395 } else if(!strcmp(key,"extrafontdata")) {
396 this->config_extrafontdata = atoi(value);
400 void GFXOutputDev::setDevice(gfxdevice_t*dev)
402 parameter_t*p = this->parameters;
404 /* pass parameters to output device */
408 this->device->setparameter(this->device, p->name, p->value);
414 void GFXOutputDev::setMove(int x,int y)
416 this->user_movex = x;
417 this->user_movey = y;
420 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
422 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
423 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
425 this->user_clipx1 = x1;
426 this->user_clipy1 = y1;
427 this->user_clipx2 = x2;
428 this->user_clipy2 = y2;
431 static char*getFontName(GfxFont*font)
434 GString*gstr = font->getName();
435 char* fname = gstr==0?0:gstr->getCString();
439 sprintf(buf, "UFONT%d", r->num);
440 fontid = strdup(buf);
442 fontid = strdup(fname);
446 char* plus = strchr(fontid, '+');
447 if(plus && plus < &fontid[strlen(fontid)-1]) {
448 fontname = strdup(plus+1);
450 fontname = strdup(fontid);
456 static void dumpFontInfo(const char*loglevel, GfxFont*font);
457 static int lastdumps[1024];
458 static int lastdumppos = 0;
463 static void showFontError(GfxFont*font, int nr)
467 for(t=0;t<lastdumppos;t++)
468 if(lastdumps[t] == r->num)
472 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
473 lastdumps[lastdumppos++] = r->num;
475 msg("<warning> The following font caused problems:");
477 msg("<warning> The following font caused problems (substituting):");
479 msg("<warning> The following Type 3 Font will be rendered as graphics:");
480 dumpFontInfo("<warning>", font);
483 static void dumpFontInfo(const char*loglevel, GfxFont*font)
485 char* id = getFontID(font);
486 char* name = getFontName(font);
487 Ref* r=font->getID();
488 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
490 GString*gstr = font->getTag();
492 msg("%s| Tag: %s\n", loglevel, id);
494 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
496 GfxFontType type=font->getType();
498 case fontUnknownType:
499 msg("%s| Type: unknown\n",loglevel);
502 msg("%s| Type: 1\n",loglevel);
505 msg("%s| Type: 1C\n",loglevel);
508 msg("%s| Type: 3\n",loglevel);
511 msg("%s| Type: TrueType\n",loglevel);
514 msg("%s| Type: CIDType0\n",loglevel);
517 msg("%s| Type: CIDType0C\n",loglevel);
520 msg("%s| Type: CIDType2\n",loglevel);
525 GBool embedded = font->getEmbeddedFontID(&embRef);
527 if(font->getEmbeddedFontName()) {
528 embeddedName = font->getEmbeddedFontName()->getCString();
531 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
533 gstr = font->getExtFontFile();
535 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
537 // Get font descriptor flags.
538 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
539 if(font->isSerif()) msg("%s| is serif\n", loglevel);
540 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
541 if(font->isItalic()) msg("%s| is italic\n", loglevel);
542 if(font->isBold()) msg("%s| is bold\n", loglevel);
548 //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");}
549 //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");}
551 void dump_outline(gfxline_t*line)
554 if(line->type == gfx_moveTo) {
555 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
556 } else if(line->type == gfx_lineTo) {
557 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
558 } else if(line->type == gfx_splineTo) {
559 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
565 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
567 int num = path->getNumSubpaths();
570 double lastx=0,lasty=0,posx=0,posy=0;
573 msg("<warning> empty path");
577 gfxdrawer_target_gfxline(&draw);
579 for(t = 0; t < num; t++) {
580 GfxSubpath *subpath = path->getSubpath(t);
581 int subnum = subpath->getNumPoints();
582 double bx=0,by=0,cx=0,cy=0;
584 for(s=0;s<subnum;s++) {
587 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
592 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
593 draw.lineTo(&draw, lastx, lasty);
595 draw.moveTo(&draw, x,y);
600 } else if(subpath->getCurve(s) && cpos==0) {
604 } else if(subpath->getCurve(s) && cpos==1) {
612 draw.lineTo(&draw, x,y);
614 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
621 /* fix non-closed lines */
622 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
623 draw.lineTo(&draw, lastx, lasty);
625 gfxline_t*result = (gfxline_t*)draw.result(&draw);
627 gfxline_optimize(result);
632 GBool GFXOutputDev::useTilingPatternFill()
634 infofeature("tiled patterns");
638 GBool GFXOutputDev::useShadedFills()
640 infofeature("shaded fills");
644 GBool GFXOutputDev::useDrawForm()
646 infofeature("forms");
649 void GFXOutputDev::drawForm(Ref id)
651 msg("<error> drawForm not implemented");
653 GBool GFXOutputDev::needNonText()
657 void GFXOutputDev::endPage()
659 msg("<verbose> endPage");
662 #define STROKE_FILL 1
663 #define STROKE_CLIP 2
664 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
666 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
667 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
668 double miterLimit = state->getMiterLimit();
669 double width = state->getTransformedLineWidth();
672 double opaq = state->getStrokeOpacity();
674 state->getFillRGB(&rgb);
676 state->getStrokeRGB(&rgb);
678 col.r = colToByte(rgb.r);
679 col.g = colToByte(rgb.g);
680 col.b = colToByte(rgb.b);
681 col.a = (unsigned char)(opaq*255);
683 gfx_capType capType = gfx_capRound;
684 if(lineCap == 0) capType = gfx_capButt;
685 else if(lineCap == 1) capType = gfx_capRound;
686 else if(lineCap == 2) capType = gfx_capSquare;
688 gfx_joinType joinType = gfx_joinRound;
689 if(lineJoin == 0) joinType = gfx_joinMiter;
690 else if(lineJoin == 1) joinType = gfx_joinRound;
691 else if(lineJoin == 2) joinType = gfx_joinBevel;
694 double dashphase = 0;
696 state->getLineDash(&ldash, &dashnum, &dashphase);
700 if(dashnum && ldash) {
701 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
703 msg("<trace> %d dashes", dashnum);
704 msg("<trace> | phase: %f", dashphase);
705 for(t=0;t<dashnum;t++) {
707 msg("<trace> | d%-3d: %f", t, ldash[t]);
710 if(getLogLevel() >= LOGLEVEL_TRACE) {
714 line2 = gfxtool_dash_line(line, dash, dashphase);
717 msg("<trace> After dashing:");
720 if(getLogLevel() >= LOGLEVEL_TRACE) {
721 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
723 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
724 lineCap==0?"butt": (lineJoin==1?"round":"square"),
726 col.r,col.g,col.b,col.a
731 if(flags&STROKE_FILL) {
732 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
733 gfxline_t*gfxline = SVPtogfxline(svp);
734 if(getLogLevel() >= LOGLEVEL_TRACE) {
735 dump_outline(gfxline);
738 msg("<warning> Empty polygon (resulting from stroked line)");
740 if(flags&STROKE_CLIP) {
741 device->startclip(device, gfxline);
742 states[statepos].clipping++;
744 device->fill(device, gfxline, &col);
749 if(flags&STROKE_CLIP)
750 msg("<error> Stroke&clip not supported at the same time");
751 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
758 gfxcolor_t getFillColor(GfxState * state)
761 double opaq = state->getFillOpacity();
762 state->getFillRGB(&rgb);
764 col.r = colToByte(rgb.r);
765 col.g = colToByte(rgb.g);
766 col.b = colToByte(rgb.b);
767 col.a = (unsigned char)(opaq*255);
771 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
773 gfxcolor_t col = getFillColor(state);
775 if(getLogLevel() >= LOGLEVEL_TRACE) {
776 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
779 device->fill(device, line, &col);
782 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
784 if(getLogLevel() >= LOGLEVEL_TRACE) {
785 msg("<trace> clip\n");
789 device->startclip(device, line);
790 states[statepos].clipping++;
793 void GFXOutputDev::clip(GfxState *state)
795 GfxPath * path = state->getPath();
796 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
797 clipToGfxLine(state, line);
801 void GFXOutputDev::eoClip(GfxState *state)
803 GfxPath * path = state->getPath();
804 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
806 if(getLogLevel() >= LOGLEVEL_TRACE) {
807 msg("<trace> eoclip\n");
811 device->startclip(device, line);
812 states[statepos].clipping++;
815 void GFXOutputDev::clipToStrokePath(GfxState *state)
817 GfxPath * path = state->getPath();
818 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
820 if(getLogLevel() >= LOGLEVEL_TRACE) {
821 msg("<trace> cliptostrokepath\n");
825 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
829 void GFXOutputDev::endframe()
832 device->endclip(device);
837 void GFXOutputDev::finish()
841 device->endclip(device);
847 GFXOutputDev::~GFXOutputDev()
852 free(this->pages); this->pages = 0;
855 gfxfontlist_free(this->gfxfontlist, 1);
857 GBool GFXOutputDev::upsideDown()
861 GBool GFXOutputDev::useDrawChar()
866 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
867 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
869 #define RENDER_FILL 0
870 #define RENDER_STROKE 1
871 #define RENDER_FILLSTROKE 2
872 #define RENDER_INVISIBLE 3
873 #define RENDER_CLIP 4
875 static char tmp_printstr[4096];
876 char* makeStringPrintable(char*str)
878 int len = strlen(str);
893 tmp_printstr[len++] = '.';
894 tmp_printstr[len++] = '.';
895 tmp_printstr[len++] = '.';
897 tmp_printstr[len] = 0;
900 #define INTERNAL_FONT_SIZE 1024.0
901 void GFXOutputDev::updateFontMatrix(GfxState*state)
903 double* ctm = state->getCTM();
904 double fontSize = state->getFontSize();
905 double*textMat = state->getTextMat();
907 /* taking the absolute value of horizScaling seems to be required for
908 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
909 double hscale = fabs(state->getHorizScaling());
911 // from xpdf-3.02/SplashOutputDev:updateFont
912 double mm11 = textMat[0] * fontSize * hscale;
913 double mm12 = textMat[1] * fontSize * hscale;
914 double mm21 = textMat[2] * fontSize;
915 double mm22 = textMat[3] * fontSize;
917 // multiply with ctm, like state->getFontTransMat() does
918 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
919 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
920 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
921 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
922 this->current_font_matrix.tx = 0;
923 this->current_font_matrix.ty = 0;
926 void GFXOutputDev::beginString(GfxState *state, GString *s)
928 int render = state->getRender();
929 if(current_text_stroke) {
930 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
933 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
936 static gfxline_t* mkEmptyGfxShape(double x, double y)
938 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
939 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
943 static char isValidUnicode(int c)
945 if(c>=32 && c<0x2fffe)
950 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
951 double dx, double dy,
952 double originX, double originY,
953 CharCode charid, int nBytes, Unicode *_u, int uLen)
955 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
956 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
960 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
962 int render = state->getRender();
963 gfxcolor_t col = getFillColor(state);
965 // check for invisible text -- this is used by Acrobat Capture
966 if (render == RENDER_INVISIBLE) {
968 if(!config_extrafontdata)
972 GfxFont*font = state->getFont();
974 if(font->getType() == fontType3) {
975 /* type 3 chars are passed as graphics */
976 msg("<debug> type3 char at %f/%f", x, y);
980 Unicode u = uLen?(_u[0]):0;
981 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);
983 gfxmatrix_t m = this->current_font_matrix;
984 state->transform(x, y, &m.tx, &m.ty);
985 m.tx += user_movex + clipmovex;
986 m.ty += user_movey + clipmovey;
988 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
989 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
991 msg("<debug> Drawing glyph %d as shape", charid);
993 msg("<notice> Some texts will be rendered as shape");
996 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
997 gfxline_t*tglyph = gfxline_clone(glyph);
998 gfxline_transform(tglyph, &m);
999 if((render&3) != RENDER_INVISIBLE) {
1000 gfxline_t*add = gfxline_clone(tglyph);
1001 current_text_stroke = gfxline_append(current_text_stroke, add);
1003 if(render&RENDER_CLIP) {
1004 gfxline_t*add = gfxline_clone(tglyph);
1005 current_text_clip = gfxline_append(current_text_clip, add);
1006 if(!current_text_clip) {
1007 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1010 gfxline_free(tglyph);
1014 void GFXOutputDev::endString(GfxState *state)
1016 int render = state->getRender();
1017 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1019 if(current_text_stroke) {
1020 /* fillstroke and stroke text rendering objects we can process right
1021 now (as there may be texts of other rendering modes in this
1022 text object)- clipping objects have to wait until endTextObject,
1024 device->setparameter(device, "mark","TXT");
1025 if((render&3) == RENDER_FILL) {
1026 fillGfxLine(state, current_text_stroke);
1027 gfxline_free(current_text_stroke);
1028 current_text_stroke = 0;
1029 } else if((render&3) == RENDER_FILLSTROKE) {
1030 fillGfxLine(state, current_text_stroke);
1031 strokeGfxline(state, current_text_stroke,0);
1032 gfxline_free(current_text_stroke);
1033 current_text_stroke = 0;
1034 } else if((render&3) == RENDER_STROKE) {
1035 strokeGfxline(state, current_text_stroke,0);
1036 gfxline_free(current_text_stroke);
1037 current_text_stroke = 0;
1039 device->setparameter(device, "mark","");
1043 void GFXOutputDev::endTextObject(GfxState *state)
1045 int render = state->getRender();
1046 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1048 if(current_text_clip) {
1049 device->setparameter(device, "mark","TXT");
1050 clipToGfxLine(state, current_text_clip);
1051 device->setparameter(device, "mark","");
1052 gfxline_free(current_text_clip);
1053 current_text_clip = 0;
1057 /* the logic seems to be as following:
1058 first, beginType3Char is called, with the charcode and the coordinates.
1059 if this function returns true, it already knew about the char and has now drawn it.
1060 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1061 called with some parameters.
1062 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1063 at the position first passed to beginType3Char). the char ends with endType3Char.
1065 The drawing operations between beginType3Char and endType3Char are somewhat different to
1066 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1067 color determines the color of a font)
1070 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1072 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1075 if(config_extrafontdata && current_fontinfo) {
1077 gfxmatrix_t m = this->current_font_matrix;
1078 state->transform(0, 0, &m.tx, &m.ty);
1079 m.m00*=INTERNAL_FONT_SIZE;
1080 m.m01*=INTERNAL_FONT_SIZE;
1081 m.m10*=INTERNAL_FONT_SIZE;
1082 m.m11*=INTERNAL_FONT_SIZE;
1083 m.tx += user_movex + clipmovex;
1084 m.ty += user_movey + clipmovey;
1086 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1087 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1090 gfxcolor_t col={0,0,0,0};
1091 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1092 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1096 /* the character itself is going to be passed using the draw functions */
1097 return gFalse; /* gTrue= is_in_cache? */
1100 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1102 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1105 void GFXOutputDev::endType3Char(GfxState *state)
1108 msg("<debug> endType3Char");
1111 void GFXOutputDev::startFrame(int width, int height)
1113 if(outer_clip_box) {
1114 device->endclip(device);
1118 device->startpage(device, width, height);
1119 this->width = width;
1120 this->height = height;
1123 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1125 this->currentpage = pageNum;
1127 int rot = doc->getPageRotate(1);
1130 gfxline_t clippath[5];
1132 white.r = white.g = white.b = white.a = 255;
1134 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1135 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1136 Use CropBox, not MediaBox, as page size
1143 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1144 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1146 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1147 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1149 this->clipmovex = -(int)x1;
1150 this->clipmovey = -(int)y1;
1152 /* apply user clip box */
1153 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1154 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1155 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1156 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1157 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1158 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1161 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1163 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);
1165 msg("<verbose> page is rotated %d degrees\n", rot);
1167 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1168 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1169 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1170 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1171 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1172 device->startclip(device, clippath); outer_clip_box = 1;
1173 if(!config_transparent) {
1174 device->fill(device, clippath, &white);
1179 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1181 double x1, y1, x2, y2, w;
1182 gfxline_t points[5];
1185 msg("<debug> drawlink\n");
1187 link->getRect(&x1, &y1, &x2, &y2);
1188 cvtUserToDev(x1, y1, &x, &y);
1189 points[0].type = gfx_moveTo;
1190 points[0].x = points[4].x = x + user_movex + clipmovex;
1191 points[0].y = points[4].y = y + user_movey + clipmovey;
1192 points[0].next = &points[1];
1193 cvtUserToDev(x2, y1, &x, &y);
1194 points[1].type = gfx_lineTo;
1195 points[1].x = x + user_movex + clipmovex;
1196 points[1].y = y + user_movey + clipmovey;
1197 points[1].next = &points[2];
1198 cvtUserToDev(x2, y2, &x, &y);
1199 points[2].type = gfx_lineTo;
1200 points[2].x = x + user_movex + clipmovex;
1201 points[2].y = y + user_movey + clipmovey;
1202 points[2].next = &points[3];
1203 cvtUserToDev(x1, y2, &x, &y);
1204 points[3].type = gfx_lineTo;
1205 points[3].x = x + user_movex + clipmovex;
1206 points[3].y = y + user_movey + clipmovey;
1207 points[3].next = &points[4];
1208 cvtUserToDev(x1, y1, &x, &y);
1209 points[4].type = gfx_lineTo;
1210 points[4].x = x + user_movex + clipmovex;
1211 points[4].y = y + user_movey + clipmovey;
1214 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1215 points[0].x, points[0].y,
1216 points[1].x, points[1].y,
1217 points[2].x, points[2].y,
1218 points[3].x, points[3].y);
1220 LinkAction*action=link->getAction();
1223 const char*type = "-?-";
1226 msg("<trace> drawlink action=%d\n", action->getKind());
1227 switch(action->getKind())
1231 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1232 LinkDest *dest=NULL;
1233 if (ha->getDest()==NULL)
1234 dest=catalog->findDest(ha->getNamedDest());
1235 else dest=ha->getDest();
1237 if (dest->isPageRef()){
1238 Ref pageref=dest->getPageRef();
1239 page=catalog->findPage(pageref.num,pageref.gen);
1241 else page=dest->getPageNum();
1242 sprintf(buf, "%d", page);
1249 LinkGoToR*l = (LinkGoToR*)action;
1250 GString*g = l->getFileName();
1252 s = strdup(g->getCString());
1254 /* if the GoToR link has no filename, then
1255 try to find a refernce in the *local*
1257 GString*g = l->getNamedDest();
1259 s = strdup(g->getCString());
1265 LinkNamed*l = (LinkNamed*)action;
1266 GString*name = l->getName();
1268 s = strdup(name->lowerCase()->getCString());
1269 named = name->getCString();
1272 if(strstr(s, "next") || strstr(s, "forward"))
1274 page = currentpage + 1;
1276 else if(strstr(s, "prev") || strstr(s, "back"))
1278 page = currentpage - 1;
1280 else if(strstr(s, "last") || strstr(s, "end"))
1282 if(pages && pagepos>0)
1283 page = pages[pagepos-1];
1285 else if(strstr(s, "first") || strstr(s, "top"))
1293 case actionLaunch: {
1295 LinkLaunch*l = (LinkLaunch*)action;
1296 GString * str = new GString(l->getFileName());
1297 GString * params = l->getParams();
1299 str->append(params);
1300 s = strdup(str->getCString());
1307 LinkURI*l = (LinkURI*)action;
1308 GString*g = l->getURI();
1310 url = g->getCString();
1315 case actionUnknown: {
1317 LinkUnknown*l = (LinkUnknown*)action;
1322 msg("<error> Unknown link type!\n");
1327 if(!s) s = strdup("-?-");
1329 msg("<trace> drawlink s=%s\n", s);
1331 if(!linkinfo && (page || s))
1333 msg("<notice> File contains links");
1341 for(t=1;t<=pagepos;t++) {
1342 if(pages[t]==page) {
1351 sprintf(buf, "page%d", lpage);
1352 device->drawlink(device, points, buf);
1356 device->drawlink(device, points, s);
1359 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1363 void GFXOutputDev::saveState(GfxState *state) {
1364 dbg("saveState");dbgindent+=2;
1366 msg("<trace> saveState\n");
1369 msg("<error> Too many nested states in pdf.");
1373 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1374 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1375 states[statepos].clipping = 0;
1378 void GFXOutputDev::restoreState(GfxState *state) {
1379 dbgindent-=2; dbg("restoreState");
1382 msg("<error> Invalid restoreState");
1385 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1386 states[statepos].clipping?" (end clipping)":"");
1387 if(states[statepos].softmask) {
1388 clearSoftMask(state);
1391 while(states[statepos].clipping) {
1392 device->endclip(device);
1393 states[statepos].clipping--;
1398 void GFXOutputDev::updateLineWidth(GfxState *state)
1400 double width = state->getTransformedLineWidth();
1401 //swfoutput_setlinewidth(&device, width);
1404 void GFXOutputDev::updateLineCap(GfxState *state)
1406 int c = state->getLineCap();
1409 void GFXOutputDev::updateLineJoin(GfxState *state)
1411 int j = state->getLineJoin();
1414 void GFXOutputDev::updateFillColor(GfxState *state)
1417 double opaq = state->getFillOpacity();
1418 state->getFillRGB(&rgb);
1420 void GFXOutputDev::updateFillOpacity(GfxState *state)
1423 double opaq = state->getFillOpacity();
1424 state->getFillRGB(&rgb);
1425 dbg("update fillopaq %f", opaq);
1427 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1429 double opaq = state->getFillOpacity();
1430 dbg("update strokeopaq %f", opaq);
1432 void GFXOutputDev::updateFillOverprint(GfxState *state)
1434 double opaq = state->getFillOverprint();
1435 dbg("update filloverprint %f", opaq);
1437 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1439 double opaq = state->getStrokeOverprint();
1440 dbg("update strokeoverprint %f", opaq);
1442 void GFXOutputDev::updateTransfer(GfxState *state)
1444 dbg("update transfer");
1448 void GFXOutputDev::updateStrokeColor(GfxState *state)
1451 double opaq = state->getStrokeOpacity();
1452 state->getStrokeRGB(&rgb);
1455 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1461 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1463 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1464 memset(font, 0, sizeof(gfxfont_t));
1466 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1467 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1468 font->id = strdup(getFontID(xpdffont));
1470 double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
1472 //printf("%d glyphs\n", font->num_glyphs);
1473 font->num_glyphs = 0;
1474 for(t=0;t<src->num_glyphs;t++) {
1475 if(src->glyphs[t]) {
1476 SplashPath*path = src->glyphs[t]->path;
1477 int len = path?path->getLength():0;
1478 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1479 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1480 src->glyphs[t]->glyphid = font->num_glyphs;
1481 glyph->unicode = src->glyphs[t]->unicode;
1482 if(glyph->unicode >= font->max_unicode)
1483 font->max_unicode = glyph->unicode+1;
1485 gfxdrawer_target_gfxline(&drawer);
1489 for(s=0;s<len;s++) {
1492 path->getPoint(s, &x, &y, &f);
1495 if(f&splashPathFirst) {
1496 drawer.moveTo(&drawer, x*scale, y*scale);
1498 if(f&splashPathCurve) {
1500 path->getPoint(++s, &x2, &y2, &f);
1501 if(f&splashPathCurve) {
1503 path->getPoint(++s, &x3, &y3, &f);
1504 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1506 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1509 drawer.lineTo(&drawer, x*scale, y*scale);
1511 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1512 // (f&splashPathFirst)?"first":"",
1513 // (f&splashPathLast)?"last":"");
1515 glyph->line = (gfxline_t*)drawer.result(&drawer);
1516 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1520 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1521 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1522 for(t=0;t<font->num_glyphs;t++) {
1523 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1524 font->unicode2glyph[font->glyphs[t].unicode] = t;
1528 msg("<trace> %d glyphs.", t, font->num_glyphs);
1532 void GFXOutputDev::updateFont(GfxState *state)
1534 GfxFont* gfxFont = state->getFont();
1538 char*id = getFontID(gfxFont);
1539 msg("<verbose> Updating font to %s", id);
1540 if(gfxFont->getType() == fontType3) {
1541 infofeature("Type3 fonts");
1542 if(!config_extrafontdata) {
1547 msg("<error> Internal Error: FontID is null");
1551 this->current_fontinfo = this->info->getFont(id);
1552 if(!this->current_fontinfo) {
1553 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1555 if(!this->current_fontinfo->seen) {
1556 dumpFontInfo("<verbose>", gfxFont);
1559 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1561 font = createGfxFont(gfxFont, current_fontinfo);
1562 font->id = strdup(id);
1563 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1564 device->addfont(device, font);
1566 current_gfxfont = font;
1569 updateFontMatrix(state);
1572 #define SQR(x) ((x)*(x))
1574 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1576 if((newwidth<2 || newheight<2) ||
1577 (width<=newwidth || height<=newheight))
1579 unsigned char*newdata;
1581 newdata= (unsigned char*)malloc(newwidth*newheight);
1583 double fx = (double)(width)/newwidth;
1584 double fy = (double)(height)/newheight;
1586 int blocksize = (int)(8192/(fx*fy));
1587 int r = 8192*256/palettesize;
1588 for(x=0;x<newwidth;x++) {
1589 double ex = px + fx;
1590 int fromx = (int)px;
1592 int xweight1 = (int)(((fromx+1)-px)*256);
1593 int xweight2 = (int)((ex-tox)*256);
1595 for(y=0;y<newheight;y++) {
1596 double ey = py + fy;
1597 int fromy = (int)py;
1599 int yweight1 = (int)(((fromy+1)-py)*256);
1600 int yweight2 = (int)((ey-toy)*256);
1603 for(xx=fromx;xx<=tox;xx++)
1604 for(yy=fromy;yy<=toy;yy++) {
1605 int b = 1-data[width*yy+xx];
1607 if(xx==fromx) weight = (weight*xweight1)/256;
1608 if(xx==tox) weight = (weight*xweight2)/256;
1609 if(yy==fromy) weight = (weight*yweight1)/256;
1610 if(yy==toy) weight = (weight*yweight2)/256;
1613 //if(a) a=(palettesize-1)*r/blocksize;
1614 newdata[y*newwidth+x] = (a*blocksize)/r;
1622 #define IMAGE_TYPE_JPEG 0
1623 #define IMAGE_TYPE_LOSSLESS 1
1625 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1626 double x1,double y1,
1627 double x2,double y2,
1628 double x3,double y3,
1629 double x4,double y4, int type)
1631 gfxcolor_t*newpic=0;
1633 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1634 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1636 gfxline_t p1,p2,p3,p4,p5;
1637 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1638 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1639 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1640 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1641 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1643 {p1.x = (int)(p1.x*20)/20.0;
1644 p1.y = (int)(p1.y*20)/20.0;
1645 p2.x = (int)(p2.x*20)/20.0;
1646 p2.y = (int)(p2.y*20)/20.0;
1647 p3.x = (int)(p3.x*20)/20.0;
1648 p3.y = (int)(p3.y*20)/20.0;
1649 p4.x = (int)(p4.x*20)/20.0;
1650 p4.y = (int)(p4.y*20)/20.0;
1651 p5.x = (int)(p5.x*20)/20.0;
1652 p5.y = (int)(p5.y*20)/20.0;
1659 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1660 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1665 img.data = (gfxcolor_t*)data;
1669 if(type == IMAGE_TYPE_JPEG)
1670 /* TODO: pass image_dpi to device instead */
1671 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1673 dev->fillbitmap(dev, &p1, &img, &m, 0);
1676 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1677 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1679 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1682 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1683 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1685 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1689 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1690 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1691 GBool inlineImg, int mask, int*maskColors,
1692 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1694 double x1,y1,x2,y2,x3,y3,x4,y4;
1695 ImageStream *imgStr;
1700 unsigned char* maskbitmap = 0;
1703 ncomps = colorMap->getNumPixelComps();
1704 bits = colorMap->getBits();
1709 unsigned char buf[8];
1710 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1712 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1713 imgMaskStr->reset();
1714 unsigned char pal[256];
1715 int n = 1 << colorMap->getBits();
1720 maskColorMap->getGray(pixBuf, &gray);
1721 pal[t] = colToByte(gray);
1723 for (y = 0; y < maskHeight; y++) {
1724 for (x = 0; x < maskWidth; x++) {
1725 imgMaskStr->getPixel(buf);
1726 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1731 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1732 imgMaskStr->reset();
1733 for (y = 0; y < maskHeight; y++) {
1734 for (x = 0; x < maskWidth; x++) {
1735 imgMaskStr->getPixel(buf);
1737 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1745 imgStr = new ImageStream(str, width, ncomps,bits);
1748 if(!width || !height || (height<=1 && width<=1))
1750 msg("<verbose> Ignoring %d by %d image", width, height);
1751 unsigned char buf[8];
1753 for (y = 0; y < height; ++y)
1754 for (x = 0; x < width; ++x) {
1755 imgStr->getPixel(buf);
1763 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1764 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1765 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1766 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1768 if(!pbminfo && !(str->getKind()==strDCT)) {
1770 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1774 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1776 if(!jpeginfo && (str->getKind()==strDCT)) {
1777 msg("<notice> file contains jpeg pictures");
1783 unsigned char buf[8];
1785 unsigned char*pic = new unsigned char[width*height];
1786 gfxcolor_t pal[256];
1788 state->getFillRGB(&rgb);
1790 memset(pal,255,sizeof(pal));
1791 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1792 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1793 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1794 pal[0].a = 255; pal[1].a = 0;
1797 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1798 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1799 for (y = 0; y < height; ++y)
1800 for (x = 0; x < width; ++x)
1802 imgStr->getPixel(buf);
1805 pic[width*y+x] = buf[0];
1808 /* the size of the drawn image is added to the identifier
1809 as the same image may require different bitmaps if displayed
1810 at different sizes (due to antialiasing): */
1813 unsigned char*pic2 = 0;
1816 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1825 height = realheight;
1829 /* make a black/white palette */
1831 float r = 255/(numpalette-1);
1833 for(t=0;t<numpalette;t++) {
1834 pal[t].r = colToByte(rgb.r);
1835 pal[t].g = colToByte(rgb.g);
1836 pal[t].b = colToByte(rgb.b);
1837 pal[t].a = (unsigned char)(t*r);
1841 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1842 for (y = 0; y < height; ++y) {
1843 for (x = 0; x < width; ++x) {
1844 pic2[width*y+x] = pal[pic[y*width+x]];
1847 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1851 if(maskbitmap) free(maskbitmap);
1857 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1858 gfxcolor_t*pic=new gfxcolor_t[width*height];
1859 for (y = 0; y < height; ++y) {
1860 for (x = 0; x < width; ++x) {
1861 imgStr->getPixel(pixBuf);
1862 colorMap->getRGB(pixBuf, &rgb);
1863 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1864 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1865 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1866 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1868 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1872 if(str->getKind()==strDCT)
1873 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1875 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1878 if(maskbitmap) free(maskbitmap);
1881 gfxcolor_t*pic=new gfxcolor_t[width*height];
1882 gfxcolor_t pal[256];
1883 int n = 1 << colorMap->getBits();
1885 for(t=0;t<256;t++) {
1887 colorMap->getRGB(pixBuf, &rgb);
1889 {/*if(maskColors && *maskColors==t) {
1890 msg("<notice> Color %d is transparent", t);
1891 if (imgData->maskColors) {
1893 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1894 if (pix[i] < imgData->maskColors[2*i] ||
1895 pix[i] > imgData->maskColors[2*i+1]) {
1910 pal[t].r = (unsigned char)(colToByte(rgb.r));
1911 pal[t].g = (unsigned char)(colToByte(rgb.g));
1912 pal[t].b = (unsigned char)(colToByte(rgb.b));
1913 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1916 for (y = 0; y < height; ++y) {
1917 for (x = 0; x < width; ++x) {
1918 imgStr->getPixel(pixBuf);
1919 pic[width*y+x] = pal[pixBuf[0]];
1921 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1925 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1929 if(maskbitmap) free(maskbitmap);
1934 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1935 int width, int height, GBool invert,
1938 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1939 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1940 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1943 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1944 int width, int height, GfxImageColorMap *colorMap,
1945 int *maskColors, GBool inlineImg)
1947 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1948 colorMap?"colorMap":"no colorMap",
1949 maskColors?"maskColors":"no maskColors",
1951 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1952 colorMap?"colorMap":"no colorMap",
1953 maskColors?"maskColors":"no maskColors",
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,inlineImg,0,maskColors, 0,0,0,0, 0);
1961 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1962 int width, int height,
1963 GfxImageColorMap *colorMap,
1964 Stream *maskStr, int maskWidth, int maskHeight,
1967 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1968 colorMap?"colorMap":"no colorMap",
1969 maskWidth, maskHeight);
1970 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1971 colorMap?"colorMap":"no colorMap",
1972 maskWidth, maskHeight);
1974 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1975 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1976 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1979 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1980 int width, int height,
1981 GfxImageColorMap *colorMap,
1983 int maskWidth, int maskHeight,
1984 GfxImageColorMap *maskColorMap)
1986 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1987 colorMap?"colorMap":"no colorMap",
1988 maskWidth, maskHeight);
1989 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1990 colorMap?"colorMap":"no colorMap",
1991 maskWidth, maskHeight);
1993 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1994 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1995 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1998 void GFXOutputDev::stroke(GfxState *state)
2002 GfxPath * path = state->getPath();
2003 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2004 strokeGfxline(state, line, 0);
2008 void GFXOutputDev::fill(GfxState *state)
2010 gfxcolor_t col = getFillColor(state);
2011 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2013 GfxPath * path = state->getPath();
2014 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2015 fillGfxLine(state, line);
2019 void GFXOutputDev::eoFill(GfxState *state)
2021 gfxcolor_t col = getFillColor(state);
2022 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2024 GfxPath * path = state->getPath();
2025 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2026 fillGfxLine(state, line);
2031 static const char* dirseparator()
2040 void addGlobalFont(const char*filename)
2043 memset(&f, 0, sizeof(fontfile_t));
2044 f.filename = filename;
2045 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2046 msg("<notice> Adding font \"%s\".", filename);
2047 fonts[fontnum++] = f;
2049 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2053 void addGlobalLanguageDir(const char*dir)
2055 msg("<notice> Adding %s to language pack directories", dir);
2059 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2060 strcpy(config_file, dir);
2061 strcat(config_file, dirseparator());
2062 strcat(config_file, "add-to-xpdfrc");
2064 fi = fopen(config_file, "rb");
2066 msg("<error> Could not open %s", config_file);
2069 globalParams->parseFile(new GString(config_file), fi);
2073 void addGlobalFontDir(const char*dirname)
2075 #ifdef HAVE_DIRENT_H
2076 msg("<notice> Adding %s to font directories", dirname);
2077 lastfontdir = strdup(dirname);
2078 DIR*dir = opendir(dirname);
2080 msg("<warning> Couldn't open directory %s\n", dirname);
2085 ent = readdir (dir);
2089 char*name = ent->d_name;
2095 if(!strncasecmp(&name[l-4], ".pfa", 4))
2097 if(!strncasecmp(&name[l-4], ".pfb", 4))
2099 if(!strncasecmp(&name[l-4], ".ttf", 4))
2102 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2103 strcpy(fontname, dirname);
2104 strcat(fontname, dirseparator());
2105 strcat(fontname, name);
2106 addGlobalFont(fontname);
2111 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2115 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2121 this->pagebuflen = 1024;
2122 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2123 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2125 while(pdfpage >= this->pagebuflen)
2127 int oldlen = this->pagebuflen;
2128 this->pagebuflen+=1024;
2129 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2130 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2133 this->pages[pdfpage] = outputpage;
2134 if(pdfpage>this->pagepos)
2135 this->pagepos = pdfpage;
2141 double width,height;
2144 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2146 double xMin, yMin, xMax, yMax, x, y;
2147 double tx, ty, w, h;
2148 // transform the bbox
2149 state->transform(bbox[0], bbox[1], &x, &y);
2152 state->transform(bbox[0], bbox[3], &x, &y);
2155 } else if (x > xMax) {
2160 } else if (y > yMax) {
2163 state->transform(bbox[2], bbox[1], &x, &y);
2166 } else if (x > xMax) {
2171 } else if (y > yMax) {
2174 state->transform(bbox[2], bbox[3], &x, &y);
2177 } else if (x > xMax) {
2182 } else if (y > yMax) {
2185 tx = (int)floor(xMin);
2188 } else if (tx > width) {
2191 ty = (int)floor(yMin);
2194 } else if (ty > height) {
2197 w = (int)ceil(xMax) - tx + 1;
2198 if (tx + w > width) {
2204 h = (int)ceil(yMax) - ty + 1;
2205 if (ty + h > height) {
2219 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2220 GfxColorSpace *blendingColorSpace,
2221 GBool isolated, GBool knockout,
2224 const char*colormodename = "";
2225 BBox rect = mkBBox(state, bbox, this->width, this->height);
2227 if(blendingColorSpace) {
2228 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2230 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);
2231 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2232 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);
2234 states[statepos].createsoftmask |= forSoftMask;
2235 states[statepos].transparencygroup = !forSoftMask;
2236 states[statepos].isolated = isolated;
2238 states[statepos].olddevice = this->device;
2239 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2241 gfxdevice_record_init(this->device);
2243 /*if(!forSoftMask) { ////???
2244 state->setFillOpacity(0.0);
2249 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2252 dbg("endTransparencyGroup");
2253 msg("<verbose> endTransparencyGroup");
2255 gfxdevice_t*r = this->device;
2257 this->device = states[statepos].olddevice;
2259 if(states[statepos].createsoftmask) {
2260 states[statepos-1].softmaskrecording = r->finish(r);
2262 states[statepos-1].grouprecording = r->finish(r);
2265 states[statepos].createsoftmask = 0;
2266 states[statepos].transparencygroup = 0;
2270 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2272 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2273 "colordodge","colorburn","hardlight","softlight","difference",
2274 "exclusion","hue","saturation","color","luminosity"};
2276 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2277 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2279 if(state->getBlendMode() == gfxBlendNormal)
2280 infofeature("transparency groups");
2283 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2284 warnfeature(buffer, 0);
2287 gfxresult_t*grouprecording = states[statepos].grouprecording;
2289 if(state->getBlendMode() == gfxBlendNormal) {
2291 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2292 gfxresult_record_replay(grouprecording, &ops);
2295 grouprecording->destroy(grouprecording);
2297 states[statepos].grouprecording = 0;
2300 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2302 /* alpha = 1: retrieve mask values from alpha layer
2303 alpha = 0: retrieve mask values from luminance */
2304 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2305 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2306 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2307 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2309 infofeature("soft masks");
2311 warnfeature("soft masks from alpha channel",0);
2313 states[statepos].olddevice = this->device;
2314 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2315 gfxdevice_record_init(this->device);
2317 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2319 states[statepos].softmask = 1;
2320 states[statepos].softmask_alpha = alpha;
2323 static inline Guchar div255(int x) {
2324 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2327 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2329 if(c < min) c = min;
2330 if(c > max) c = max;
2334 void GFXOutputDev::clearSoftMask(GfxState *state)
2336 if(!states[statepos].softmask)
2338 states[statepos].softmask = 0;
2339 dbg("clearSoftMask statepos=%d", statepos);
2340 msg("<verbose> clearSoftMask");
2342 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2343 msg("<error> Error in softmask/tgroup ordering");
2347 gfxresult_t*mask = states[statepos].softmaskrecording;
2348 gfxresult_t*below = this->device->finish(this->device);
2349 this->device = states[statepos].olddevice;
2351 /* get outline of all objects below the soft mask */
2352 gfxdevice_t uniondev;
2353 gfxdevice_union_init(&uniondev, 0);
2354 gfxresult_record_replay(below, &uniondev);
2355 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2356 uniondev.finish(&uniondev);
2358 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2360 this->device->startclip(this->device, belowoutline);
2361 gfxresult_record_replay(below, this->device);
2362 gfxresult_record_replay(mask, this->device);
2363 this->device->endclip(this->device);
2364 gfxline_free(belowoutline);
2367 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2368 if(width<=0 || height<=0)
2371 gfxdevice_t belowrender;
2372 gfxdevice_render_init(&belowrender);
2373 if(states[statepos+1].isolated) {
2374 belowrender.setparameter(&belowrender, "fillwhite", "1");
2376 belowrender.setparameter(&belowrender, "antialize", "2");
2377 belowrender.startpage(&belowrender, width, height);
2378 gfxresult_record_replay(below, &belowrender);
2379 belowrender.endpage(&belowrender);
2380 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2381 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2382 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2384 gfxdevice_t maskrender;
2385 gfxdevice_render_init(&maskrender);
2386 maskrender.startpage(&maskrender, width, height);
2387 gfxresult_record_replay(mask, &maskrender);
2388 maskrender.endpage(&maskrender);
2389 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2390 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2392 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2393 msg("<fatal> Internal error in mask drawing");
2398 for(y=0;y<height;y++) {
2399 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2400 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2401 for(x=0;x<width;x++) {
2403 if(states[statepos].softmask_alpha) {
2406 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2409 l2->a = div255(alpha*l2->a);
2411 /* DON'T premultiply alpha- this is done by fillbitmap,
2412 depending on the output device */
2413 //l2->r = div255(alpha*l2->r);
2414 //l2->g = div255(alpha*l2->g);
2415 //l2->b = div255(alpha*l2->b);
2421 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2424 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2425 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2427 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2429 mask->destroy(mask);
2430 below->destroy(below);
2431 maskresult->destroy(maskresult);
2432 belowresult->destroy(belowresult);
2433 states[statepos].softmaskrecording = 0;
2438 // public: ~MemCheck()
2440 // delete globalParams;globalParams=0;
2441 // Object::memCheck(stderr);
2442 // gMemReport(stderr);