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;
280 case splashModeRGB8Packed: color.rgb8 = splashMakeRGB8(0, 0, 0); break;
281 case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break;
283 splash->setStrokePattern(new SplashSolidColor(color));
284 splash->setFillPattern(new SplashSolidColor(color));
285 splash->setLineCap(splashLineCapButt);
286 splash->setLineJoin(splashLineJoinMiter);
287 splash->setLineDash(NULL, 0, 0);
288 splash->setMiterLimit(10);
289 splash->setFlatness(1);
290 splash->clear(paperColor);
293 (*underlayCbk)(underlayCbkData);
297 void SplashOutputDev::endPage() {
300 void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
301 double x1, y1, x2, y2;
302 LinkBorderStyle *borderStyle;
307 SplashCoord dashList[20];
311 link->getRect(&x1, &y1, &x2, &y2);
312 borderStyle = link->getBorderStyle();
313 if (borderStyle->getWidth() > 0) {
314 borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
315 gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
319 splash->setStrokePattern(getColor(gray, &rgb));
320 splash->setLineWidth((SplashCoord)borderStyle->getWidth());
321 borderStyle->getDash(&dash, &dashLength);
322 if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
323 if (dashLength > 20) {
326 for (i = 0; i < dashLength; ++i) {
327 dashList[i] = (SplashCoord)dash[i];
329 splash->setLineDash(dashList, dashLength, 0);
331 path = new SplashPath();
332 if (borderStyle->getType() == linkBorderUnderlined) {
333 cvtUserToDev(x1, y1, &x, &y);
334 path->moveTo((SplashCoord)x, (SplashCoord)y);
335 cvtUserToDev(x2, y1, &x, &y);
336 path->lineTo((SplashCoord)x, (SplashCoord)y);
338 cvtUserToDev(x1, y1, &x, &y);
339 path->moveTo((SplashCoord)x, (SplashCoord)y);
340 cvtUserToDev(x2, y1, &x, &y);
341 path->lineTo((SplashCoord)x, (SplashCoord)y);
342 cvtUserToDev(x2, y2, &x, &y);
343 path->lineTo((SplashCoord)x, (SplashCoord)y);
344 cvtUserToDev(x1, y2, &x, &y);
345 path->lineTo((SplashCoord)x, (SplashCoord)y);
348 splash->stroke(path);
353 void SplashOutputDev::saveState(GfxState *state) {
357 void SplashOutputDev::restoreState(GfxState *state) {
358 splash->restoreState();
359 needFontUpdate = gTrue;
362 void SplashOutputDev::updateAll(GfxState *state) {
363 updateLineDash(state);
364 updateLineJoin(state);
365 updateLineCap(state);
366 updateLineWidth(state);
367 updateFlatness(state);
368 updateMiterLimit(state);
369 updateFillColor(state);
370 updateStrokeColor(state);
371 needFontUpdate = gTrue;
374 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
375 double m21, double m22,
376 double m31, double m32) {
377 updateLineDash(state);
378 updateLineJoin(state);
379 updateLineCap(state);
380 updateLineWidth(state);
383 void SplashOutputDev::updateLineDash(GfxState *state) {
387 SplashCoord dash[20];
391 state->getLineDash(&dashPattern, &dashLength, &dashStart);
392 if (dashLength > 20) {
395 for (i = 0; i < dashLength; ++i) {
396 dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]);
401 phase = (SplashCoord)state->transformWidth(dashStart);
402 splash->setLineDash(dash, dashLength, phase);
405 void SplashOutputDev::updateFlatness(GfxState *state) {
406 splash->setFlatness(state->getFlatness());
409 void SplashOutputDev::updateLineJoin(GfxState *state) {
410 splash->setLineJoin(state->getLineJoin());
413 void SplashOutputDev::updateLineCap(GfxState *state) {
414 splash->setLineCap(state->getLineCap());
417 void SplashOutputDev::updateMiterLimit(GfxState *state) {
418 splash->setMiterLimit(state->getMiterLimit());
421 void SplashOutputDev::updateLineWidth(GfxState *state) {
422 splash->setLineWidth(state->getTransformedLineWidth());
425 void SplashOutputDev::updateFillColor(GfxState *state) {
429 state->getFillGray(&gray);
430 state->getFillRGB(&rgb);
431 splash->setFillPattern(getColor(gray, &rgb));
434 void SplashOutputDev::updateStrokeColor(GfxState *state) {
438 state->getStrokeGray(&gray);
439 state->getStrokeRGB(&rgb);
440 splash->setStrokePattern(getColor(gray, &rgb));
443 SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) {
444 SplashPattern *pattern;
445 SplashColor color0, color1;
459 pattern = NULL; // make gcc happy
461 case splashModeMono1:
464 pattern = new SplashHalftone(color0, color1,
465 splash->getScreen()->copy(),
468 case splashModeMono8:
469 color1.mono8 = soutRound(255 * gray);
470 pattern = new SplashSolidColor(color1);
473 case splashModeRGB8Packed:
474 color1.rgb8 = splashMakeRGB8(soutRound(255 * r),
477 pattern = new SplashSolidColor(color1);
479 case splashModeBGR8Packed:
480 color1.bgr8 = splashMakeBGR8(soutRound(255 * r),
483 pattern = new SplashSolidColor(color1);
490 void SplashOutputDev::updateFont(GfxState *state) {
492 GfxFontType fontType;
493 SplashOutFontFileID *id;
494 SplashFontFile *fontFile;
497 Object refObj, strObj;
498 GString *tmpFileName, *fileName, *substName;
501 DisplayFontParam *dfp;
502 double m11, m12, m21, m22, w1, w2;
505 int c, substIdx, n, code;
507 needFontUpdate = gFalse;
512 if (!(gfxFont = state->getFont())) {
515 fontType = gfxFont->getType();
516 if (fontType == fontType3) {
520 // check the font file cache
521 id = new SplashOutFontFileID(gfxFont->getID());
522 if ((fontFile = fontEngine->getFontFile(id))) {
527 // if there is an embedded font, write it to disk
528 if (gfxFont->getEmbeddedFontID(&embRef)) {
529 if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
530 error(-1, "Couldn't create temporary font file");
533 refObj.initRef(embRef.num, embRef.gen);
534 refObj.fetch(xref, &strObj);
536 strObj.streamReset();
537 while ((c = strObj.streamGetChar()) != EOF) {
540 strObj.streamClose();
543 fileName = tmpFileName;
545 // if there is an external font file, use it
546 } else if (!(fileName = gfxFont->getExtFontFile())) {
548 // look for a display font mapping or a substitute font
550 if (gfxFont->isCIDFont()) {
551 if (((GfxCIDFont *)gfxFont)->getCollection()) {
553 getDisplayCIDFont(gfxFont->getName(),
554 ((GfxCIDFont *)gfxFont)->getCollection());
557 if (gfxFont->getName()) {
558 dfp = globalParams->getDisplayFont(gfxFont->getName());
561 // 8-bit font substitution
562 if (gfxFont->isFixedWidth()) {
564 } else if (gfxFont->isSerif()) {
569 if (gfxFont->isBold()) {
572 if (gfxFont->isItalic()) {
575 substName = new GString(splashOutSubstFonts[substIdx].name);
576 dfp = globalParams->getDisplayFont(substName);
578 id->setSubstIdx(substIdx);
582 error(-1, "Couldn't find a font for '%s'",
583 gfxFont->getName() ? gfxFont->getName()->getCString()
589 fileName = dfp->t1.fileName;
590 fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
593 fileName = dfp->tt.fileName;
594 fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
599 // load the font file
602 if (!(fontFile = fontEngine->loadType1Font(
604 fileName->getCString(),
605 fileName == tmpFileName,
606 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
607 error(-1, "Couldn't create a font for '%s'",
608 gfxFont->getName() ? gfxFont->getName()->getCString()
614 if (!(fontFile = fontEngine->loadType1CFont(
616 fileName->getCString(),
617 fileName == tmpFileName,
618 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
619 error(-1, "Couldn't create a font for '%s'",
620 gfxFont->getName() ? gfxFont->getName()->getCString()
626 if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
629 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
631 if (!(fontFile = fontEngine->loadTrueTypeFont(
633 fileName->getCString(),
634 fileName == tmpFileName,
636 error(-1, "Couldn't create a font for '%s'",
637 gfxFont->getName() ? gfxFont->getName()->getCString()
644 if (!(fontFile = fontEngine->loadCIDFont(
646 fileName->getCString(),
647 fileName == tmpFileName))) {
648 error(-1, "Couldn't create a font for '%s'",
649 gfxFont->getName() ? gfxFont->getName()->getCString()
655 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
656 codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
657 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
658 n * sizeof(Gushort));
659 if (!(fontFile = fontEngine->loadTrueTypeFont(
661 fileName->getCString(),
662 fileName == tmpFileName,
664 error(-1, "Couldn't create a font for '%s'",
665 gfxFont->getName() ? gfxFont->getName()->getCString()
671 // this shouldn't happen
676 // get the font matrix
677 state->getFontTransMat(&m11, &m12, &m21, &m22);
678 m11 *= state->getHorizScaling();
679 m12 *= state->getHorizScaling();
681 // for substituted fonts: adjust the font matrix -- compare the
682 // width of 'm' in the original font and the substituted font
683 substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
685 for (code = 0; code < 256; ++code) {
686 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
687 name[0] == 'm' && name[1] == '\0') {
692 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
693 w2 = splashOutSubstFonts[substIdx].mWidth;
694 if (!gfxFont->isSymbolic()) {
695 // if real font is substantially narrower than substituted
696 // font, reduce the font size accordingly
697 if (w1 > 0.01 && w1 < 0.9 * w2) {
706 // create the scaled font
707 mat[0] = m11; mat[1] = -m12;
708 mat[2] = m21; mat[3] = -m22;
709 font = fontEngine->getFont(fontFile, mat);
725 void SplashOutputDev::stroke(GfxState *state) {
728 path = convertPath(state, state->getPath());
729 splash->stroke(path);
733 void SplashOutputDev::fill(GfxState *state) {
736 path = convertPath(state, state->getPath());
737 splash->fill(path, gFalse);
741 void SplashOutputDev::eoFill(GfxState *state) {
744 path = convertPath(state, state->getPath());
745 splash->fill(path, gTrue);
749 void SplashOutputDev::clip(GfxState *state) {
752 path = convertPath(state, state->getPath());
753 splash->clipToPath(path, gFalse);
757 void SplashOutputDev::eoClip(GfxState *state) {
760 path = convertPath(state, state->getPath());
761 splash->clipToPath(path, gTrue);
765 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
768 double x1, y1, x2, y2, x3, y3;
771 sPath = new SplashPath();
772 for (i = 0; i < path->getNumSubpaths(); ++i) {
773 subpath = path->getSubpath(i);
774 if (subpath->getNumPoints() > 0) {
775 state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
776 sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
778 while (j < subpath->getNumPoints()) {
779 if (subpath->getCurve(j)) {
780 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
781 state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
782 state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
783 sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
784 (SplashCoord)x2, (SplashCoord)y2,
785 (SplashCoord)x3, (SplashCoord)y3);
788 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
789 sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
793 if (subpath->isClosed()) {
801 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
802 double dx, double dy,
803 double originX, double originY,
804 CharCode code, Unicode *u, int uLen) {
809 if (needFontUpdate) {
816 // check for invisible text -- this is used by Acrobat Capture
817 render = state->getRender();
824 state->transform(x, y, &x1, &y1);
828 splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
832 if ((render & 3) == 1 || (render & 3) == 2) {
833 if ((path = font->getGlyphPath(code))) {
834 path->offset((SplashCoord)x1, (SplashCoord)y1);
835 splash->stroke(path);
842 path = font->getGlyphPath(code);
843 path->offset((SplashCoord)x1, (SplashCoord)y1);
845 textClipPath->append(path);
853 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
854 double dx, double dy,
855 CharCode code, Unicode *u, int uLen) {
861 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
864 if (!(gfxFont = state->getFont())) {
867 fontID = gfxFont->getID();
868 ctm = state->getCTM();
869 state->transform(0, 0, &xt, &yt);
871 // is it the first (MRU) font in the cache?
872 if (!(nT3Fonts > 0 &&
873 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
875 // is the font elsewhere in the cache?
876 for (i = 1; i < nT3Fonts; ++i) {
877 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
878 t3Font = t3FontCache[i];
879 for (j = i; j > 0; --j) {
880 t3FontCache[j] = t3FontCache[j - 1];
882 t3FontCache[0] = t3Font;
888 // create new entry in the font cache
889 if (nT3Fonts == splashOutT3FontCacheSize) {
890 delete t3FontCache[nT3Fonts - 1];
893 for (j = nT3Fonts; j > 0; --j) {
894 t3FontCache[j] = t3FontCache[j - 1];
897 bbox = gfxFont->getFontBBox();
898 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
899 // broken bounding box -- just take a guess
905 state->transform(bbox[0], bbox[1], &x1, &y1);
908 state->transform(bbox[0], bbox[3], &x1, &y1);
911 } else if (x1 > xMax) {
916 } else if (y1 > yMax) {
919 state->transform(bbox[2], bbox[1], &x1, &y1);
922 } else if (x1 > xMax) {
927 } else if (y1 > yMax) {
930 state->transform(bbox[2], bbox[3], &x1, &y1);
933 } else if (x1 > xMax) {
938 } else if (y1 > yMax) {
942 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
943 (int)floor(xMin - xt),
944 (int)floor(yMin - yt),
945 (int)ceil(xMax) - (int)floor(xMin) + 3,
946 (int)ceil(yMax) - (int)floor(yMin) + 3,
947 colorMode != splashModeMono1);
950 t3Font = t3FontCache[0];
952 // is the glyph in the cache?
953 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
954 for (j = 0; j < t3Font->cacheAssoc; ++j) {
955 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
956 t3Font->cacheTags[i+j].code == code) {
957 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
958 t3Font->cacheData + (i+j) * t3Font->glyphSize,
964 // push a new Type 3 glyph record
965 t3gs = new T3GlyphStack();
966 t3gs->next = t3GlyphStack;
968 t3GlyphStack->code = code;
969 t3GlyphStack->x = xt;
970 t3GlyphStack->y = yt;
971 t3GlyphStack->cache = t3Font;
972 t3GlyphStack->cacheTag = NULL;
973 t3GlyphStack->cacheData = NULL;
978 void SplashOutputDev::endType3Char(GfxState *state) {
982 if (t3GlyphStack->cacheTag) {
983 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8,
984 t3GlyphStack->cache->glyphSize);
987 bitmap = t3GlyphStack->origBitmap;
988 splash = t3GlyphStack->origSplash;
989 ctm = state->getCTM();
990 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
991 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
992 drawType3Glyph(t3GlyphStack->cache,
993 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
994 t3GlyphStack->x, t3GlyphStack->y);
997 t3GlyphStack = t3gs->next;
1001 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1004 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1005 double llx, double lly, double urx, double ury) {
1007 T3FontCache *t3Font;
1009 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1012 t3Font = t3GlyphStack->cache;
1014 // check for a valid bbox
1015 state->transform(0, 0, &xt, &yt);
1016 state->transform(llx, lly, &x1, &y1);
1019 state->transform(llx, ury, &x1, &y1);
1022 } else if (x1 > xMax) {
1027 } else if (y1 > yMax) {
1030 state->transform(urx, lly, &x1, &y1);
1033 } else if (x1 > xMax) {
1038 } else if (y1 > yMax) {
1041 state->transform(urx, ury, &x1, &y1);
1044 } else if (x1 > xMax) {
1049 } else if (y1 > yMax) {
1052 if (xMin - xt < t3Font->glyphX ||
1053 yMin - yt < t3Font->glyphY ||
1054 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1055 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1056 error(-1, "Bad bounding box in Type 3 glyph");
1060 // allocate a cache entry
1061 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1062 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1063 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1064 t3Font->cacheTags[i+j].mru = 0x8000;
1065 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1066 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1067 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1069 ++t3Font->cacheTags[i+j].mru;
1074 t3GlyphStack->origBitmap = bitmap;
1075 t3GlyphStack->origSplash = splash;
1076 ctm = state->getCTM();
1077 t3GlyphStack->origCTM4 = ctm[4];
1078 t3GlyphStack->origCTM5 = ctm[5];
1080 // create the temporary bitmap
1081 if (colorMode == splashModeMono1) {
1082 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1);
1083 splash = new Splash(bitmap);
1085 splash->clear(color);
1088 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8);
1089 splash = new Splash(bitmap);
1091 splash->clear(color);
1094 splash->setFillPattern(new SplashSolidColor(color));
1095 splash->setStrokePattern(new SplashSolidColor(color));
1096 //~ this should copy other state from t3GlyphStack->origSplash?
1097 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1098 -t3Font->glyphX, -t3Font->glyphY);
1101 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1102 T3FontCacheTag *tag, Guchar *data,
1103 double x, double y) {
1104 SplashGlyphBitmap glyph;
1106 glyph.x = -t3Font->glyphX;
1107 glyph.y = -t3Font->glyphY;
1108 glyph.w = t3Font->glyphW;
1109 glyph.h = t3Font->glyphH;
1110 glyph.aa = colorMode != splashModeMono1;
1112 glyph.freeData = gFalse;
1113 splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1116 void SplashOutputDev::endTextObject(GfxState *state) {
1118 splash->clipToPath(textClipPath, gFalse);
1119 delete textClipPath;
1120 textClipPath = NULL;
1124 struct SplashOutImageMaskData {
1125 ImageStream *imgStr;
1130 GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) {
1131 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1134 if (imgMaskData->idx >= imgMaskData->nPixels) {
1138 imgMaskData->imgStr->getPixel(&pix);
1139 if (!imgMaskData->invert) {
1147 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1148 int width, int height, GBool invert,
1152 SplashOutImageMaskData imgMaskData;
1155 ctm = state->getCTM();
1160 mat[4] = ctm[2] + ctm[4];
1161 mat[5] = ctm[3] + ctm[5];
1163 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1164 imgMaskData.imgStr->reset();
1165 imgMaskData.nPixels = width * height;
1166 imgMaskData.idx = 0;
1167 imgMaskData.invert = invert;
1169 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1171 while (imageMaskSrc(&imgMaskData, &pix)) ;
1174 delete imgMaskData.imgStr;
1177 struct SplashOutImageData {
1178 ImageStream *imgStr;
1179 GfxImageColorMap *colorMap;
1181 SplashOutputDev *out;
1185 GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel,
1187 SplashOutImageData *imgData = (SplashOutImageData *)data;
1188 Guchar pix[gfxColorMaxComps];
1193 if (imgData->idx >= imgData->nPixels) {
1198 imgData->imgStr->getPixel(pix);
1199 switch (imgData->out->colorMode) {
1200 case splashModeMono1:
1201 case splashModeMono8:
1202 imgData->colorMap->getGray(pix, &gray);
1203 pixel->mono8 = soutRound(255 * gray);
1205 case splashModeRGB8:
1206 case splashModeRGB8Packed:
1207 imgData->colorMap->getRGB(pix, &rgb);
1208 pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r),
1209 soutRound(255 * rgb.g),
1210 soutRound(255 * rgb.b));
1212 case splashModeBGR8Packed:
1213 imgData->colorMap->getRGB(pix, &rgb);
1214 pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r),
1215 soutRound(255 * rgb.g),
1216 soutRound(255 * rgb.b));
1220 if (imgData->maskColors) {
1222 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1223 if (pix[i] < imgData->maskColors[2*i] ||
1224 pix[i] > imgData->maskColors[2*i+1]) {
1237 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1238 int width, int height,
1239 GfxImageColorMap *colorMap,
1240 int *maskColors, GBool inlineImg) {
1243 SplashOutImageData imgData;
1247 ctm = state->getCTM();
1252 mat[4] = ctm[2] + ctm[4];
1253 mat[5] = ctm[3] + ctm[5];
1255 imgData.imgStr = new ImageStream(str, width,
1256 colorMap->getNumPixelComps(),
1257 colorMap->getBits());
1258 imgData.imgStr->reset();
1259 imgData.colorMap = colorMap;
1260 imgData.maskColors = maskColors;
1262 imgData.nPixels = width * height;
1265 splash->drawImage(&imageSrc, &imgData,
1266 (colorMode == splashModeMono1) ? splashModeMono8
1268 width, height, mat);
1270 while (imageSrc(&imgData, &pix, &alpha)) ;
1273 delete imgData.imgStr;
1276 int SplashOutputDev::getBitmapWidth() {
1277 return bitmap->getWidth();
1280 int SplashOutputDev::getBitmapHeight() {
1281 return bitmap->getHeight();
1284 void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1,
1285 SplashPattern *pattern) {
1288 path = new SplashPath();
1289 path->moveTo((SplashCoord)x0, (SplashCoord)y0);
1290 path->lineTo((SplashCoord)x1, (SplashCoord)y0);
1291 path->lineTo((SplashCoord)x1, (SplashCoord)y1);
1292 path->lineTo((SplashCoord)x0, (SplashCoord)y1);
1294 splash->setFillPattern(pattern);
1295 splash->xorFill(path, gTrue);
1299 void SplashOutputDev::setFillColor(int r, int g, int b) {
1306 gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g;
1307 splash->setFillPattern(getColor(gray, &rgb));
1310 SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
1311 DisplayFontParam *dfp;
1313 SplashOutFontFileID *id;
1314 SplashFontFile *fontFile;
1315 SplashFont *fontObj;
1318 for (i = 0; i < 16; ++i) {
1319 if (!name->cmp(splashOutSubstFonts[i].name)) {
1328 id = new SplashOutFontFileID(&ref);
1330 // check the font file cache
1331 if ((fontFile = fontEngine->getFontFile(id))) {
1334 // load the font file
1336 dfp = globalParams->getDisplayFont(name);
1337 if (dfp->kind != displayFontT1) {
1340 fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
1341 gFalse, winAnsiEncoding);
1344 // create the scaled font
1345 fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);