feat: implemented multi page reflow
This commit is contained in:
@@ -54,10 +54,6 @@ void get_or_throw(std::expected<void, pdfengine::EngineError>&& res) {
|
||||
#include <serializer/content_serializer.hpp>
|
||||
#include <serializer/ast_serializer.hpp>
|
||||
|
||||
// A TJ array number more negative than this is treated as an inter-word space when flattening a
|
||||
// TJ run to display/edit text (numbers are thousandths-of-em, subtracted from the X position; a
|
||||
// word space is a large positive gap = large-negative adjustment). extract and replace MUST share
|
||||
// this constant so the flattened text they produce stays positionally aligned (see P1a redistribution).
|
||||
static constexpr double kTjSpaceKern = -500.0;
|
||||
|
||||
class StreamEditor {
|
||||
@@ -140,14 +136,6 @@ public:
|
||||
}
|
||||
if (!combinedText.empty()) {
|
||||
if (textCount == object_index) {
|
||||
// P1a — preserve TJ positioning. When the edit keeps the SAME length as the
|
||||
// extracted text (the typo-fix common case), slice new_text back into the
|
||||
// original string slots and KEEP every kern number, so glyph spacing/kerning
|
||||
// survives. We mirror extract's reconstruction (a synthetic space per
|
||||
// large-negative kern) to keep positions aligned; if a synthetic-space
|
||||
// position was itself edited (can't map cleanly) or the length changed, we
|
||||
// fall back to a single string at natural advances — exactly the old behaviour,
|
||||
// so this is never worse than before, only better when it aligns.
|
||||
bool redistributed = false;
|
||||
if (new_text.size() == combinedText.size()) {
|
||||
std::vector<std::pair<pdfengine::AstNode*, std::string>> assign;
|
||||
@@ -162,7 +150,7 @@ public:
|
||||
} else if (item->type == pdfengine::AstNodeType::Number &&
|
||||
item->numberValue < kTjSpaceKern) {
|
||||
if (pos >= new_text.size() || new_text[pos] != ' ') { ok = false; break; }
|
||||
pos += 1; // consume the synthetic space; keep the kern number as-is
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
if (ok && pos == new_text.size()) {
|
||||
@@ -316,7 +304,8 @@ PYBIND11_MODULE(pdfengine, m) {
|
||||
.def_readonly("w", &pdfengine::TextRun::w)
|
||||
.def_readonly("h", &pdfengine::TextRun::h)
|
||||
.def_readonly("object_indices", &pdfengine::TextRun::objectIndices)
|
||||
.def_readonly("fill_color", &pdfengine::TextRun::fillColor);
|
||||
.def_readonly("fill_color", &pdfengine::TextRun::fillColor)
|
||||
.def_readonly("para_id", &pdfengine::TextRun::paraId);
|
||||
|
||||
py::class_<pdfengine::TextLine>(m, "TextLine")
|
||||
.def_readonly("runs", &pdfengine::TextLine::runs)
|
||||
@@ -365,8 +354,6 @@ PYBIND11_MODULE(pdfengine, m) {
|
||||
return get_or_throw(self.render(dpi));
|
||||
}, py::arg("dpi") = 96)
|
||||
.def("render_region_raw", [](const pdfengine::PdfPage& self, int dpi, double y_top_pt, double height_pt) {
|
||||
// Returns (width, height, raw RGBA bytes) of the band [y_top_pt, y_top_pt+height_pt]
|
||||
// (points from page top); height_pt<=0 → to page bottom. No PNG. For preview/perf tests.
|
||||
auto img = get_or_throw(self.renderRegionRaw(dpi, y_top_pt, height_pt));
|
||||
return py::make_tuple(img.width, img.height,
|
||||
py::bytes(reinterpret_cast<const char*>(img.data.data()), img.data.size()));
|
||||
@@ -474,8 +461,6 @@ PYBIND11_MODULE(pdfengine, m) {
|
||||
return get_or_throw(self.getFonts(start_page, end_page));
|
||||
}, py::arg("start_page") = 0, py::arg("end_page") = -1)
|
||||
.def("get_font_data", [](const pdfengine::PdfDocument& self, const std::string& internal_font_id) {
|
||||
// Raw embedded font bytes for the given run font; empty bytes when the font
|
||||
// is not embedded / not found (the gateway maps that to a 404 + CSS fallback).
|
||||
auto res = self.getFontData(internal_font_id);
|
||||
if (!res || res->empty()) {
|
||||
return py::bytes();
|
||||
|
||||
Reference in New Issue
Block a user