Files
pdf/engine/src/document/document_builder.cpp
T
2026-08-10 14:21:54 +05:30

179 lines
6.0 KiB
C++

#include "pdfengine/document/document_builder.hpp"
#include <algorithm>
#include <cmath>
namespace pdfengine::document {
PageModel DocumentBuilder::buildFromOCR(const RawOCRPage& rawPage) const {
PageModel model;
model.pageIndex = rawPage.pageIndex;
model.width = rawPage.pageWidth;
model.height = rawPage.pageHeight;
if (rawPage.lines.empty()) {
return model;
}
// Standard baseline clustering to group lines into Paragraphs
std::vector<Paragraph> paragraphs;
Paragraph currentPara;
double lastLineY = -1.0;
double lastLineH = 0.0;
for (size_t lIdx = 0; lIdx < rawPage.lines.size(); ++lIdx) {
const auto& rawLine = rawPage.lines[lIdx];
TextLine line;
line.x = rawLine.x;
line.y = rawLine.y;
line.w = rawLine.width;
line.h = rawLine.height;
line.baselineY = rawLine.baselineY;
line.angle = 0.0;
TextRun run;
run.text = rawLine.text;
run.fontName = rawLine.fontName.empty() ? "Helvetica" : rawLine.fontName;
bool isBold = rawLine.isBold || rawLine.fontWeight >= 600;
bool isItalic = rawLine.isItalic || rawLine.fontStyle == "italic";
run.flags = (isBold ? 2 : 0) | (isItalic ? 1 : 0);
run.fontWeight = rawLine.fontWeight > 0 ? rawLine.fontWeight : (isBold ? 700 : 400);
run.fontStyle = !rawLine.fontStyle.empty() ? rawLine.fontStyle : (isItalic ? "italic" : "normal");
if (!rawLine.fontFace.empty()) {
run.fontFace = rawLine.fontFace;
} else if (isBold && isItalic) {
run.fontFace = run.fontName + "-BoldItalic";
} else if (isBold) {
run.fontFace = run.fontName + "-Bold";
} else if (isItalic) {
run.fontFace = run.fontName + "-Italic";
} else {
run.fontFace = run.fontName;
}
run.internalFontId = !rawLine.fontId.empty()
? rawLine.fontId
: (run.fontName + "_TrueType_" + std::to_string(run.fontWeight));
run.fontSize = rawLine.fontSize > 0.0 ? rawLine.fontSize : (rawLine.height * 0.72);
run.x = rawLine.x;
run.y = rawLine.y;
run.w = rawLine.width;
run.h = rawLine.height;
run.fillColor = "#000000";
run.fontFidelity = "exact";
// Generate Glyphs
if (!rawLine.words.empty()) {
for (const auto& w : rawLine.words) {
double charW = w.width / std::max<size_t>(1, w.text.size());
double currX = w.x;
for (char ch : w.text) {
Glyph g;
g.text = std::string(1, ch);
g.unicode = static_cast<uint32_t>(static_cast<unsigned char>(ch));
g.fontName = run.fontName;
g.fontSize = run.fontSize;
g.flags = run.flags;
g.originX = currX;
g.originY = rawLine.baselineY;
g.bboxX = currX;
g.bboxY = w.y;
g.bboxW = charW;
g.bboxH = w.height;
run.glyphs.push_back(g);
line.glyphs.push_back(g);
currX += charW;
}
}
} else {
double charW = rawLine.width / std::max<size_t>(1, rawLine.text.size());
double currX = rawLine.x;
for (char ch : rawLine.text) {
Glyph g;
g.text = std::string(1, ch);
g.unicode = static_cast<uint32_t>(static_cast<unsigned char>(ch));
g.fontName = run.fontName;
g.fontSize = run.fontSize;
g.flags = run.flags;
g.originX = currX;
g.originY = rawLine.baselineY;
g.bboxX = currX;
g.bboxY = rawLine.y;
g.bboxW = charW;
g.bboxH = rawLine.height;
run.glyphs.push_back(g);
line.glyphs.push_back(g);
currX += charW;
}
}
line.runs.push_back(run);
// Check paragraph break (vertical gap > 1.5x line height)
bool isNewPara = false;
if (lastLineY >= 0.0) {
double gap = rawLine.y - (lastLineY + lastLineH);
if (gap > lastLineH * 1.5 || gap < -lastLineH * 0.5) {
isNewPara = true;
}
}
if (isNewPara && !currentPara.lines.empty()) {
// Finalize current paragraph bounds
double pX1 = currentPara.lines.front().x;
double pY1 = currentPara.lines.front().y;
double pX2 = pX1 + currentPara.lines.front().w;
double pY2 = pY1 + currentPara.lines.front().h;
for (const auto& l : currentPara.lines) {
pX1 = std::min(pX1, l.x);
pY1 = std::min(pY1, l.y);
pX2 = std::max(pX2, l.x + l.w);
pY2 = std::max(pY2, l.y + l.h);
}
currentPara.x = pX1;
currentPara.y = pY1;
currentPara.w = pX2 - pX1;
currentPara.h = pY2 - pY1;
paragraphs.push_back(currentPara);
currentPara = Paragraph();
}
currentPara.lines.push_back(line);
lastLineY = rawLine.y;
lastLineH = rawLine.height;
}
if (!currentPara.lines.empty()) {
double pX1 = currentPara.lines.front().x;
double pY1 = currentPara.lines.front().y;
double pX2 = pX1 + currentPara.lines.front().w;
double pY2 = pY1 + currentPara.lines.front().h;
for (const auto& l : currentPara.lines) {
pX1 = std::min(pX1, l.x);
pY1 = std::min(pY1, l.y);
pX2 = std::max(pX2, l.x + l.w);
pY2 = std::max(pY2, l.y + l.h);
}
currentPara.x = pX1;
currentPara.y = pY1;
currentPara.w = pX2 - pX1;
currentPara.h = pY2 - pY1;
paragraphs.push_back(currentPara);
}
model.paragraphs = std::move(paragraphs);
return model;
}
} // namespace pdfengine::document