1 //========================================================================
5 // Copyright 2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
18 #include "GlobalParams.h"
24 #include "CharCodeToUnicode.h"
25 #include "FontEncodingTables.h"
26 #include "FoFiTrueType.h"
27 #include "SplashBitmap.h"
28 #include "SplashGlyphBitmap.h"
29 #include "SplashPattern.h"
30 #include "SplashScreen.h"
31 #include "SplashPath.h"
32 #include "SplashState.h"
33 #include "SplashErrorCodes.h"
34 #include "SplashFontEngine.h"
35 #include "SplashFont.h"
36 #include "SplashFontFile.h"
37 #include "SplashFontFileID.h"
39 #include "SplashOutputDev.h"
41 //------------------------------------------------------------------------
43 //------------------------------------------------------------------------
45 struct SplashOutFontSubst {
50 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
51 static SplashOutFontSubst splashOutSubstFonts[16] = {
53 {"Helvetica-Oblique", 0.833},
54 {"Helvetica-Bold", 0.889},
55 {"Helvetica-BoldOblique", 0.889},
56 {"Times-Roman", 0.788},
57 {"Times-Italic", 0.722},
58 {"Times-Bold", 0.833},
59 {"Times-BoldItalic", 0.778},
61 {"Courier-Oblique", 0.600},
62 {"Courier-Bold", 0.600},
63 {"Courier-BoldOblique", 0.600},
70 //------------------------------------------------------------------------
72 #define soutRound(x) ((int)(x + 0.5))
74 //------------------------------------------------------------------------
75 // SplashOutFontFileID
76 //------------------------------------------------------------------------
78 class SplashOutFontFileID: public SplashFontFileID {
81 SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
83 ~SplashOutFontFileID() {}
85 GBool matches(SplashFontFileID *id) {
86 return ((SplashOutFontFileID *)id)->r.num == r.num &&
87 ((SplashOutFontFileID *)id)->r.gen == r.gen;
90 void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
91 int getSubstIdx() { return substIdx; }
99 //------------------------------------------------------------------------
101 //------------------------------------------------------------------------
103 struct T3FontCacheTag {
105 Gushort mru; // valid bit (0x8000) and MRU index
111 T3FontCache(Ref *fontID, double m11A, double m12A,
112 double m21A, double m22A,
113 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
116 GBool matches(Ref *idA, double m11A, double m12A,
117 double m21A, double m22A)
118 { return fontID.num == idA->num && fontID.gen == idA->gen &&
119 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
121 Ref fontID; // PDF font ID
122 double m11, m12, m21, m22; // transform matrix
123 int glyphX, glyphY; // pixel offset of glyph bitmaps
124 int glyphW, glyphH; // size of glyph bitmaps, in pixels
125 int glyphSize; // size of glyph bitmaps, in bytes
126 int cacheSets; // number of sets in cache
127 int cacheAssoc; // cache associativity (glyphs per set)
128 Guchar *cacheData; // glyph pixmap cache
129 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
132 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
133 double m21A, double m22A,
134 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
148 glyphSize = glyphW * glyphH;
150 glyphSize = ((glyphW + 7) >> 3) * glyphH;
153 if (glyphSize <= 256) {
155 } else if (glyphSize <= 512) {
157 } else if (glyphSize <= 1024) {
162 cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
163 cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
164 sizeof(T3FontCacheTag));
165 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
166 cacheTags[i].mru = i & (cacheAssoc - 1);
170 T3FontCache::~T3FontCache() {
175 struct T3GlyphStack {
176 Gushort code; // character code
177 double x, y; // position to draw the glyph
180 T3FontCache *cache; // font cache for the current font
181 T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
182 Guchar *cacheData; // pointer to cache data for the glyph
185 SplashBitmap *origBitmap;
187 double origCTM4, origCTM5;
189 T3GlyphStack *next; // next object on stack
192 //------------------------------------------------------------------------
194 //------------------------------------------------------------------------
196 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
198 SplashColor paperColorA) {
199 colorMode = colorModeA;
200 reverseVideo = reverseVideoA;
201 paperColor = paperColorA;
205 bitmap = new SplashBitmap(1, 1, colorMode);
206 splash = new Splash(bitmap);
207 splash->clear(paperColor);
215 needFontUpdate = gFalse;
219 underlayCbkData = NULL;
222 SplashOutputDev::~SplashOutputDev() {
225 for (i = 0; i < nT3Fonts; ++i) {
226 delete t3FontCache[i];
239 void SplashOutputDev::startDoc(XRef *xrefA) {
246 fontEngine = new SplashFontEngine(
248 globalParams->getEnableT1lib(),
250 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
251 globalParams->getEnableFreeType(),
253 globalParams->getAntialias());
254 for (i = 0; i < nT3Fonts; ++i) {
255 delete t3FontCache[i];
260 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
264 w = state ? (int)(state->getPageWidth() + 0.5) : 1;
265 h = state ? (int)(state->getPageHeight() + 0.5) : 1;
269 if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
273 bitmap = new SplashBitmap(w, h, colorMode);
275 splash = new Splash(bitmap);
277 case splashModeMono1: color.mono1 = 0; break;
278 case splashModeMono8: color.mono8 = 0; break;
279 case splashModeRGB8: color.rgb8 = splashMakeRGB8(0, 0, 0); break;
280 case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break;
282 splash->setStrokePattern(new SplashSolidColor(color));
283 splash->setFillPattern(new SplashSolidColor(color));
284 splash->setLineCap(splashLineCapButt);
285 splash->setLineJoin(splashLineJoinMiter);
286 splash->setLineDash(NULL, 0, 0);
287 splash->setMiterLimit(10);
288 splash->setFlatness(1);
289 splash->clear(paperColor);
292 (*underlayCbk)(underlayCbkData);
296 void SplashOutputDev::endPage() {
299 void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
300 double x1, y1, x2, y2;
301 LinkBorderStyle *borderStyle;
306 SplashCoord dashList[20];
310 link->getRect(&x1, &y1, &x2, &y2);
311 borderStyle = link->getBorderStyle();
312 if (borderStyle->getWidth() > 0) {
313 borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
314 gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
318 splash->setStrokePattern(getColor(gray, &rgb));
319 splash->setLineWidth((SplashCoord)borderStyle->getWidth());
320 borderStyle->getDash(&dash, &dashLength);
321 if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
322 if (dashLength > 20) {
325 for (i = 0; i < dashLength; ++i) {
326 dashList[i] = (SplashCoord)dash[i];
328 splash->setLineDash(dashList, dashLength, 0);
330 path = new SplashPath();
331 if (borderStyle->getType() == linkBorderUnderlined) {
332 cvtUserToDev(x1, y1, &x, &y);
333 path->moveTo((SplashCoord)x, (SplashCoord)y);
334 cvtUserToDev(x2, y1, &x, &y);
335 path->lineTo((SplashCoord)x, (SplashCoord)y);
337 cvtUserToDev(x1, y1, &x, &y);
338 path->moveTo((SplashCoord)x, (SplashCoord)y);
339 cvtUserToDev(x2, y1, &x, &y);
340 path->lineTo((SplashCoord)x, (SplashCoord)y);
341 cvtUserToDev(x2, y2, &x, &y);
342 path->lineTo((SplashCoord)x, (SplashCoord)y);
343 cvtUserToDev(x1, y2, &x, &y);
344 path->lineTo((SplashCoord)x, (SplashCoord)y);
347 splash->stroke(path);
352 void SplashOutputDev::saveState(GfxState *state) {
356 void SplashOutputDev::restoreState(GfxState *state) {
357 splash->restoreState();
358 needFontUpdate = gTrue;
361 void SplashOutputDev::updateAll(GfxState *state) {
362 updateLineDash(state);
363 updateLineJoin(state);
364 updateLineCap(state);
365 updateLineWidth(state);
366 updateFlatness(state);
367 updateMiterLimit(state);
368 updateFillColor(state);
369 updateStrokeColor(state);
370 needFontUpdate = gTrue;
373 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
374 double m21, double m22,
375 double m31, double m32) {
376 updateLineDash(state);
377 updateLineJoin(state);
378 updateLineCap(state);
379 updateLineWidth(state);
382 void SplashOutputDev::updateLineDash(GfxState *state) {
386 SplashCoord dash[20];
390 state->getLineDash(&dashPattern, &dashLength, &dashStart);
391 if (dashLength > 20) {
394 for (i = 0; i < dashLength; ++i) {
395 dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]);
400 phase = (SplashCoord)state->transformWidth(dashStart);
401 splash->setLineDash(dash, dashLength, phase);
404 void SplashOutputDev::updateFlatness(GfxState *state) {
405 splash->setFlatness(state->getFlatness());
408 void SplashOutputDev::updateLineJoin(GfxState *state) {
409 splash->setLineJoin(state->getLineJoin());
412 void SplashOutputDev::updateLineCap(GfxState *state) {
413 splash->setLineCap(state->getLineCap());
416 void SplashOutputDev::updateMiterLimit(GfxState *state) {
417 splash->setMiterLimit(state->getMiterLimit());
420 void SplashOutputDev::updateLineWidth(GfxState *state) {
421 splash->setLineWidth(state->getTransformedLineWidth());
424 void SplashOutputDev::updateFillColor(GfxState *state) {
428 state->getFillGray(&gray);
429 state->getFillRGB(&rgb);
430 splash->setFillPattern(getColor(gray, &rgb));
433 void SplashOutputDev::updateStrokeColor(GfxState *state) {
437 state->getStrokeGray(&gray);
438 state->getStrokeRGB(&rgb);
439 splash->setStrokePattern(getColor(gray, &rgb));
442 SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) {
443 SplashPattern *pattern;
444 SplashColor color0, color1;
458 pattern = NULL; // make gcc happy
460 case splashModeMono1:
463 pattern = new SplashHalftone(color0, color1,
464 splash->getScreen()->copy(),
467 case splashModeMono8:
468 color1.mono8 = soutRound(255 * gray);
469 pattern = new SplashSolidColor(color1);
472 color1.rgb8 = splashMakeRGB8(soutRound(255 * r),
475 pattern = new SplashSolidColor(color1);
477 case splashModeBGR8Packed:
478 color1.bgr8 = splashMakeBGR8(soutRound(255 * r),
481 pattern = new SplashSolidColor(color1);
488 void SplashOutputDev::updateFont(GfxState *state) {
490 GfxFontType fontType;
491 SplashOutFontFileID *id;
492 SplashFontFile *fontFile;
495 Object refObj, strObj;
496 GString *tmpFileName, *fileName, *substName;
499 DisplayFontParam *dfp;
500 double m11, m12, m21, m22, w1, w2;
503 int c, substIdx, n, code;
505 needFontUpdate = gFalse;
510 if (!(gfxFont = state->getFont())) {
513 fontType = gfxFont->getType();
514 if (fontType == fontType3) {
518 // check the font file cache
519 id = new SplashOutFontFileID(gfxFont->getID());
520 if ((fontFile = fontEngine->getFontFile(id))) {
525 // if there is an embedded font, write it to disk
526 if (gfxFont->getEmbeddedFontID(&embRef)) {
527 if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
528 error(-1, "Couldn't create temporary font file");
531 refObj.initRef(embRef.num, embRef.gen);
532 refObj.fetch(xref, &strObj);
534 strObj.streamReset();
535 while ((c = strObj.streamGetChar()) != EOF) {
538 strObj.streamClose();
541 fileName = tmpFileName;
543 // if there is an external font file, use it
544 } else if (!(fileName = gfxFont->getExtFontFile())) {
546 // look for a display font mapping or a substitute font
548 if (gfxFont->isCIDFont()) {
549 if (((GfxCIDFont *)gfxFont)->getCollection()) {
551 getDisplayCIDFont(gfxFont->getName(),
552 ((GfxCIDFont *)gfxFont)->getCollection());
555 if (gfxFont->getName()) {
556 dfp = globalParams->getDisplayFont(gfxFont->getName());
559 // 8-bit font substitution
560 if (gfxFont->isFixedWidth()) {
562 } else if (gfxFont->isSerif()) {
567 if (gfxFont->isBold()) {
570 if (gfxFont->isItalic()) {
573 substName = new GString(splashOutSubstFonts[substIdx].name);
574 dfp = globalParams->getDisplayFont(substName);
576 id->setSubstIdx(substIdx);
580 error(-1, "Couldn't find a font for '%s'",
581 gfxFont->getName() ? gfxFont->getName()->getCString()
587 fileName = dfp->t1.fileName;
588 fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
591 fileName = dfp->tt.fileName;
592 fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
597 // load the font file
600 if (!(fontFile = fontEngine->loadType1Font(
602 fileName->getCString(),
603 fileName == tmpFileName,
604 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
605 error(-1, "Couldn't create a font for '%s'",
606 gfxFont->getName() ? gfxFont->getName()->getCString()
612 if (!(fontFile = fontEngine->loadType1CFont(
614 fileName->getCString(),
615 fileName == tmpFileName,
616 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
617 error(-1, "Couldn't create a font for '%s'",
618 gfxFont->getName() ? gfxFont->getName()->getCString()
624 if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
627 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
629 if (!(fontFile = fontEngine->loadTrueTypeFont(
631 fileName->getCString(),
632 fileName == tmpFileName,
634 error(-1, "Couldn't create a font for '%s'",
635 gfxFont->getName() ? gfxFont->getName()->getCString()
642 if (!(fontFile = fontEngine->loadCIDFont(
644 fileName->getCString(),
645 fileName == tmpFileName))) {
646 error(-1, "Couldn't create a font for '%s'",
647 gfxFont->getName() ? gfxFont->getName()->getCString()
653 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
654 codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
655 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
656 n * sizeof(Gushort));
657 if (!(fontFile = fontEngine->loadTrueTypeFont(
659 fileName->getCString(),
660 fileName == tmpFileName,
662 error(-1, "Couldn't create a font for '%s'",
663 gfxFont->getName() ? gfxFont->getName()->getCString()
669 // this shouldn't happen
674 // get the font matrix
675 state->getFontTransMat(&m11, &m12, &m21, &m22);
676 m11 *= state->getHorizScaling();
677 m12 *= state->getHorizScaling();
679 // for substituted fonts: adjust the font matrix -- compare the
680 // width of 'm' in the original font and the substituted font
681 substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
683 for (code = 0; code < 256; ++code) {
684 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
685 name[0] == 'm' && name[1] == '\0') {
690 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
691 w2 = splashOutSubstFonts[substIdx].mWidth;
692 if (!gfxFont->isSymbolic()) {
693 // if real font is substantially narrower than substituted
694 // font, reduce the font size accordingly
695 if (w1 > 0.01 && w1 < 0.9 * w2) {
704 // create the scaled font
705 mat[0] = m11; mat[1] = -m12;
706 mat[2] = m21; mat[3] = -m22;
707 font = fontEngine->getFont(fontFile, mat);
723 void SplashOutputDev::stroke(GfxState *state) {
726 path = convertPath(state, state->getPath());
727 splash->stroke(path);
731 void SplashOutputDev::fill(GfxState *state) {
734 path = convertPath(state, state->getPath());
735 splash->fill(path, gFalse);
739 void SplashOutputDev::eoFill(GfxState *state) {
742 path = convertPath(state, state->getPath());
743 splash->fill(path, gTrue);
747 void SplashOutputDev::clip(GfxState *state) {
750 path = convertPath(state, state->getPath());
751 splash->clipToPath(path, gFalse);
755 void SplashOutputDev::eoClip(GfxState *state) {
758 path = convertPath(state, state->getPath());
759 splash->clipToPath(path, gTrue);
763 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
766 double x1, y1, x2, y2, x3, y3;
769 sPath = new SplashPath();
770 for (i = 0; i < path->getNumSubpaths(); ++i) {
771 subpath = path->getSubpath(i);
772 if (subpath->getNumPoints() > 0) {
773 state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
774 sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
776 while (j < subpath->getNumPoints()) {
777 if (subpath->getCurve(j)) {
778 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
779 state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
780 state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
781 sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
782 (SplashCoord)x2, (SplashCoord)y2,
783 (SplashCoord)x3, (SplashCoord)y3);
786 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
787 sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
791 if (subpath->isClosed()) {
799 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
800 double dx, double dy,
801 double originX, double originY,
802 CharCode code, Unicode *u, int uLen) {
807 if (needFontUpdate) {
814 // check for invisible text -- this is used by Acrobat Capture
815 render = state->getRender();
822 state->transform(x, y, &x1, &y1);
826 splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
830 if ((render & 3) == 1 || (render & 3) == 2) {
831 if ((path = font->getGlyphPath(code))) {
832 path->offset((SplashCoord)x1, (SplashCoord)y1);
833 splash->stroke(path);
840 path = font->getGlyphPath(code);
841 path->offset((SplashCoord)x1, (SplashCoord)y1);
843 textClipPath->append(path);
851 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
852 double dx, double dy,
853 CharCode code, Unicode *u, int uLen) {
859 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
862 if (!(gfxFont = state->getFont())) {
865 fontID = gfxFont->getID();
866 ctm = state->getCTM();
867 state->transform(0, 0, &xt, &yt);
869 // is it the first (MRU) font in the cache?
870 if (!(nT3Fonts > 0 &&
871 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
873 // is the font elsewhere in the cache?
874 for (i = 1; i < nT3Fonts; ++i) {
875 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
876 t3Font = t3FontCache[i];
877 for (j = i; j > 0; --j) {
878 t3FontCache[j] = t3FontCache[j - 1];
880 t3FontCache[0] = t3Font;
886 // create new entry in the font cache
887 if (nT3Fonts == splashOutT3FontCacheSize) {
888 delete t3FontCache[nT3Fonts - 1];
891 for (j = nT3Fonts; j > 0; --j) {
892 t3FontCache[j] = t3FontCache[j - 1];
895 bbox = gfxFont->getFontBBox();
896 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
897 // broken bounding box -- just take a guess
903 state->transform(bbox[0], bbox[1], &x1, &y1);
906 state->transform(bbox[0], bbox[3], &x1, &y1);
909 } else if (x1 > xMax) {
914 } else if (y1 > yMax) {
917 state->transform(bbox[2], bbox[1], &x1, &y1);
920 } else if (x1 > xMax) {
925 } else if (y1 > yMax) {
928 state->transform(bbox[2], bbox[3], &x1, &y1);
931 } else if (x1 > xMax) {
936 } else if (y1 > yMax) {
940 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
941 (int)floor(xMin - xt),
942 (int)floor(yMin - yt),
943 (int)ceil(xMax) - (int)floor(xMin) + 3,
944 (int)ceil(yMax) - (int)floor(yMin) + 3,
945 colorMode != splashModeMono1);
948 t3Font = t3FontCache[0];
950 // is the glyph in the cache?
951 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
952 for (j = 0; j < t3Font->cacheAssoc; ++j) {
953 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
954 t3Font->cacheTags[i+j].code == code) {
955 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
956 t3Font->cacheData + (i+j) * t3Font->glyphSize,
962 // push a new Type 3 glyph record
963 t3gs = new T3GlyphStack();
964 t3gs->next = t3GlyphStack;
966 t3GlyphStack->code = code;
967 t3GlyphStack->x = xt;
968 t3GlyphStack->y = yt;
969 t3GlyphStack->cache = t3Font;
970 t3GlyphStack->cacheTag = NULL;
971 t3GlyphStack->cacheData = NULL;
976 void SplashOutputDev::endType3Char(GfxState *state) {
980 if (t3GlyphStack->cacheTag) {
981 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8,
982 t3GlyphStack->cache->glyphSize);
985 bitmap = t3GlyphStack->origBitmap;
986 splash = t3GlyphStack->origSplash;
987 ctm = state->getCTM();
988 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
989 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
990 drawType3Glyph(t3GlyphStack->cache,
991 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
992 t3GlyphStack->x, t3GlyphStack->y);
995 t3GlyphStack = t3gs->next;
999 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1002 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1003 double llx, double lly, double urx, double ury) {
1005 T3FontCache *t3Font;
1007 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1010 t3Font = t3GlyphStack->cache;
1012 // check for a valid bbox
1013 state->transform(0, 0, &xt, &yt);
1014 state->transform(llx, lly, &x1, &y1);
1017 state->transform(llx, ury, &x1, &y1);
1020 } else if (x1 > xMax) {
1025 } else if (y1 > yMax) {
1028 state->transform(urx, lly, &x1, &y1);
1031 } else if (x1 > xMax) {
1036 } else if (y1 > yMax) {
1039 state->transform(urx, ury, &x1, &y1);
1042 } else if (x1 > xMax) {
1047 } else if (y1 > yMax) {
1050 if (xMin - xt < t3Font->glyphX ||
1051 yMin - yt < t3Font->glyphY ||
1052 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1053 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1054 error(-1, "Bad bounding box in Type 3 glyph");
1058 // allocate a cache entry
1059 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1060 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1061 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1062 t3Font->cacheTags[i+j].mru = 0x8000;
1063 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1064 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1065 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1067 ++t3Font->cacheTags[i+j].mru;
1072 t3GlyphStack->origBitmap = bitmap;
1073 t3GlyphStack->origSplash = splash;
1074 ctm = state->getCTM();
1075 t3GlyphStack->origCTM4 = ctm[4];
1076 t3GlyphStack->origCTM5 = ctm[5];
1078 // create the temporary bitmap
1079 if (colorMode == splashModeMono1) {
1080 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1);
1081 splash = new Splash(bitmap);
1083 splash->clear(color);
1086 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8);
1087 splash = new Splash(bitmap);
1089 splash->clear(color);
1092 splash->setFillPattern(new SplashSolidColor(color));
1093 splash->setStrokePattern(new SplashSolidColor(color));
1094 //~ this should copy other state from t3GlyphStack->origSplash?
1095 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1096 -t3Font->glyphX, -t3Font->glyphY);
1099 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1100 T3FontCacheTag *tag, Guchar *data,
1101 double x, double y) {
1102 SplashGlyphBitmap glyph;
1104 glyph.x = -t3Font->glyphX;
1105 glyph.y = -t3Font->glyphY;
1106 glyph.w = t3Font->glyphW;
1107 glyph.h = t3Font->glyphH;
1108 glyph.aa = colorMode != splashModeMono1;
1110 glyph.freeData = gFalse;
1111 splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1114 void SplashOutputDev::endTextObject(GfxState *state) {
1116 splash->clipToPath(textClipPath, gFalse);
1117 delete textClipPath;
1118 textClipPath = NULL;
1122 struct SplashOutImageMaskData {
1123 ImageStream *imgStr;
1128 GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) {
1129 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1132 if (imgMaskData->idx >= imgMaskData->nPixels) {
1136 imgMaskData->imgStr->getPixel(&pix);
1137 if (!imgMaskData->invert) {
1145 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1146 int width, int height, GBool invert,
1150 SplashOutImageMaskData imgMaskData;
1153 ctm = state->getCTM();
1158 mat[4] = ctm[2] + ctm[4];
1159 mat[5] = ctm[3] + ctm[5];
1161 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1162 imgMaskData.imgStr->reset();
1163 imgMaskData.nPixels = width * height;
1164 imgMaskData.idx = 0;
1165 imgMaskData.invert = invert;
1167 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1169 while (imageMaskSrc(&imgMaskData, &pix)) ;
1172 delete imgMaskData.imgStr;
1175 struct SplashOutImageData {
1176 ImageStream *imgStr;
1177 GfxImageColorMap *colorMap;
1179 SplashOutputDev *out;
1183 GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel,
1185 SplashOutImageData *imgData = (SplashOutImageData *)data;
1186 Guchar pix[gfxColorMaxComps];
1191 if (imgData->idx >= imgData->nPixels) {
1196 imgData->imgStr->getPixel(pix);
1197 switch (imgData->out->colorMode) {
1198 case splashModeMono1:
1199 case splashModeMono8:
1200 imgData->colorMap->getGray(pix, &gray);
1201 pixel->mono8 = soutRound(255 * gray);
1203 case splashModeRGB8:
1204 imgData->colorMap->getRGB(pix, &rgb);
1205 pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r),
1206 soutRound(255 * rgb.g),
1207 soutRound(255 * rgb.b));
1209 case splashModeBGR8Packed:
1210 imgData->colorMap->getRGB(pix, &rgb);
1211 pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r),
1212 soutRound(255 * rgb.g),
1213 soutRound(255 * rgb.b));
1217 if (imgData->maskColors) {
1219 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1220 if (pix[i] < imgData->maskColors[2*i] ||
1221 pix[i] > imgData->maskColors[2*i+1]) {
1234 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1235 int width, int height,
1236 GfxImageColorMap *colorMap,
1237 int *maskColors, GBool inlineImg) {
1240 SplashOutImageData imgData;
1244 ctm = state->getCTM();
1249 mat[4] = ctm[2] + ctm[4];
1250 mat[5] = ctm[3] + ctm[5];
1252 imgData.imgStr = new ImageStream(str, width,
1253 colorMap->getNumPixelComps(),
1254 colorMap->getBits());
1255 imgData.imgStr->reset();
1256 imgData.colorMap = colorMap;
1257 imgData.maskColors = maskColors;
1259 imgData.nPixels = width * height;
1262 splash->drawImage(&imageSrc, &imgData,
1263 (colorMode == splashModeMono1) ? splashModeMono8
1265 width, height, mat);
1267 while (imageSrc(&imgData, &pix, &alpha)) ;
1270 delete imgData.imgStr;
1273 int SplashOutputDev::getBitmapWidth() {
1274 return bitmap->getWidth();
1277 int SplashOutputDev::getBitmapHeight() {
1278 return bitmap->getHeight();
1281 void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1,
1282 SplashPattern *pattern) {
1285 path = new SplashPath();
1286 path->moveTo((SplashCoord)x0, (SplashCoord)y0);
1287 path->lineTo((SplashCoord)x1, (SplashCoord)y0);
1288 path->lineTo((SplashCoord)x1, (SplashCoord)y1);
1289 path->lineTo((SplashCoord)x0, (SplashCoord)y1);
1291 splash->setFillPattern(pattern);
1292 splash->xorFill(path, gTrue);
1296 void SplashOutputDev::setFillColor(int r, int g, int b) {
1303 gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g;
1304 splash->setFillPattern(getColor(gray, &rgb));
1307 SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
1308 DisplayFontParam *dfp;
1310 SplashOutFontFileID *id;
1311 SplashFontFile *fontFile;
1312 SplashFont *fontObj;
1315 for (i = 0; i < 16; ++i) {
1316 if (!name->cmp(splashOutSubstFonts[i].name)) {
1325 id = new SplashOutFontFileID(&ref);
1327 // check the font file cache
1328 if ((fontFile = fontEngine->getFontFile(id))) {
1331 // load the font file
1333 dfp = globalParams->getDisplayFont(name);
1334 if (dfp->kind != displayFontT1) {
1337 fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
1338 gFalse, winAnsiEncoding);
1341 // create the scaled font
1342 fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);