[poppler] 2 commits - poppler/Annot.cc poppler/Annot.h

Albert Astals Cid aacid at kemper.freedesktop.org
Wed Apr 11 16:37:00 PDT 2012


 poppler/Annot.cc |  408 +++++++++++++++++++++++++++++++++++++++----------------
 poppler/Annot.h  |    3 
 2 files changed, 298 insertions(+), 113 deletions(-)

New commits:
commit 46b3a70cae3b37cb4270a83afaddd6734442b752
Author: Fabio D'Urso <fabiodurso at hotmail.it>
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 <fabiodurso at hotmail.it>
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


More information about the poppler mailing list