poppler/Annot.cc | 408 +++++++++++++++++++++++++++++++++++++++---------------- poppler/Annot.h | 3 2 files changed, 298 insertions(+), 113 deletions(-)
New commits: commit 46b3a70cae3b37cb4270a83afaddd6734442b752 Author: Fabio D'Urso <[email protected]> Date: Mon Apr 9 19:32:24 2012 +0200 Caption text rendering in AnnotLine diff --git a/poppler/Annot.cc b/poppler/Annot.cc index d733501..5d03e33 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -3101,111 +3101,202 @@ void AnnotLine::setIntent(AnnotLineIntent new_intent) { update ("IT", &obj1); } -void AnnotLine::draw(Gfx *gfx, GBool printing) { - Object obj; - double ca = 1; +void AnnotLine::generateLineAppearance() +{ + double borderWidth, ca = opacity; - if (!isVisible (printing)) - return; + appearBBox = new AnnotAppearanceBBox(rect); + appearBuf = new GooString (); + appearBuf->append ("q\n"); + if (color) { + setColor(color, gFalse); + } - if (appearance.isNull()) { - appearBBox = new AnnotAppearanceBBox(rect); - ca = opacity; + if (border) { + int i, dashLength; + double *dash; - appearBuf = new GooString (); - appearBuf->append ("q\n"); - if (color) - setColor(color, gFalse); + switch (border->getStyle()) { + case AnnotBorder::borderDashed: + appearBuf->append("["); + dashLength = border->getDashLength(); + dash = border->getDash(); + for (i = 0; i < dashLength; ++i) + appearBuf->appendf(" {0:.2f}", dash[i]); + appearBuf->append(" ] 0 d\n"); + break; + default: + appearBuf->append("[] 0 d\n"); + break; + } + borderWidth = border->getWidth(); + appearBuf->appendf("{0:.2f} w\n", borderWidth); + appearBBox->setBorderWidth(borderWidth); + } else { + borderWidth = 0; + } - if (border) { - int i, dashLength; - double *dash; + const double x1 = coord1->getX(); + const double y1 = coord1->getY(); + const double x2 = coord2->getX(); + const double y2 = coord2->getY(); - switch (border->getStyle()) { - case AnnotBorder::borderDashed: - appearBuf->append("["); - dashLength = border->getDashLength(); - dash = border->getDash(); - for (i = 0; i < dashLength; ++i) - appearBuf->appendf(" {0:.2f}", dash[i]); - appearBuf->append(" ] 0 d\n"); - break; - default: - appearBuf->append("[] 0 d\n"); - break; + // Main segment length + const double main_len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); + + // Main segment becomes positive x direction, coord1 becomes (0,0) + Matrix matr; + const double angle = atan2(y2 - y1, x2 - x1); + matr.m[0] = matr.m[3] = cos(angle); + matr.m[1] = sin(angle); + matr.m[2] = -matr.m[1]; + matr.m[4] = x1-rect->x1; + matr.m[5] = y1-rect->y1; + + double tx, ty, captionwidth = 0, captionheight = 0; + AnnotLineCaptionPos actualCaptionPos = captionPos; + const double fontsize = 9; + const double captionhmargin = 2; // Left and right margin (inline caption only) + const double captionmaxwidth = main_len - 2 * captionhmargin; + + Object fontResDict; + GfxFont *font; + + // Calculate caption width and height + if (caption) { + font = createAnnotDrawFont(xref, &fontResDict); + int lines = 0; + int i = 0; + while (i < contents->getLength()) { + GooString out; + double linewidth; + layoutText(contents, &out, &i, font, &linewidth, 0, NULL, gFalse); + linewidth *= fontsize; + if (linewidth > captionwidth) { + captionwidth = linewidth; } - appearBuf->appendf("{0:.2f} w\n", border->getWidth()); - appearBBox->setBorderWidth(border->getWidth()); + ++lines; } + captionheight = lines * fontsize; + // If text is longer than available space, turn into captionPosTop + if (captionwidth > captionmaxwidth) { + actualCaptionPos = captionPosTop; + } + } else { + fontResDict.initNull(); + font = NULL; + } - const double x1 = coord1->getX(); - const double y1 = coord1->getY(); - const double x2 = coord2->getX(); - const double y2 = coord2->getY(); + // Draw main segment + matr.transform (0, leaderLineLength, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); + appearBBox->extendTo (tx, ty); - // Main segment length - const double main_len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); + if (captionwidth != 0 && actualCaptionPos == captionPosInline) { // Break in the middle + matr.transform ((main_len-captionwidth)/2 - captionhmargin, leaderLineLength, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); - // Main segment becomes positive x direction, coord1 becomes (0,0) - Matrix matr; - const double angle = atan2(y2 - y1, x2 - x1); - matr.m[0] = matr.m[3] = cos(angle); - matr.m[1] = sin(angle); - matr.m[2] = -matr.m[1]; - matr.m[4] = x1-rect->x1; - matr.m[5] = y1-rect->y1; + matr.transform ((main_len+captionwidth)/2 + captionhmargin, leaderLineLength, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); + } - double tx, ty; + matr.transform (main_len, leaderLineLength, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); + appearBBox->extendTo (tx, ty); - // Draw main segment - matr.transform (0, leaderLineLength, &tx, &ty); - appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); + // TODO: Line endings + + // Draw caption text + if (caption) { + double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords + if (actualCaptionPos == captionPosInline) { + tly = leaderLineLength + captionheight / 2; + } else { + tly = leaderLineLength + captionheight + 2*borderWidth; + } + + tlx += captionTextHorizontal; + tly += captionTextVertical; + + // Adjust bounding box + matr.transform (tlx, tly-captionheight, &tx, &ty); + appearBBox->extendTo (tx, ty); + matr.transform (tlx+captionwidth, tly-captionheight, &tx, &ty); + appearBBox->extendTo (tx, ty); + matr.transform (tlx+captionwidth, tly, &tx, &ty); + appearBBox->extendTo (tx, ty); + matr.transform (tlx, tly, &tx, &ty); appearBBox->extendTo (tx, ty); - matr.transform (main_len, leaderLineLength, &tx, &ty); + // Setup text state (reusing transformed top-left coord) + appearBuf->appendf ("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize); // Font color: black + appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} Tm\n", + matr.m[0], matr.m[1], matr.m[2], matr.m[3], tx, ty); + appearBuf->appendf ("0 {0:.2f} Td\n", -fontsize * font->getDescent()); + // Draw text + int i = 0; + double xposPrev = 0; + while (i < contents->getLength()) { + GooString out; + double linewidth, xpos; + layoutText(contents, &out, &i, font, &linewidth, 0, NULL, gFalse); + linewidth *= fontsize; + xpos = (captionwidth - linewidth) / 2; + appearBuf->appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize); + writeString(&out, appearBuf); + appearBuf->append ("Tj\n"); + xposPrev = xpos; + } + appearBuf->append ("ET\n"); + font->decRefCnt(); + } + + // Draw leader lines + double ll_len = fabs(leaderLineLength) + leaderLineExtension; + double sign = leaderLineLength >= 0 ? 1 : -1; + if (ll_len != 0) { + matr.transform (0, 0, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); + appearBBox->extendTo (tx, ty); + matr.transform (0, sign*ll_len, &tx, &ty); appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); appearBBox->extendTo (tx, ty); - // TODO: Line ending, caption - - // Draw leader lines - double ll_len = fabs(leaderLineLength) + leaderLineExtension; - double sign = leaderLineLength >= 0 ? 1 : -1; - if (ll_len != 0) { - matr.transform (0, 0, &tx, &ty); - appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); - appearBBox->extendTo (tx, ty); + matr.transform (main_len, 0, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); + appearBBox->extendTo (tx, ty); + matr.transform (main_len, sign*ll_len, &tx, &ty); + appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); + appearBBox->extendTo (tx, ty); + } - matr.transform (0, sign*ll_len, &tx, &ty); - appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); - appearBBox->extendTo (tx, ty); + appearBuf->append ("Q\n"); - matr.transform (main_len, 0, &tx, &ty); - appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty); - appearBBox->extendTo (tx, ty); + double bbox[4]; + appearBBox->getBBoxRect(bbox); + if (ca == 1) { + createForm(bbox, gFalse, &fontResDict, &appearance); + } else { + Object aStream, resDict; - matr.transform (main_len, sign*ll_len, &tx, &ty); - appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty); - appearBBox->extendTo (tx, ty); - } + createForm(bbox, gTrue, &fontResDict, &aStream); + delete appearBuf; - appearBuf->append ("Q\n"); + appearBuf = new GooString ("/GS0 gs\n/Fm0 Do"); + createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict); + createForm(bbox, gFalse, &resDict, &appearance); + } + delete appearBuf; +} - double bbox[4]; - appearBBox->getBBoxRect(bbox); - if (ca == 1) { - createForm(bbox, gFalse, NULL, &appearance); - } else { - Object aStream, resDict; +void AnnotLine::draw(Gfx *gfx, GBool printing) { + Object obj; - createForm(bbox, gTrue, NULL, &aStream); - delete appearBuf; + if (!isVisible (printing)) + return; - appearBuf = new GooString ("/GS0 gs\n/Fm0 Do"); - createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict); - createForm(bbox, gFalse, &resDict, &appearance); - } - delete appearBuf; + if (appearance.isNull()) { + generateLineAppearance(); } // draw the appearance stream @@ -3221,6 +3312,15 @@ void AnnotLine::draw(Gfx *gfx, GBool printing) { obj.free(); } +// Before retrieving the res dict, regenerate the appearance stream if needed, +// because AnnotLine::draw may need to store font info in the res dict +Object *AnnotLine::getAppearanceResDict(Object *dest) { + if (appearance.isNull()) { + generateLineAppearance(); + } + return Annot::getAppearanceResDict(dest); +} + //------------------------------------------------------------------------ // AnnotTextMarkup //------------------------------------------------------------------------ diff --git a/poppler/Annot.h b/poppler/Annot.h index e3afbc3..98e4d70 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -932,6 +932,7 @@ public: ~AnnotLine(); virtual void draw(Gfx *gfx, GBool printing); + virtual Object *getAppearanceResDict(Object *dest); void setVertices(double x1, double y1, double x2, double y2); void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end); @@ -962,6 +963,7 @@ public: protected: void initialize(PDFDoc *docA, Dict *dict); + void generateLineAppearance(); // required AnnotCoord *coord1, *coord2; commit 855607828447ecec2c8444650d015e21bd17d2e2 Author: Fabio D'Urso <[email protected]> Date: Mon Apr 9 16:45:50 2012 +0200 AnnotFreeText rendering improvements (auto word-wrap, quadding, border style, font/border color) diff --git a/poppler/Annot.cc b/poppler/Annot.cc index 53d71ed..d733501 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -2649,26 +2649,14 @@ static GfxFont * createAnnotDrawFont(XRef * xref, Object *fontResDict) return GfxFont::makeFont(xref, "AnnotDrawFont", dummyRef, fontDict); } -void AnnotFreeText::generateFreeTextAppearance() -{ - double ca = opacity; - - appearBuf = new GooString (); - appearBuf->append ("q\n"); - if (color) { - setColor(color, gTrue); - } - - // Main segment length - const double width = rect->x2 - rect->x1; - const double height = rect->y2 - rect->y1; - - // Parse text size from appearance string (TODO: other properties) - double fontsize = 0; - GooString * da = appearanceString; +void AnnotFreeText::parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor) { + fontsize = -1; + fontcolor = NULL; if (da) { GooList * daToks = new GooList(); int j, i = 0; + + // Tokenize while (i < da->getLength()) { while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) { ++i; @@ -2680,39 +2668,133 @@ void AnnotFreeText::generateFreeTextAppearance() i = j; } } - for (i = 2; i < daToks->getLength(); ++i) { - if (!((GooString *)daToks->get(i))->cmp("Tf")) { - GooString * tok = (GooString *)daToks->get(i - 1); - fontsize = gatof(tok->getCString()); - break; + + // Scan backwards: we are looking for the last set value + for (i = daToks->getLength()-1; i >= 0; --i) { + if (fontsize == -1) { + if (!((GooString *)daToks->get(i))->cmp("Tf") && i >= 2) { + // TODO: Font name + fontsize = gatof(( (GooString *)daToks->get(i-1) )->getCString()); + } + } + if (fontcolor == NULL) { + if (!((GooString *)daToks->get(i))->cmp("g") && i >= 1) { + fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-1) )->getCString())); + } else if (!((GooString *)daToks->get(i))->cmp("rg") && i >= 3) { + fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-3) )->getCString()), + gatof(( (GooString *)daToks->get(i-2) )->getCString()), + gatof(( (GooString *)daToks->get(i-1) )->getCString())); + } else if (!((GooString *)daToks->get(i))->cmp("k") && i >= 4) { + fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-4) )->getCString()), + gatof(( (GooString *)daToks->get(i-3) )->getCString()), + gatof(( (GooString *)daToks->get(i-2) )->getCString()), + gatof(( (GooString *)daToks->get(i-1) )->getCString())); + } } } deleteGooList(daToks, GooString); } - if (fontsize <= 0) { - fontsize = 10; // Default value +} + +void AnnotFreeText::generateFreeTextAppearance() +{ + double borderWidth, ca = opacity; + + appearBuf = new GooString (); + appearBuf->append ("q\n"); + + if (border) { + int i, dashLength; + double *dash; + borderWidth = border->getWidth(); + + switch (border->getStyle()) { + case AnnotBorder::borderDashed: + appearBuf->append("["); + dashLength = border->getDashLength(); + dash = border->getDash(); + for (i = 0; i < dashLength; ++i) + appearBuf->appendf(" {0:.2f}", dash[i]); + appearBuf->append(" ] 0 d\n"); + break; + default: + appearBuf->append("[] 0 d\n"); + break; + } + appearBuf->appendf("{0:.2f} w\n", borderWidth); + } else { + borderWidth = 0; // No border } - // Draw box and setup clipping - appearBuf->appendf ("[] 0 d 1 w 0 G 0 0 {0:.2f} {1:.2f} re b\n", width, height); - appearBuf->appendf ("2 0 {0:.2f} {1:.2f} re W n\n", width-4, height); + // Box size + const double width = rect->x2 - rect->x1; + const double height = rect->y2 - rect->y1; - // Set font state - appearBuf->appendf ("0 g BT 1 0 0 1 2 {0:.2f} Tm\n", height); - appearBuf->appendf ("{0:.2f} TL /AnnotDrawFont {0:.2f} Tf\n", fontsize); + // Parse some properties from the appearance string + double fontsize; + AnnotColor *fontcolor; + parseAppearanceString(appearanceString, fontsize, fontcolor); + // Default values + if (fontsize <= 0) + fontsize = 10; + if (fontcolor == NULL) + fontcolor = new AnnotColor(0, 0, 0); // Black + + // Draw box + GBool doFill = (color && color->getSpace() != AnnotColor::colorTransparent); + GBool doStroke = (borderWidth != 0); + if (doFill || doStroke) { + if (doStroke) { + setColor(fontcolor, gFalse); // Border color: same as font color + } + appearBuf->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth/2, width-borderWidth, height-borderWidth); + if (doFill) { + setColor(color, gTrue); + appearBuf->append(doStroke ? "B\n" : "f\n"); + } else { + appearBuf->append("S\n"); + } + } + + // Setup text clipping + const double textmargin = borderWidth * 2; + const double textwidth = width - 2*textmargin; + appearBuf->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2*textmargin); Object fontResDict; GfxFont *font = createAnnotDrawFont(xref, &fontResDict); + // Set font state + setColor(fontcolor, gTrue); + appearBuf->appendf ("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - fontsize * font->getDescent()); + appearBuf->appendf ("/AnnotDrawFont {0:.2f} Tf\n", fontsize); + int i = 0; + double xposPrev = 0; while (i < contents->getLength()) { GooString out; - layoutText(contents, &out, &i, font, NULL, 0, NULL, gFalse); + double linewidth, xpos; + layoutText(contents, &out, &i, font, &linewidth, textwidth/fontsize, NULL, gFalse); + linewidth *= fontsize; + switch (quadding) { + case quaddingCentered: + xpos = (textwidth - linewidth) / 2; + break; + case quaddingRightJustified: + xpos = textwidth - linewidth; + break; + default: // quaddingLeftJustified: + xpos = 0; + break; + } + appearBuf->appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize); writeString(&out, appearBuf); - appearBuf->append("'\n"); + appearBuf->append("Tj\n"); + xposPrev = xpos; } font->decRefCnt(); + delete fontcolor; appearBuf->append ("ET Q\n"); double bbox[4]; diff --git a/poppler/Annot.h b/poppler/Annot.h index 2e64ac9..e3afbc3 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -891,6 +891,7 @@ public: protected: void initialize(PDFDoc *docA, Dict *dict); + static void parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor); void generateFreeTextAppearance(); // required _______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
