Skip to content

Commit 218af15

Browse files
authored
Clamp illustration and spacing coordinates to non-negative values (#236) (#242)
ALTO requires HPOS/VPOS/WIDTH/HEIGHT to be non-negative, but graphics that extend past the top or left page edge (bleed, clipped images, off-page vector paths) produced negative HPOS/VPOS, and overlapping words produced negative SP widths. - Add clampIllustrationBox() to move a negative illustration position back to 0 and shrink the corresponding dimension by the clipped-off amount, keeping the on-page portion of the box. Applied to both the raster-image and SVG-graphics dumps. - Clamp the inter-word SP width to 0 when consecutive words overlap. Fixes #236
1 parent 725d0c8 commit 218af15

1 file changed

Lines changed: 41 additions & 9 deletions

File tree

src/XmlAltoOutputDev.cc

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
56085623
void 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

Comments
 (0)