From c1e441fb009f234d403a9c6cdb298d9099324c8c Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sat, 1 Aug 2026 17:55:19 +0200 Subject: [PATCH 1/5] fix(pdf): centre pages against each other and keep a gutter on phones The page column was `.p{margin:16px auto}` directly in the body, so the scrollable width was exactly the widest page. A phone fitting the document to the screen therefore put the page edge to edge with nothing left to zoom out to, and a page narrower than another centred on the viewport rather than on the document, so a landscape page and a portrait page in one file did not line up. The pages now sit in a `.d` column sized to the widest page (`width:max-content`, `min-width:100%` so it still fills a wider viewport) and centred with flex. The page's side margin is part of that width, so it survives the fit-to-width zoom as a gutter. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MXbGab8fAobdjrzZZkZiUW --- src/odr/internal/html/pdf_file.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/odr/internal/html/pdf_file.cpp b/src/odr/internal/html/pdf_file.cpp index 999335d1..a2a5f062 100644 --- a/src/odr/internal/html/pdf_file.cpp +++ b/src/odr/internal/html/pdf_file.cpp @@ -1767,6 +1767,7 @@ class HtmlServiceImpl final : public HtmlService { }; out.write_body_begin(); + out.write_element_begin("div", HtmlElementOptions().set_class("d")); std::size_t page_number = first_page_number; for (const DualPageOut &page : pages_out) { out.write_element_begin( @@ -1795,6 +1796,7 @@ class HtmlServiceImpl final : public HtmlService { out.write_element_end("div"); // .p } + out.write_element_end("div"); // .d out.write_body_end(); out.write_end(); @@ -2290,6 +2292,7 @@ class HtmlServiceImpl final : public HtmlService { }; out.write_body_begin(); + out.write_element_begin("div", HtmlElementOptions().set_class("d")); std::size_t page_number = first_page_number; for (const SinglePageOut &page : pages_out) { out.write_element_begin( @@ -2300,8 +2303,9 @@ class HtmlServiceImpl final : public HtmlService { write_page_items(out, page.clip_defs, page.items, page.width, page.height, write_line); write_page_links(out, page.links); - out.write_element_end("div"); + out.write_element_end("div"); // .p } + out.write_element_end("div"); // .d out.write_body_end(); out.write_end(); @@ -2528,7 +2532,13 @@ class HtmlServiceImpl final : public HtmlService { write_viewport_meta(out, config(), true); out.write_header_style_begin(); out.out() << "body{margin:0;background:#525659}"; - out.out() << ".p{position:relative;margin:16px auto;background:#fff;" + // `.d`: the page column, sized to the widest page so pages of differing + // width centre against each other, not against the viewport. The page's + // side margin is part of that width, so fitting the document to a phone + // screen leaves a gutter instead of going edge to edge. + out.out() << ".d{display:flex;flex-direction:column;align-items:center;" + "gap:16px;padding:16px 0;width:max-content;min-width:100%}"; + out.out() << ".p{position:relative;margin:0 16px;background:#fff;" "box-shadow:0 1px 4px rgba(0,0,0,.5)}"; // `.t`: shared base for all absolutely-positioned line blocks. out.out() << ".t{position:absolute;left:0;top:0;transform-origin:0 0;" From ae78fe98352aa8aecfbaf834ea052889fb7bf5e1 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sat, 1 Aug 2026 18:20:49 +0200 Subject: [PATCH 2/5] feat: write the renderer's css and js into the html, dropping odr.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The css and js of the document, spreadsheet and text views came from a pinned OpenDocument.js release zip, staged into a `data/` directory that every consumer then had to ship and point the library at: an android asset extracted into no-backup storage on first use, a resource inside the apple framework with a `+load` hook to find it, a `pyodr/data` directory in the wheel, an `ODR_CORE_DATA_PATH` environment variable for the test suites. Rendering broke whenever any of that was missed, and a stylesheet fix meant a release of another repository first. They are now string constants in `internal/html/frontend.cpp`, written into the document as `