@@ -5605,6 +5605,21 @@ bool TextPage::markLineNumber() {
56055605 return true ;
56065606}
56075607
5608+ /* *
5609+ * Clamp an illustration bounding box so that its position stays non-negative.
5610+ * Graphics that extend past the top or left page edge (bleed, clipped images,
5611+ * vector paths starting off-page) otherwise produce negative HPOS/VPOS values,
5612+ * which are invalid in ALTO. When a coordinate is negative we move it back to 0
5613+ * and shrink the corresponding dimension by the clipped-off amount, keeping the
5614+ * on-page portion of the box. See issue #236.
5615+ */
5616+ static void clampIllustrationBox (double &x, double &y, double &w, double &h) {
5617+ if (x < 0 ) { w += x; x = 0 ; }
5618+ if (y < 0 ) { h += y; y = 0 ; }
5619+ if (w < 0 ) { w = 0 ; }
5620+ if (h < 0 ) { h = 0 ; }
5621+ }
5622+
56085623void TextPage::dump (GBool noLineNumbers, GBool fullFontName, const vector<bool > &lineNumberStatus) {
56095624 // Output the page in raw (content stream) order
56105625 blocks = new GList (); // these are blocks in alto schema
@@ -6547,7 +6562,12 @@ void TextPage::dump(GBool noLineNumbers, GBool fullFontName, const vector<bool>
65476562 if (wordI < line1->words ->getLength () - 1 and (word->spaceAfter == gTrue )) {
65486563 xmlNodePtr spacingNode = xmlNewNode (NULL , (const xmlChar *) TAG_SPACING );
65496564 spacingNode->type = XML_ELEMENT_NODE ;
6550- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , (nextWord->xMin - word->xMax ));
6565+ // The inter-word gap can be negative when consecutive words overlap
6566+ // (backward kerning or reordered runs); ALTO requires a non-negative
6567+ // WIDTH, so clamp it to 0. See issue #236.
6568+ double spacingWidth = nextWord->xMin - word->xMax ;
6569+ if (spacingWidth < 0 ) { spacingWidth = 0 ; }
6570+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , spacingWidth);
65516571 xmlNewProp (spacingNode, (const xmlChar *) ATTR_WIDTH ,
65526572 (const xmlChar *) tmp);
65536573 snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , (word->yMin ));
@@ -6591,13 +6611,19 @@ void TextPage::dump(GBool noLineNumbers, GBool fullFontName, const vector<bool>
65916611
65926612 // xmlNewProp(node, (const xmlChar *) ATTR_SID,(const xmlChar*)listeImages[i]->getImageSid()->getCString());
65936613
6594- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , listeImages[i]->getXPositionImage ());
6614+ double imgX = listeImages[i]->getXPositionImage ();
6615+ double imgY = listeImages[i]->getYPositionImage ();
6616+ double imgW = listeImages[i]->getWidthImage ();
6617+ double imgH = listeImages[i]->getHeightImage ();
6618+ clampIllustrationBox (imgX, imgY, imgW, imgH);
6619+
6620+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , imgX);
65956621 xmlNewProp (node, (const xmlChar *) ATTR_X , (const xmlChar *) tmp);
6596- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , listeImages[i]-> getYPositionImage () );
6622+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , imgY );
65976623 xmlNewProp (node, (const xmlChar *) ATTR_Y , (const xmlChar *) tmp);
6598- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , listeImages[i]-> getWidthImage () );
6624+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , imgW );
65996625 xmlNewProp (node, (const xmlChar *) ATTR_WIDTH , (const xmlChar *) tmp);
6600- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , listeImages[i]-> getHeightImage () );
6626+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , imgH );
66016627 xmlNewProp (node, (const xmlChar *) ATTR_HEIGHT , (const xmlChar *) tmp);
66026628
66036629 std::string rotation = std::to_string (listeImages[i]->getRotation ());
@@ -6643,13 +6669,19 @@ void TextPage::dump(GBool noLineNumbers, GBool fullFontName, const vector<bool>
66436669 // xmlNewProp(node, (const xmlChar *) ATTR_SID,(const xmlChar*)listeImages[i]->getImageSid()->getCString());
66446670
66456671 double r =0 ;
6646- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svg_xmin);
6672+ double svgX = svg_xmin;
6673+ double svgY = svg_ymin;
6674+ double svgW = svg_xmax - svg_xmin;
6675+ double svgH = svg_ymax - svg_ymin;
6676+ clampIllustrationBox (svgX, svgY, svgW, svgH);
6677+
6678+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svgX);
66476679 xmlNewProp (node, (const xmlChar *) ATTR_X , (const xmlChar *) tmp);
6648- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svg_ymin );
6680+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svgY );
66496681 xmlNewProp (node, (const xmlChar *) ATTR_Y , (const xmlChar *) tmp);
6650- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svg_xmax - svg_xmin );
6682+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svgW );
66516683 xmlNewProp (node, (const xmlChar *) ATTR_WIDTH , (const xmlChar *) tmp);
6652- snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svg_ymax - svg_ymin );
6684+ snprintf (tmp, sizeof (tmp), ATTR_NUMFORMAT , svgH );
66536685 xmlNewProp (node, (const xmlChar *) ATTR_HEIGHT , (const xmlChar *) tmp);
66546686
66556687 std::string rotation = std::to_string (r);
0 commit comments