github xberg-io/xberg v1.2.2

3 hours ago

Fixed

  • (pdf): a two-column page is no longer split down the middle of a column because one line
    crosses its gutter.
    The corridor search treated whitespace as "no span's bbox", so a single
    centred footer, caption, or heading set across both columns closed the page's real gutter for
    every line on the page. The per-line median then placed the split inside the left column, every
    line it crossed became a band boundary, and the reordered text interleaved the two columns
    mid-word — producing spliced tokens such as activamodel and demandviating. A split that six or
    more lines run through now also considers corridors at most one line crosses, still bounded by the
    same distance cap and still refused when either side does not read as a column. Measured over 490
    corpus PDFs: 7 documents change, all of them repairing cross-column splices, and none loses
    vocabulary. Reported and fixed by Data Polder. (GH#1619)
  • (pdf): a wrapped numbered heading now closes above the run-in beneath it. The GH#1634 fix's
    closing term also required a right-edge test that only applies to the un-indented case. A hanging
    indent heading's wrap and a run-in sub-heading below it are both short lines by definition, so
    their right edges land within tolerance of each other by coincidence, which overrode the correct
    left-edge answer and held the heading open across the run-in and the body under it. (GH#1637)
  • (ppt): a legacy .ppt slide's title is read from the file rather than guessed. The title came
    from the first line of the slide's merged text, gated on a second line existing and the first being
    at most 80 characters — so a title-only slide had no title, a two-line title was truncated to its
    first line, and a longer title was dropped. The outline collection's own TextHeaderAtom states
    which text is the title; that is now read, with the first-line rule kept only for decks that state
    no title. (GH#1635)
  • (ppt): the live save's slides are read, not the oldest save's. The persist chain resolved the
    current revision correctly and then scanned the stream for the first SlideListWithText, which on
    a deck saved more than once belongs to the earliest save — so edited titles and outline text
    reverted to a superseded revision. (GH#1639)
  • (ppt): speaker notes attach to the slide that owns them, and the notes master is no longer one.
    The notes master's placeholder text ("Click to edit Master text styles…") was emitted as the first
    slide's speaker note, and the remaining notes were zipped to slides by position, so every note
    after a slide without one landed on the wrong slide. Notes are now keyed by the NotesAtom's own
    slideIdRef. (GH#1640)
  • (release): the xberg-cli archives are attached again. v1.2.0 published 12 assets and
    v1.2.1 published 54, both with none of the nine xberg-cli-* archives, so mise, cargo binstall
    and every direct download had nothing to fetch. One matrix leg (the aarch64-musl link) failed, and
    the upload job required all three CLI build jobs to succeed, so a single leg zeroed the whole asset
    class — as a skipped job, which reports no failure and left both releases looking green. The
    upload now attaches whatever built, and a following step fails the release when any of the nine is
    missing. (GH#1638)
  • (pdf render): s (close-and-stroke) is no longer dropped, and no longer floods the next fill.
    The object-level content parser had arms for every path-painting operator in ISO 32000-1 Table 60
    except s, which fell through to an operator every consumer ignores. Two things followed: the
    stroke was never painted, and — because only a painting arm resets the path builder — the
    abandoned geometry stayed in the builder and was painted by the next fill. A stroked frame
    followed by an ordinary white label fill therefore flooded the frame's whole interior, wiping
    anything already drawn inside it. Text extraction was unaffected: the byte-level fast path always
    decomposed s correctly. (GH#1633)
  • (pdf render): a glyph is no longer discarded because its inferred Unicode is whitespace.
    The byte-indexed rasterizer decided whether to paint by asking what character a code represents
    rather than whether there was an outline to draw. For a simple TrueType font with a byte-indexed
    cmap and no /Encoding that character is the glyph id reverse-mapped through the font's own
    (1, 0) subtable and read as ASCII or Mac Roman — private glyph ordering decoded as an encoding
    it never expressed. Six byte values were affected (0x090x0D, 0x20, and 0xCA → U+00A0);
    re-indexed subsets numbering their glyphs from 0x01 upward walked through a whole run unpainted
    while the advance still applied, leaving gaps that read as spaces. (GH#1632)
  • (pdf): a hanging-number heading no longer swallows body text indented to its title's edge.
    A line was treated as a numbered heading's continuation whenever the heading had a hanging indent
    and the line began at the title's left edge — which is equally the geometry of any layout that
    indents the whole clause, as contracts, tenders and many installation manuals are set. Wrapping
    now also requires that the preceding line actually ran out of room. Separately, a heading that had
    absorbed a genuine wrap could never be closed, so the sub-heading and entire body below were
    pulled in after it. (GH#1634)
  • (cli): the Linux musl CLI binaries are published again.
    Since 1.2.0 the aarch64-unknown-linux-musl CLI build has been killed mid-link, and because
    the upload job requires every musl leg to succeed, no CLI assets were attached to the 1.2.0
    or 1.2.1 releases. The cause was the switch to fat LTO: over the all feature set the final
    whole-program link exceeded what the arm64 runner could complete. That build now uses a
    release-musl profile with thin LTO; every other target keeps fat LTO.
  • (php): setting outputFormat no longer throws. Every call that passed an output format —
    ExtractionConfig::from_json(json_encode(["outputFormat" => "markdown"])), and any
    extract/extractBatch carrying a per-input config — raised
    invalid type: string "markdown", expected struct OutputFormat. The generated Xberg\OutputFormat
    class carried a derived serde implementation that read and wrote its own storage shape,
    {"type":"markdown"}, while the core enum is externally tagged and writes a bare string, so no
    real value ever deserialized. EntityCategory and PiiCategory carried the same defect.
    Introduced in 1.2.0 alongside the flat-class change and shipped in three releases.
  • (wasm): metadata.format fields are readable again. metadata.format.title returned '' and
    metadata.format.sheetCount returned NaN for every document, because the value reached
    JavaScript as a Map rather than the plain object index.d.ts declares — serde-wasm-bindgen
    renders every serde map that way, and serde's internally tagged enum representation is a map.
    Property access on a Map is undefined. Introduced in 1.2.0 with the FormatMetadata flatten.

Changed

  • extraction::ppt::PptSlideText carries the slide's stated title and notes. The struct gains
    title: Option<String> and notes: Option<String>, so Rust callers constructing it with an
    exhaustive struct literal must add the two fields. The type is not exposed through any binding.
  • Breaking (PHP binding): OutputFormat, EntityCategory and PiiCategory now serialize to the
    wire their Rust counterparts emit — "markdown" for a fieldless variant and {"custom":"latex"}
    for one carrying a label — instead of the flat {"type":"markdown"} object. from_json still
    accepts the object form, so existing input keeps working; output moves.
  • Breaking (WebAssembly binding): metadata.format — and any other field carrying a flattened,
    camelCased enum — is now a plain object rather than a Map. This is the shape index.d.ts has
    always declared; code that worked around the discrepancy with .get() must switch to property
    access. Fields carrying an enum that is not flattened, such as chunking.sizing, keep the
    serde-wasm-bindgen bridge and are unchanged.
  • Breaking (C#, Dart, Go, Java, Kotlin, Python, Swift bindings): an enum variant whose name ends
    in a multi-letter acronym followed by a single lowercase letter is spelled correctly now.
    StructuredDataType.RdFa becomes Rdfa (C#, Dart, Go, Swift), Kotlin's R_D_FA becomes RDFA
    and PADDLE_O_C_R becomes PADDLE_OCR, and Python's stub and runtime agree on RDFA. Wire
    values are unchanged — only the identifiers move. Names without that shape (IOError,
    HTMLParser, JSONLD) are unaffected.

Don't miss a new xberg release

NewReleases is sending notifications on new releases.