X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=pdf%2Fxpdf%2FXOutputDev.cc;h=a156b5584dfcb2fcf554ee8ae4e918bae51488f2;hb=ad63666daeeda50acc7630132d61fe044634fddd;hp=6f207d8e45e0fe976c16bbb384bf1fef68fe709d;hpb=2a393c134fe3fe8eb85bf818cb7ad6ae4396322a;p=evince.git diff --git a/pdf/xpdf/XOutputDev.cc b/pdf/xpdf/XOutputDev.cc index 6f207d8e..a156b558 100644 --- a/pdf/xpdf/XOutputDev.cc +++ b/pdf/xpdf/XOutputDev.cc @@ -2,15 +2,16 @@ // // XOutputDev.cc // -// Copyright 1996-2002 Glyph & Cog, LLC +// Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== -#ifdef __GNUC__ +#include + +#ifdef USE_GCC_PRAGMAS #pragma implementation #endif -#include #include #include #include @@ -95,6 +96,12 @@ static XOutFontSubst xOutSubstFonts[16] = { {"Symbol", 0.576} }; +//------------------------------------------------------------------------ + +static void outputToFile(void *stream, char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} + //------------------------------------------------------------------------ // XOutputFont //------------------------------------------------------------------------ @@ -581,18 +588,30 @@ void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap, #if HAVE_T1LIB_H XOutputT1FontFile::~XOutputT1FontFile() { delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } } #endif #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) XOutputFTFontFile::~XOutputFTFontFile() { delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } } #endif #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) XOutputTTFontFile::~XOutputTTFontFile() { delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } } #endif @@ -605,18 +624,20 @@ XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA, xOut = xOutA; #if HAVE_T1LIB_H - t1Engine = NULL; t1libControl = t1libControlA; + t1Engine = NULL; + t1FontFiles = NULL; #endif #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + freetypeControl = freetypeControlA; ftEngine = NULL; + ftFontFiles = NULL; #endif #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) - ttEngine = NULL; -#endif -#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H freetypeControl = freetypeControlA; + ttEngine = NULL; + ttFontFiles = NULL; #endif clear(); @@ -626,8 +647,8 @@ XOutputFontCache::~XOutputFontCache() { delFonts(); } -void XOutputFontCache::startDoc(int screenNum, Colormap colormap, - GBool trueColor, +void XOutputFontCache::startDoc(int screenNum, Visual *visual, + Colormap colormap, GBool trueColor, int rMul, int gMul, int bMul, int rShift, int gShift, int bShift, Gulong *colors, int numColors) { @@ -636,8 +657,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, #if HAVE_T1LIB_H if (t1libControl != fontRastNone) { - t1Engine = new T1FontEngine(display, DefaultVisual(display, screenNum), - depth, colormap, + t1Engine = new T1FontEngine(display, visual, depth, colormap, t1libControl == fontRastAALow || t1libControl == fontRastAAHigh, t1libControl == fontRastAAHigh); @@ -656,8 +676,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (freetypeControl != fontRastNone) { - ftEngine = new FTFontEngine(display, DefaultVisual(display, screenNum), - depth, colormap, + ftEngine = new FTFontEngine(display, visual, depth, colormap, freetypeControl == fontRastAALow || freetypeControl == fontRastAAHigh); if (ftEngine->isOk()) { @@ -675,8 +694,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (freetypeControl != fontRastNone) { - ttEngine = new TTFontEngine(display, DefaultVisual(display, screenNum), - depth, colormap, + ttEngine = new TTFontEngine(display, visual, depth, colormap, freetypeControl == fontRastAALow || freetypeControl == fontRastAAHigh); if (ttEngine->isOk()) { @@ -702,25 +720,37 @@ void XOutputFontCache::delFonts() { #if HAVE_T1LIB_H // delete Type 1 font files - deleteGList(t1FontFiles, XOutputT1FontFile); + if (t1FontFiles) { + deleteGList(t1FontFiles, XOutputT1FontFile); + t1FontFiles = NULL; + } if (t1Engine) { delete t1Engine; + t1Engine = NULL; } #endif #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) // delete FreeType font files - deleteGList(ftFontFiles, XOutputFTFontFile); + if (ftFontFiles) { + deleteGList(ftFontFiles, XOutputFTFontFile); + ftFontFiles = NULL; + } if (ftEngine) { delete ftEngine; + ftEngine = NULL; } #endif #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) // delete TrueType fonts - deleteGList(ttFontFiles, XOutputTTFontFile); + if (ttFontFiles) { + deleteGList(ttFontFiles, XOutputTTFontFile); + ttFontFiles = NULL; + } if (ttEngine) { delete ttEngine; + ttEngine = NULL; } #endif } @@ -812,6 +842,7 @@ XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont, } #endif break; + case fontCIDType0: case fontCIDType0C: #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (freetypeControl != fontRastNone) { @@ -977,8 +1008,8 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp, case displayFontT1: #if HAVE_T1LIB_H - if (t1libControl != fontRastNone) { - font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gfxFont, + if (t1libControl != fontRastNone && !gfxFont->isCIDFont()) { + font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, m11Orig, m12Orig, m21Orig, m22Orig, m11, m12, m21, m22, subst); } @@ -986,9 +1017,9 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp, #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (!font) { if (freetypeControl != fontRastNone) { - font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gfxFont, + font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, m11Orig, m12Orig, m21Orig, m22Orig, - m11, m12, m21, m22, subst); + m11, m12, m21, m22, gFalse, subst); } } #endif @@ -1001,14 +1032,14 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp, case displayFontTT: #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (freetypeControl != fontRastNone) { - font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gfxFont, + font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, m11Orig, m12Orig, m21Orig, m22Orig, - m11, m12, m21, m22, subst); + m11, m12, m21, m22, gFalse, subst); } #endif #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) if (freetypeControl != fontRastNone) { - font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gfxFont, + font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, m11Orig, m12Orig, m21Orig, m22Orig, m11, m12, m21, m22, subst); } @@ -1071,10 +1102,19 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref, if (gfxFont->getType() == fontType1C) { if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { fclose(f); + unlink(fileName->getCString()); + delete fileName; return NULL; } ff = new Type1CFontFile(fontBuf, fontLen); - ff->convertToType1(f); + if (!ff->isOk()) { + delete ff; + gfree(fontBuf); + unlink(fileName->getCString()); + delete fileName; + return NULL; + } + ff->convertToType1(outputToFile, f); delete ff; gfree(fontBuf); } else { // fontType1 @@ -1091,17 +1131,19 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref, fclose(f); // create the Font - font = tryGetT1FontFromFile(xref, fileName, gfxFont, + font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont, m11, m12, m21, m22, m11, m12, m21, m22, gFalse); - // remove the temporary file + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file unlink(fileName->getCString()); + delete fileName; // check for an external font file } else if ((fileName = gfxFont->getExtFontFile())) { - font = tryGetT1FontFromFile(xref, fileName, gfxFont, + font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont, m11, m12, m21, m22, m11, m12, m21, m22, gFalse); @@ -1114,6 +1156,7 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref, XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref, GString *fileName, + GBool deleteFile, GfxFont *gfxFont, double m11Orig, double m12Orig, @@ -1134,13 +1177,18 @@ XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref, error(-1, "Couldn't create t1lib font from '%s'", fileName->getCString()); delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } return NULL; } // add to list id = gfxFont->getID(); t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen, - subst, fontFile)); + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); // create the Font font = new XOutputT1Font(gfxFont->getID(), fontFile, @@ -1205,6 +1253,8 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref, gfxFont->getType() == fontCIDType2) { if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { fclose(f); + unlink(fileName->getCString()); + delete fileName; return NULL; } ff = new TrueTypeFontFile(fontBuf, fontLen); @@ -1236,19 +1286,21 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref, fclose(f); // create the Font - font = tryGetFTFontFromFile(xref, fileName, gfxFont, + font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont, m11, m12, m21, m22, - m11, m12, m21, m22, gFalse); + m11, m12, m21, m22, gTrue, gFalse); - // remove the temporary file + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file unlink(fileName->getCString()); + delete fileName; // check for an external font file } else if ((fileName = gfxFont->getExtFontFile())) { - font = tryGetFTFontFromFile(xref, fileName, gfxFont, + font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont, m11, m12, m21, m22, - m11, m12, m21, m22, gFalse); + m11, m12, m21, m22, gFalse, gFalse); } else { font = NULL; @@ -1259,6 +1311,7 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref, XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref, GString *fileName, + GBool deleteFile, GfxFont *gfxFont, double m11Orig, double m12Orig, @@ -1266,36 +1319,66 @@ XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref, double m22Orig, double m11, double m12, double m21, double m22, + GBool embedded, GBool subst) { Ref *id; FTFontFile *fontFile; XOutputFont *font; + char *buf; + int len; + FILE *f; + TrueTypeFontFile *ff; + Gushort *codeToGID; // create the FreeType font file if (gfxFont->isCIDFont()) { if (gfxFont->getType() == fontCIDType2) { fontFile = new FTFontFile(ftEngine, fileName->getCString(), ((GfxCIDFont *)gfxFont)->getCIDToGID(), - ((GfxCIDFont *)gfxFont)->getCIDToGIDLen()); - } else { // fontCIDType0C - fontFile = new FTFontFile(ftEngine, fileName->getCString()); + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(), + embedded); + } else { // fontCIDType0, fontCIDType0C + fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded); } } else { + if (!(f = fopen(fileName->getCString(), "rb"))) { + return NULL; + } + fseek(f, 0, SEEK_END); + len = (int)ftell(f); + fseek(f, 0, SEEK_SET); + buf = (char *)gmalloc(len); + if ((int)fread(buf, 1, len, f) != len) { + gfree(buf); + fclose(f); + return NULL; + } + fclose(f); + ff = new TrueTypeFontFile(buf, len); + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); fontFile = new FTFontFile(ftEngine, fileName->getCString(), ((Gfx8BitFont *)gfxFont)->getEncoding(), - ((Gfx8BitFont *)gfxFont)->getHasEncoding()); + codeToGID); + gfree(codeToGID); + delete ff; + gfree(buf); } if (!fontFile->isOk()) { error(-1, "Couldn't create FreeType font from '%s'", fileName->getCString()); delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } return NULL; } // add to list id = gfxFont->getID(); ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen, - subst, fontFile)); + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); // create the Font font = new XOutputFTFont(gfxFont->getID(), fontFile, @@ -1363,16 +1446,18 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref, fclose(f); // create the Font - font = tryGetTTFontFromFile(xref, fileName, gfxFont, + font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont, m11, m12, m21, m22, m11, m12, m21, m22, gFalse); - // remove the temporary file + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file unlink(fileName->getCString()); + delete fileName; } else if ((fileName = gfxFont->getExtFontFile())) { - font = tryGetTTFontFromFile(xref, fileName, gfxFont, + font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont, m11, m12, m21, m22, m11, m12, m21, m22, gFalse); @@ -1385,6 +1470,7 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref, XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref, GString *fileName, + GBool deleteFile, GfxFont *gfxFont, double m11Orig, double m12Orig, @@ -1412,13 +1498,18 @@ XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref, error(-1, "Couldn't create FreeType font from '%s'", fileName->getCString()); delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } return NULL; } // add to list id = gfxFont->getID(); ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen, - subst, fontFile)); + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); // create the Font font = new XOutputTTFont(gfxFont->getID(), fontFile, @@ -1570,6 +1661,7 @@ struct T3GlyphStack { GC origStrokeGC; GC origFillGC; Region origClipRegion; + double origCTM4, origCTM5; double wx, wy; // untransformed glyph metrics T3GlyphStack *next; }; @@ -1578,53 +1670,63 @@ struct T3GlyphStack { // XOutputDev //------------------------------------------------------------------------ -XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA, - Colormap colormapA, GBool reverseVideoA, - unsigned long paperColor, GBool installCmap, - int rgbCubeSize) { +XOutputDev::XOutputDev(Display *displayA, int screenNumA, + Visual *visualA, Colormap colormapA, + GBool reverseVideoA, unsigned long paperColorA, + GBool installCmap, int rgbCubeSize, + int forceDepth) { XVisualInfo visualTempl; XVisualInfo *visualList; int nVisuals; Gulong mask; - XGCValues gcValues; XColor xcolor; XColor *xcolors; - int r, g, b, n, m, i; + int r, g, b, n, m; GBool ok; + // no document yet xref = NULL; - // get display/pixmap info + // display / screen / visual / colormap display = displayA; - screenNum = DefaultScreen(display); - pixmap = pixmapA; - depth = depthA; + screenNum = screenNumA; + visual = visualA; colormap = colormapA; + // no pixmap yet + pixmapW = pixmapH = 0; + // check for TrueColor visual - trueColor = gFalse; - if (depth == 0) { - depth = DefaultDepth(display, screenNum); - visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals); - for (i = 0; i < nVisuals; ++i) { - if (visualList[i].visual == DefaultVisual(display, screenNum)) { - if (visualList[i].c_class == TrueColor) { - trueColor = gTrue; - for (mask = visualList[i].red_mask, rShift = 0; - mask && !(mask & 1); - mask >>= 1, ++rShift) ; - rMul = (int)mask; - for (mask = visualList[i].green_mask, gShift = 0; - mask && !(mask & 1); - mask >>= 1, ++gShift) ; - gMul = (int)mask; - for (mask = visualList[i].blue_mask, bShift = 0; - mask && !(mask & 1); - mask >>= 1, ++bShift) ; - bMul = (int)mask; - } - break; - } + if (forceDepth != 0) { + depth = forceDepth; + trueColor = depth >= 16; + } else { + visualTempl.visualid = XVisualIDFromVisual(visual); + visualList = XGetVisualInfo(display, VisualIDMask, + &visualTempl, &nVisuals); + if (nVisuals < 1) { + // this shouldn't happen + XFree((XPointer)visualList); + visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, + &nVisuals); + } + depth = visualList->depth; + if (visualList->c_class == TrueColor) { + trueColor = gTrue; + for (mask = visualList->red_mask, rShift = 0; + mask && !(mask & 1); + mask >>= 1, ++rShift) ; + rMul = (int)mask; + for (mask = visualList->green_mask, gShift = 0; + mask && !(mask & 1); + mask >>= 1, ++gShift) ; + gMul = (int)mask; + for (mask = visualList->blue_mask, bShift = 0; + mask && !(mask & 1); + mask >>= 1, ++bShift) ; + bMul = (int)mask; + } else { + trueColor = gFalse; } XFree((XPointer)visualList); } @@ -1710,37 +1812,23 @@ XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA, } } - // reverse video mode + // misc parameters reverseVideo = reverseVideoA; - - // allocate GCs - gcValues.foreground = BlackPixel(display, screenNum); - gcValues.background = WhitePixel(display, screenNum); - gcValues.line_width = 0; - gcValues.line_style = LineSolid; - strokeGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - fillGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - gcValues.foreground = paperColor; - paperGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - - // no clip region yet - clipRegion = NULL; + paperColor = paperColorA; // set up the font cache and fonts gfxFont = NULL; font = NULL; + needFontUpdate = gFalse; fontCache = new XOutputFontCache(display, depth, this, globalParams->getT1libControl(), globalParams->getFreeTypeControl()); nT3Fonts = 0; t3GlyphStack = NULL; + // no text outline clipping path + textClipPath = NULL; + // empty state stack save = NULL; @@ -1755,12 +1843,6 @@ XOutputDev::~XOutputDev() { for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } - XFreeGC(display, strokeGC); - XFreeGC(display, fillGC); - XFreeGC(display, paperGC); - if (clipRegion) { - XDestroyRegion(clipRegion); - } delete text; } @@ -1768,7 +1850,7 @@ void XOutputDev::startDoc(XRef *xrefA) { int i; xref = xrefA; - fontCache->startDoc(screenNum, colormap, trueColor, rMul, gMul, bMul, + fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul, rShift, gShift, bShift, colors, numColors); for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; @@ -1777,39 +1859,29 @@ void XOutputDev::startDoc(XRef *xrefA) { } void XOutputDev::startPage(int pageNum, GfxState *state) { - XOutputState *s; XGCValues gcValues; XRectangle rect; - // clear state stack - while (save) { - s = save; - save = save->next; - XFreeGC(display, s->strokeGC); - XFreeGC(display, s->fillGC); - XDestroyRegion(s->clipRegion); - delete s; - } - save = NULL; - // default line flatness flatness = 0; - // reset GCs + // allocate GCs gcValues.foreground = BlackPixel(display, screenNum); gcValues.background = WhitePixel(display, screenNum); gcValues.line_width = 0; gcValues.line_style = LineSolid; - XChangeGC(display, strokeGC, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - XChangeGC(display, fillGC, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - - // clear clipping region - if (clipRegion) - XDestroyRegion(clipRegion); + strokeGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + fillGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + gcValues.foreground = paperColor; + paperGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + + // initialize clip region clipRegion = XCreateRegion(); rect.x = rect.y = 0; rect.width = pixmapW; @@ -1826,40 +1898,85 @@ void XOutputDev::startPage(int pageNum, GfxState *state) { XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH); // clear text object - text->clear(); + text->startPage(state); } void XOutputDev::endPage() { - text->coalesce(); + XOutputState *s; + + text->coalesce(gTrue); + + // clear state stack, free all GCs, free the clip region + while (save) { + s = save; + save = save->next; + XFreeGC(display, s->strokeGC); + XFreeGC(display, s->fillGC); + XDestroyRegion(s->clipRegion); + delete s; + } + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + XFreeGC(display, paperGC); + XDestroyRegion(clipRegion); } void XOutputDev::drawLink(Link *link, Catalog *catalog) { - double x1, y1, x2, y2, w; + double x1, y1, x2, y2; + LinkBorderStyle *borderStyle; GfxRGB rgb; + double *dash; + char dashList[20]; + int dashLength; XPoint points[5]; - int x, y; + int x, y, i; - link->getBorder(&x1, &y1, &x2, &y2, &w); - if (w > 0) { - rgb.r = 0; - rgb.g = 0; - rgb.b = 1; + link->getRect(&x1, &y1, &x2, &y2); + borderStyle = link->getBorderStyle(); + if (borderStyle->getWidth() > 0) { + borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b); XSetForeground(display, strokeGC, findColor(&rgb)); - XSetLineAttributes(display, strokeGC, xoutRound(w), - LineSolid, CapRound, JoinRound); - cvtUserToDev(x1, y1, &x, &y); - points[0].x = points[4].x = x; - points[0].y = points[4].y = y; - cvtUserToDev(x2, y1, &x, &y); - points[1].x = x; - points[1].y = y; - cvtUserToDev(x2, y2, &x, &y); - points[2].x = x; - points[2].y = y; - cvtUserToDev(x1, y2, &x, &y); - points[3].x = x; - points[3].y = y; - XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin); + borderStyle->getDash(&dash, &dashLength); + if (borderStyle->getType() == linkBorderDashed && dashLength > 0) { + if (dashLength > 20) { + dashLength = 20; + } + for (i = 0; i < dashLength; ++i) { + if ((dashList[i] = xoutRound(dash[i])) == 0) { + dashList[i] = 1; + } + } + XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()), + LineOnOffDash, CapButt, JoinMiter); + XSetDashes(display, strokeGC, 0, dashList, dashLength); + } else { + XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()), + LineSolid, CapButt, JoinMiter); + } + if (borderStyle->getType() == linkBorderUnderlined) { + cvtUserToDev(x1, y1, &x, &y); + points[0].x = x; + points[0].y = y; + cvtUserToDev(x2, y1, &x, &y); + points[1].x = x; + points[1].y = y; + XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y, + points[1].x, points[1].y); + } else { + cvtUserToDev(x1, y1, &x, &y); + points[0].x = points[4].x = x; + points[0].y = points[4].y = y; + cvtUserToDev(x2, y1, &x, &y); + points[1].x = x; + points[1].y = y; + cvtUserToDev(x2, y2, &x, &y); + points[2].x = x; + points[2].y = y; + cvtUserToDev(x1, y2, &x, &y); + points[3].x = x; + points[3].y = y; + XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin); + } } } @@ -1909,6 +2026,9 @@ void XOutputDev::restoreState(GfxState *state) { s = save; save = save->next; delete s; + + // we'll need to restore the font + needFontUpdate = gTrue; } } @@ -1918,7 +2038,7 @@ void XOutputDev::updateAll(GfxState *state) { updateMiterLimit(state); updateFillColor(state); updateStrokeColor(state); - updateFont(state); + needFontUpdate = gTrue; } void XOutputDev::updateCTM(GfxState *state, double m11, double m12, @@ -2026,6 +2146,10 @@ void XOutputDev::updateStrokeColor(GfxState *state) { void XOutputDev::updateFont(GfxState *state) { double m11, m12, m21, m22; + needFontUpdate = gFalse; + + text->updateFont(state); + if (!(gfxFont = state->getFont())) { font = NULL; return; @@ -2042,8 +2166,6 @@ void XOutputDev::updateFont(GfxState *state) { font->updateGC(fillGC); font->updateGC(strokeGC); } - - text->updateFont(state); } void XOutputDev::stroke(GfxState *state) { @@ -2052,7 +2174,8 @@ void XOutputDev::stroke(GfxState *state) { int n, size, numPoints, i, j; // transform points - n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse); + n = convertPath(state, state->getPath(), + &points, &size, &numPoints, &lengths, gFalse); // draw each subpath j = 0; @@ -2095,7 +2218,8 @@ void XOutputDev::doFill(GfxState *state, int rule) { XSetFillRule(display, fillGC, rule); // transform points, build separate polygons - n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); + n = convertPath(state, state->getPath(), + &points, &size, &numPoints, &lengths, gTrue); // fill them j = 0; @@ -2117,41 +2241,109 @@ void XOutputDev::doFill(GfxState *state, int rule) { } void XOutputDev::clip(GfxState *state) { - doClip(state, WindingRule); + doClip(state, state->getPath(), WindingRule); } void XOutputDev::eoClip(GfxState *state) { - doClip(state, EvenOddRule); + doClip(state, state->getPath(), EvenOddRule); } -void XOutputDev::doClip(GfxState *state, int rule) { +void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) { + GfxSubpath *subpath; Region region, region2; + XPoint rect[5]; XPoint *points; int *lengths; + double x0, y0, x1, y1, x2, y2, x3, y3; + GBool gotRect; int n, size, numPoints, i, j; - // transform points, build separate polygons - n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); - - // construct union of subpath regions - // (XPolygonRegion chokes if there aren't at least three points -- - // this happens if the PDF file does moveto/closepath/clip, which - // sets an empty clipping region) - if (lengths[0] > 2) { - region = XPolygonRegion(points, lengths[0], rule); - } else { - region = XCreateRegion(); + // special case for rectangular clipping paths -- this is a common + // case, and we want to make sure not to clip an extra pixel on the + // right and bottom edges due to the difference between the PDF and + // X rendering models + gotRect = gFalse; + if (path->getNumSubpaths() == 1) { + subpath = path->getSubpath(0); + if ((subpath->isClosed() && subpath->getNumPoints() == 5) || + (!subpath->isClosed() && subpath->getNumPoints() == 4)) { + state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0); + state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1); + state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2); + state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3); + if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 && + fabs(y0-y3) < 1 && fabs(y1-y2) < 1) { + if (x0 < x2) { + rect[0].x = rect[1].x = rect[4].x = (int)floor(x0); + rect[2].x = rect[3].x = (int)floor(x2) + 1; + } else { + rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1; + rect[2].x = rect[3].x = (int)floor(x2); + } + if (y0 < y1) { + rect[0].y = rect[3].y = rect[4].y = (int)floor(y0); + rect[1].y = rect[2].y = (int)floor(y1) + 1; + } else { + rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1; + rect[1].y = rect[2].y = (int)floor(y1); + } + gotRect = gTrue; + } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 && + fabs(y0-y1) < 1 && fabs(y2-y3) < 1) { + if (x0 < x1) { + rect[0].x = rect[3].x = rect[4].x = (int)floor(x0); + rect[1].x = rect[2].x = (int)floor(x1) + 1; + } else { + rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1; + rect[1].x = rect[2].x = (int)floor(x1); + } + if (y0 < y2) { + rect[0].y = rect[1].y = rect[4].y = (int)floor(y0); + rect[2].y = rect[3].y = (int)floor(y2) + 1; + } else { + rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1; + rect[2].y = rect[3].y = (int)floor(y2); + } + gotRect = gTrue; + } + } } - j = lengths[0] + 1; - for (i = 1; i < n; ++i) { - if (lengths[i] > 2) { - region2 = XPolygonRegion(points + j, lengths[i], rule); + + if (gotRect) { + region = XPolygonRegion(rect, 5, EvenOddRule); + + } else { + // transform points, build separate polygons + n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue); + + // construct union of subpath regions + // (XPolygonRegion chokes if there aren't at least three points -- + // this happens if the PDF file does moveto/closepath/clip, which + // sets an empty clipping region) + if (lengths[0] > 2) { + region = XPolygonRegion(points, lengths[0], rule); } else { - region2 = XCreateRegion(); + region = XCreateRegion(); + } + j = lengths[0] + 1; + for (i = 1; i < n; ++i) { + if (lengths[i] > 2) { + region2 = XPolygonRegion(points + j, lengths[i], rule); + } else { + region2 = XCreateRegion(); + } + XUnionRegion(region2, region, region); + XDestroyRegion(region2); + j += lengths[i] + 1; + } + + // free points and lengths arrays + if (points != tmpPoints) { + gfree(points); + } + if (lengths != tmpLengths) { + gfree(lengths); } - XUnionRegion(region2, region, region); - XDestroyRegion(region2); - j += lengths[i] + 1; } // intersect region with clipping region @@ -2159,12 +2351,6 @@ void XOutputDev::doClip(GfxState *state, int rule) { XDestroyRegion(region); XSetRegion(display, strokeGC, clipRegion); XSetRegion(display, fillGC, clipRegion); - - // free points and lengths arrays - if (points != tmpPoints) - gfree(points); - if (lengths != tmpLengths) - gfree(lengths); } // @@ -2176,15 +2362,14 @@ void XOutputDev::doClip(GfxState *state, int rule) { // Then it connects subaths within a single compound polygon to a single // point so that X can fill the polygon (sort of). // -int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, +int XOutputDev::convertPath(GfxState *state, GfxPath *path, + XPoint **points, int *size, int *numPoints, int **lengths, GBool fillHack) { - GfxPath *path; BoundingRect *rects; BoundingRect rect; int n, i, ii, j, k, k0; // get path and number of subpaths - path = state->getPath(); n = path->getNumSubpaths(); // allocate lengths array @@ -2246,15 +2431,15 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, // kludge: munge any points that are *way* out of bounds - these can // crash certain (buggy) X servers for (i = 0; i < *numPoints; ++i) { - if ((*points)[i].x < -pixmapW) { - (*points)[i].x = -pixmapW; - } else if ((*points)[i].x > 2 * pixmapW) { - (*points)[i].x = 2 * pixmapW; + if ((*points)[i].x < -4 * pixmapW) { + (*points)[i].x = -4 * pixmapW; + } else if ((*points)[i].x > 4 * pixmapW) { + (*points)[i].x = 4 * pixmapW; } if ((*points)[i].y < -pixmapH) { - (*points)[i].y = -pixmapH; - } else if ((*points)[i].y > 2 * pixmapH) { - (*points)[i].y = 2 * pixmapH; + (*points)[i].y = -4 * pixmapH; + } else if ((*points)[i].y > 4 * pixmapH) { + (*points)[i].y = 4 * pixmapH; } } @@ -2450,11 +2635,11 @@ void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) { } void XOutputDev::beginString(GfxState *state, GString *s) { - text->beginString(state); + text->beginWord(state, state->getCurX(), state->getCurY()); } void XOutputDev::endString(GfxState *state) { - text->endString(); + text->endWord(); } void XOutputDev::drawChar(GfxState *state, double x, double y, @@ -2468,7 +2653,11 @@ void XOutputDev::drawChar(GfxState *state, double x, double y, double *ctm; double saveCTM[6]; - text->addChar(state, x, y, dx, dy, u, uLen); + if (needFontUpdate) { + updateFont(state); + } + + text->addChar(state, x, y, dx, dy, code, u, uLen); if (!font) { return; @@ -2476,7 +2665,7 @@ void XOutputDev::drawChar(GfxState *state, double x, double y, // check for invisible text -- this is used by Acrobat Capture render = state->getRender(); - if ((render & 3) == 3) { + if (render == 3) { return; } @@ -2525,11 +2714,22 @@ void XOutputDev::drawChar(GfxState *state, double x, double y, } } -#if 0 //~ unimplemented: clipping to char path // clip if (render & 4) { + if (font->hasGetCharPath()) { + saveCurX = state->getCurX(); + saveCurY = state->getCurY(); + font->getCharPath(state, code, u, uLen); + state->getPath()->offset(x1, y1); + if (textClipPath) { + textClipPath->append(state->getPath()); + } else { + textClipPath = state->getPath()->copy(); + } + state->clearPath(); + state->moveTo(saveCurX, saveCurY); + } } -#endif } GBool XOutputDev::beginType3Char(GfxState *state, @@ -2542,6 +2742,9 @@ GBool XOutputDev::beginType3Char(GfxState *state, double x1, y1, xMin, yMin, xMax, yMax, xt, yt; int i, j; + if (needFontUpdate) { + updateFont(state); + } if (!gfxFont) { return gFalse; } @@ -2625,9 +2828,7 @@ GBool XOutputDev::beginType3Char(GfxState *state, (int)floor(yMin - yt), (int)ceil(xMax) - (int)floor(xMin) + 3, (int)ceil(yMax) - (int)floor(yMin) + 3, - display, - DefaultVisual(display, screenNum), - depth, pixmap); + display, visual, depth, pixmap); } } t3Font = t3FontCache[0]; @@ -2645,7 +2846,7 @@ GBool XOutputDev::beginType3Char(GfxState *state, } text->addChar(state, 0, 0, t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy, - u, uLen); + code, u, uLen); drawType3Glyph(t3Font, &t3Font->cacheTags[i+j], t3Font->cacheData + (i+j) * t3Font->glyphSize, xt, yt, &color); @@ -2676,6 +2877,7 @@ void XOutputDev::endType3Char(GfxState *state) { Gulong pixel; double alpha; T3GlyphStack *t3gs; + double *ctm; if (t3GlyphStack->cacheable) { image = t3GlyphStack->cache->image; @@ -2716,9 +2918,14 @@ void XOutputDev::endType3Char(GfxState *state) { drawType3Glyph(t3GlyphStack->cache, t3GlyphStack->cacheTag, t3GlyphStack->cacheData, t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color); + // the CTM must be restored here in order for TextPage::addChar to + // work correctly + ctm = state->getCTM(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); } text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy, - t3GlyphStack->u, t3GlyphStack->uLen); + t3GlyphStack->code, t3GlyphStack->u, t3GlyphStack->uLen); t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; delete t3gs; @@ -2813,11 +3020,61 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy, XRectangle rect; double *ctm; T3FontCache *t3Font; + double xt, yt, xMin, xMax, yMin, yMax, x1, y1; int i, j; + t3Font = t3GlyphStack->cache; + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; + + // check for a valid bbox + state->transform(0, 0, &xt, &yt); + state->transform(llx, lly, &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(llx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, lly, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + if (xMin - xt < t3Font->glyphX || + yMin - yt < t3Font->glyphY || + xMax - xt > t3Font->glyphX + t3Font->glyphW || + yMax - yt > t3Font->glyphY + t3Font->glyphH) { + error(-1, "Bad bounding box in Type 3 glyph"); + return; + } + // allocate a cache entry t3GlyphStack->cacheable = gTrue; - t3Font = t3GlyphStack->cache; i = t3GlyphStack->cacheIdx; for (j = 0; j < t3Font->cacheAssoc; ++j) { if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { @@ -2829,8 +3086,6 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy, ++t3Font->cacheTags[i+j].mru; } } - t3GlyphStack->wx = wx; - t3GlyphStack->wy = wy; t3GlyphStack->cacheTag->wx = wx; t3GlyphStack->cacheTag->wy = wy; @@ -2878,11 +3133,29 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy, XSetRegion(display, strokeGC, clipRegion); XSetRegion(display, fillGC, clipRegion); ctm = state->getCTM(); + t3GlyphStack->origCTM4 = ctm[4]; + t3GlyphStack->origCTM5 = ctm[5]; state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY); } -inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) { +void XOutputDev::endTextObject(GfxState *state) { + double *ctm; + double saveCTM[6]; + + if (textClipPath) { + ctm = state->getCTM(); + memcpy(saveCTM, ctm, 6 * sizeof(double)); + state->setCTM(1, 0, 0, 1, 0, 0); + doClip(state, textClipPath, WindingRule); + state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3], + saveCTM[4], saveCTM[5]); + delete textClipPath; + textClipPath = NULL; + } +} + +inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) { double gray; int r, g, b; Gulong pixel; @@ -2894,30 +3167,26 @@ inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) { pixel = ((Gulong)r << rShift) + ((Gulong)g << gShift) + ((Gulong)b << bShift); - err->r = x->r - (double)r / rMul; - err->g = x->g - (double)g / gMul; - err->b = x->b - (double)b / bMul; + actual->r = (double)r / rMul; + actual->g = (double)g / gMul; + actual->b = (double)b / bMul; } else if (numColors == 1) { gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b; if (gray < 0.5) { pixel = colors[0]; - err->r = x->r; - err->g = x->g; - err->b = x->b; + actual->r = actual->g = actual->b = 0; } else { pixel = colors[1]; - err->r = x->r - 1; - err->g = x->g - 1; - err->b = x->b - 1; + actual->r = actual->g = actual->b = 1; } } else { r = xoutRound(x->r * (numColors - 1)); g = xoutRound(x->g * (numColors - 1)); b = xoutRound(x->b * (numColors - 1)); pixel = colors[(r * numColors + g) * numColors + b]; - err->r = x->r - (double)r / (numColors - 1); - err->g = x->g - (double)g / (numColors - 1); - err->b = x->b - (double)b / (numColors - 1); + actual->r = (double)r / (numColors - 1); + actual->g = (double)g / (numColors - 1); + actual->b = (double)b / (numColors - 1); } return pixel; } @@ -3118,8 +3387,7 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); // allocate XImage and read from page pixmap - image = XCreateImage(display, DefaultVisual(display, screenNum), - depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); image->data = (char *)gmalloc(bh * image->bytes_per_line); XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, image, cx1, cy1); @@ -3167,13 +3435,13 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, if (n > 0) { p = pixBuf; for (i = 0; i < n; ++i) { - for (j = 0; j < width; ++j) { - imgStr->getPixel(p); - if (invert) { - *p ^= 1; + memcpy(p, imgStr->getLine(), width); + if (invert) { + for (j = 0; j < width; ++j) { + p[j] ^= 1; } - ++p; } + p += width; } } lastYStep = yStep; @@ -3272,14 +3540,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int yp, yq, yt, yStep, lastYStep; int xp, xq, xt, xStep, xSrc; GfxRGB *pixBuf; - Guchar *alphaBuf; + Guchar *pixBuf1, *alphaBuf; Guchar pixBuf2[gfxColorMaxComps]; - GfxRGB color2, err, errRight; - GfxRGB *errDown; + GfxRGB color2, color3, actual, err, errRight; + GfxRGB *errDown0, *errDown1, *errDownTmp; double r0, g0, b0, alpha, mul; Gulong pix; GfxRGB *p; - Guchar *q; + Guchar *q, *p1, *p2; GBool oneBitMode; GfxRGB oneBitRGB[2]; int x, y, x1, y1, x2, y2; @@ -3289,6 +3557,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, nComps = colorMap->getNumPixelComps(); nVals = width * nComps; nBits = colorMap->getBits(); + oneBitMode = nComps == 1 && nBits == 1 && !maskColors; dither = nComps > 1 || nBits > 1; // get CTM, check for singular matrix @@ -3321,6 +3590,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } tx = xoutRound(ctm[2] + ctm[4]); ty = xoutRound(ctm[3] + ctm[5]); + if (xScale < 0) { + // this is the right edge which needs to be (left + width - 1) + --tx; + } + if (yScale < 0) { + // this is the bottom edge which needs to be (top + height - 1) + --ty; + } // use ceil() to avoid gaps between "striped" images scaledWidth = (int)ceil(fabs(xScale)); xSign = (xScale < 0) ? -1 : 1; @@ -3417,7 +3694,13 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, xq = width % scaledWidth; // allocate pixel buffer - pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB)); + if (oneBitMode) { + pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + pixBuf = NULL; + } else { + pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB)); + pixBuf1 = NULL; + } if (maskColors) { alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); } else { @@ -3425,8 +3708,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } // allocate XImage - image = XCreateImage(display, DefaultVisual(display, screenNum), - depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); image->data = (char *)gmalloc(bh * image->bytes_per_line); // if the transform is anything other than a 0/90/180/270 degree @@ -3441,16 +3723,18 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // allocate error diffusion accumulators if (dither) { - errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB)); - for (j = 0; j < bw; ++j) { - errDown[j].r = errDown[j].g = errDown[j].b = 0; + errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB)); + errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB)); + for (j = 0; j < scaledWidth + 2; ++j) { + errDown0[j].r = errDown0[j].g = errDown0[j].b = 0; + errDown1[j].r = errDown1[j].g = errDown1[j].b = 0; } } else { - errDown = NULL; + errDown0 = errDown1 = NULL; } // optimize the one-bit-deep image case - if ((oneBitMode = nComps == 1 && nBits == 1)) { + if (oneBitMode) { pixBuf2[0] = 0; colorMap->getRGB(pixBuf2, &oneBitRGB[0]); pixBuf2[0] = 1; @@ -3467,8 +3751,16 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, for (y = 0; y < scaledHeight; ++y) { - // initialize error diffusion accumulator - errRight.r = errRight.g = errRight.b = 0; + // initialize error diffusion accumulators + if (dither) { + errDownTmp = errDown0; + errDown0 = errDown1; + errDown1 = errDownTmp; + for (j = 0; j < scaledWidth + 2; ++j) { + errDown1[j].r = errDown1[j].g = errDown1[j].b = 0; + } + errRight.r = errRight.g = errRight.b = 0; + } // y scale Bresenham yStep = yp; @@ -3481,27 +3773,33 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // read row(s) from image n = (yp > 0) ? yStep : lastYStep; if (n > 0) { - p = pixBuf; - q = alphaBuf; - for (i = 0; i < n; ++i) { - for (j = 0; j < width; ++j) { - imgStr->getPixel(pixBuf2); - if (oneBitMode) { - *p++ = oneBitRGB[pixBuf2[0]]; - } else { - colorMap->getRGB(pixBuf2, p); + if (oneBitMode) { + p1 = pixBuf1; + for (i = 0; i < n; ++i) { + p2 = imgStr->getLine(); + memcpy(p1, p2, width); + p1 += width; + } + } else { + p = pixBuf; + q = alphaBuf; + for (i = 0; i < n; ++i) { + p2 = imgStr->getLine(); + for (j = 0; j < width; ++j) { + colorMap->getRGB(p2, p); ++p; - } - if (q) { - *q = 1; - for (k = 0; k < nComps; ++k) { - if (pixBuf2[k] < maskColors[2*k] || - pixBuf2[k] > maskColors[2*k]) { - *q = 0; - break; + if (q) { + *q = 1; + for (k = 0; k < nComps; ++k) { + if (p2[k] < maskColors[2*k] || + p2[k] > maskColors[2*k+1]) { + *q = 0; + break; + } } + ++q; } - ++q; + p2 += nComps; } } } @@ -3541,60 +3839,94 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // x and y scaling operations n = yStep > 0 ? yStep : 1; m = xStep > 0 ? xStep : 1; - p = pixBuf + xSrc; - r0 = g0 = b0 = 0; - q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL; - alpha = 0; - for (i = 0; i < n; ++i) { - for (j = 0; j < m; ++j) { - r0 += p->r; - g0 += p->g; - b0 += p->b; - ++p; - if (q) { - alpha += *q++; + if (oneBitMode) { + p1 = pixBuf1 + xSrc; + k = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + k += *p1++; } + p1 += width - m; } - p += width - m; + mul = (double)k / (double)(n * m); + r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r; + g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g; + b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b; + alpha = 0; + } else { + p = pixBuf + xSrc; + q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL; + alpha = 0; + r0 = g0 = b0 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + r0 += p->r; + g0 += p->g; + b0 += p->b; + ++p; + if (q) { + alpha += *q++; + } + } + p += width - m; + } + mul = 1 / (double)(n * m); + r0 *= mul; + g0 *= mul; + b0 *= mul; + alpha *= mul; } - mul = 1 / (double)(n * m); - r0 *= mul; - g0 *= mul; - b0 *= mul; - alpha *= mul; // x scale Bresenham xSrc += xStep; // compute pixel if (dither) { - color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r; + color2.r = r0 + errRight.r + errDown0[x + 1].r; if (color2.r > 1) { - color2.r = 1; + color3.r = 1; } else if (color2.r < 0) { - color2.r = 0; + color3.r = 0; + } else { + color3.r = color2.r; } - color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g; + color2.g = g0 + errRight.g + errDown0[x + 1].g; if (color2.g > 1) { - color2.g = 1; + color3.g = 1; } else if (color2.g < 0) { - color2.g = 0; + color3.g = 0; + } else { + color3.g = color2.g; } - color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b; + color2.b = b0 + errRight.b + errDown0[x + 1].b; if (color2.b > 1) { - color2.b = 1; + color3.b = 1; } else if (color2.b < 0) { - color2.b = 0; + color3.b = 0; + } else { + color3.b = color2.b; } - pix = findColor(&color2, &err); - errRight.r = errDown[tx + x2 - bx0].r = err.r / 2; - errRight.g = errDown[tx + x2 - bx0].g = err.g / 2; - errRight.b = errDown[tx + x2 - bx0].b = err.b / 2; + pix = findColor(&color3, &actual); + err.r = (color2.r - actual.r) / 16; + err.g = (color2.g - actual.g) / 16; + err.b = (color2.b - actual.b) / 16; + errRight.r = 7 * err.r; + errRight.g = 7 * err.g; + errRight.b = 7 * err.b; + errDown1[x].r += 3 * err.r; + errDown1[x].g += 3 * err.g; + errDown1[x].b += 3 * err.b; + errDown1[x + 1].r += 5 * err.r; + errDown1[x + 1].g += 5 * err.g; + errDown1[x + 1].b += 5 * err.b; + errDown1[x + 2].r = err.r; + errDown1[x + 2].g = err.g; + errDown1[x + 2].b = err.b; } else { color2.r = r0; color2.g = g0; color2.b = b0; - pix = findColor(&color2, &err); + pix = findColor(&color2, &actual); } // set pixel @@ -3610,25 +3942,35 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // free memory delete imgStr; - gfree(pixBuf); + if (oneBitMode) { + gfree(pixBuf1); + } else { + gfree(pixBuf); + } if (maskColors) { gfree(alphaBuf); } gfree(image->data); image->data = NULL; XDestroyImage(image); - gfree(errDown); + gfree(errDown0); + gfree(errDown1); } -GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom, - int *xMin, int *yMin, int *xMax, int *yMax) { +GBool XOutputDev::findText(Unicode *s, int len, + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + int *xMin, int *yMin, + int *xMax, int *yMax) { double xMin1, yMin1, xMax1, yMax1; xMin1 = (double)*xMin; yMin1 = (double)*yMin; xMax1 = (double)*xMax; yMax1 = (double)*yMax; - if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) { + if (text->findText(s, len, startAtTop, stopAtBottom, + startAtLast, stopAtLast, + &xMin1, &yMin1, &xMax1, &yMax1)) { *xMin = xoutRound(xMin1); *xMax = xoutRound(xMax1); *yMin = xoutRound(yMin1);