Skip to content

Releases: sjvrensburg/railmark

v0.16.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 07:35
d9aaec6

Replies are real threads now

Built on RailReader.Core 0.71.0, which can write /IRT. Replies added with --apply-markup carry a genuine /IRT link to their parent, so they nest in Acrobat and other readers that support threads, not only in RailReader2 and RailMark. The /NM-based link is kept as a fallback for files Core cannot link (signed or read-only files, RC4/AES-128 encryption) and for PDFs written by earlier RailMark versions.

Running --apply-markup on a PDF with older RailMark replies now writes the missing /IRT for them as well.

Removing a comment removes its replies

--remove-ids, --replace-author and --replace-unauthored now take a removed annotation's whole reply thread with it: replies to replies, replies on other pages, and the page-break tail of a reply. Previously the replies were left behind as orphan notes. The removed count includes them.

Behaviour change: the cascade is by thread, not by author, so --replace-author "AI Reviewer" also removes another author's reply to one of the AI's annotations.

Other changes

  • Markdown output takes its reply threading from Core's AnnotationThreads. A malformed loop of replies is now cut at its earliest member (shown as a plain note, with the rest nested beneath it) instead of every member being shown as a plain note.
  • RailReader.Export, RailReader.Renderer.Skia and RailReader.Core.Analysis updated from 0.67.0 to 0.71.0.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 16 Oct 12:55
3b33814

New

  • Near-duplicate warning. When an entry is added and a markup of the same type already on the page covers much the same text (any author, however the lines are split), the entry now carries a Near-duplicate of <id> warning. It suggests an update entry; a short markup nested inside a longer one is not flagged. It is a warning only, so exit codes are unchanged. This catches duplicates from re-applying --format json records, which the exact-geometry dedupe missed.

Notes

  • Only the first part of a page-break markup is checked for near-duplicates.
  • The build is now free of compiler warnings.

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 16 Oct 12:12
6e804dd

0.14.0

Behaviour change: highlights are now written at 35% opacity, matching RailReader2's own highlight tool, so saturated colours no longer hide the text under them. Highlights already in a PDF are not touched. To get the old look, pass "opacity": 1 on an entry or --opacity 1 for a whole run.

New

  • Update an annotation in place: {"page": 17, "update": "<id>", "comment": …, "color": …, "author": …, "opacity": …} changes an existing annotation of any type, notes and ink included, keeping its id, creation date and position (#35).
  • Opacity: a per-entry opacity (0–1) and a --opacity flag. --format json now reports opacity (#36).
  • Remove annotations without an author: --replace-unauthored (or --replace-author "") (#34).
  • Remove annotations by id: --remove-ids a,b,c (#34).

Notes

  • type is now optional in a plan entry, but only for an update entry.
  • Changing an annotation's author with update changes how re-applying a plan finds it. Removing a parent annotation leaves its replies as top-level notes.
  • Requested in #33.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:20

What's new

This release is about where sticky notes land. Everything below is covered by the integration smoke against real pdfium geometry, which now reads the written /Rect values back from the PDF.

Sticky-note placement (--apply-markup)

  • Notes sit beside their text. A note icon used to be pinned a fixed 24pt from the page's right edge, so in a wide-margin or two-column document it sat far from the passage it was about. It now goes just outside the text column its quoted line belongs to, found from the page's character boxes, on whichever margin has room nearer: the right margin on a single-column page, the gutter or left margin for the left column of a two-column page. It is kept clear of any glyphs it would cover and inside the page.
  • Notes on one line stack instead of piling up. A note that would overlap an icon already there moves down (then up, at the foot of a page) to the nearest free spot, so several notes on one passage are all visible and read top to bottom.
  • Reply threads stay together. A reply takes the spot directly beneath its parent note and the replies already there, pushing any other note in the way down the column, so a thread reads parent, reply, reply without unrelated notes in between.
  • Re-applying a plan still finds its notes. A note is identified by its text and where it would ideally sit, with an allowance for how far stacking or a thread may have moved it, so re-running a plan reports unchanged rather than adding duplicates. Notes written by earlier releases at the page edge are recognised too. The allowance is six icons: a note with more than six replies inserted above it on the same line is re-added rather than matched.

Skill

The railmark-markup-plan skill now picks the annotation by the scope of the feedback. A remark about specific words or a sentence goes on the text as a highlight, underline, squiggly, strikeout or replace with the remark in its comment; a note is for a paragraph or section as a whole, placed beside the passage rather than on it, quoting the paragraph's opening words so the icon lands beside its first line. The schema reference and example plan are updated to match.

Upgrading

railmark --version   # should report 0.13.0

See install.sh in this repo, or download the platform artifact below directly.

Full Changelog: v0.12.0...v0.13.0

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 12:48
0765f5e

What's new

This release closes the loop between an AI agent's markup plan and the PDF: a plan can be drafted, applied, re-applied and read back as a plan again. Everything below is covered by the integration smoke against real pdfium geometry.

Applying markup (--apply-markup)

  • Pick which occurrence a quote marks. New occurrence (1-based), contextBefore and contextAfter fields on a plan entry narrow a quote that repeats on its page. An unqualified quote that matches more than once still resolves to the first match, but the result now carries matchCount and a warning so the agent can retry with context. --dry-run adds a context field per entry showing what surrounds each match.
  • replace markup type. A suggested replacement is one strikeout carrying Replace with: <text> (plus the comment), since PDFium cannot write Acrobat's caret + strikeout pair. Annotation mode reads it back as a single suggested replacement entry.
  • Re-applying a plan is safe. Resolved markups are merged into what the page already holds: the same markup (type, author, geometry) is updated in place when its comment or colour changed, and left alone otherwise. Two entries of one run that would write different things to the same spot produce a conflict on the later entry rather than a silent overwrite. --replace-author <name> removes that author's earlier annotations first, for a fresh pass.
  • Quotes across a page break. A quote that starts at the bottom of one page and ends at the top of the next resolves as a head annotation plus a continuation on the following page, reported as one markup with endPage. The split must fall at a real line break, so a quote on the wrong page fails naming the page it was found on instead of half-matching.
  • Replies. A note with inReplyTo set to an existing annotation's id threads beneath it. Successive replies to the same parent stack below it.
  • Plans from stdin. --apply-markup - reads the plan from standard input.

Extracting annotations

  • --format json. One record per annotation in reading order, filed under the same headings as the Markdown. Field names match the plan schema, so extracted records can be fed straight back in as a plan; occurrence is filled only where the quote repeats on its page, and continuations fold into their head with endPage.
  • Author and date filters. --author and --exclude-author (case-insensitive, repeatable) and --since <date> filter both output formats. --since uses the modified date, falling back to created, and reports how many undated annotations it dropped.
  • Reply notes are threaded under their parent as nested blockquotes in Markdown. Heading assignment now uses each heading's position on the page, so two headings on one page split their annotations correctly.

Dependencies

  • RailReader.Core 0.62.2 → 0.67.0. All upstream changes in that range are additive for RailMark. The useful one is 0.64.2: the signed-PDF probe behind Save reads a few KB instead of the whole file, so applying markup to a large scanned PDF no longer pays a full read up front. RailReader.Core.Analysis now pulls in NetTopologySuite (pure managed); it is bundled in all three artifacts.

The railmark-markup-plan skill's schema reference and example plan are updated for the new fields.

Upgrading

railmark --version   # should report 0.12.0

See install.sh in this repo, or download the platform artifact below directly.

Full Changelog: v0.11.0...v0.12.0

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 12:33
fabe8f1

What's new

  • --apply-markup default colours now match RailReader2's annotation palette. The old defaults were chosen before RailReader2 had a fixed palette, and three of them were colours it can't reproduce. Now, when a plan entry has no color, the entry gets RailReader2's default colour for that tool:

    Type 0.10.x 0.11.0
    highlight #FFFF00 #FFFF00 (unchanged)
    underline #00AAFF #00A000 green
    strikeout #FF0000 #FF0000 (unchanged)
    squiggly #FF8800 #00A000 green
    note #FFCC00 #FFFF00 yellow

    AI-authored markup now looks the same as markup a reader adds in RailReader2, and one --color filter selects both. Explicit color values in a plan are unaffected. The railmark-markup-plan skill's schema reference lists the new defaults and the full five-colour palette (yellow, green, red, blue #0066FF, black #000000).

    Watch out: if you filter annotation-mode output with --color 00AAFF, FF8800 or FFCC00 to find RailMark-written markup, PDFs marked up by 0.11.0 need 00A000 or FFFF00 instead.

  • RailReader.Core bumped 0.62.1 → 0.62.2. Upstream this release only fixes auto-scroll in the GUI, so nothing changes in RailMark.

Upgrading

railmark --version   # should report 0.11.0

See install.sh in this repo, or download the platform artifact below directly.

Full Changelog: v0.10.1...v0.11.0

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 14 Sep 13:27

Dependency bump

Bumps the RailReader.Core packages (RailReader.Export, RailReader.Renderer.Skia,
RailReader.Core.Analysis) from 0.60.2 to 0.62.1.

No annotation, export, or markup-plan behavior changed upstream in that range — this
release exists to pick up two transparent perf wins with no code changes required:

  • SkiaPdfService now caches page sizes instead of re-opening the document per lookup
  • Rendered-page bitmaps are marked immutable, avoiding a pixel copy on every
    SKImage.FromBitmap wrap (used by --images crop rendering)

All 88 unit tests and the full integration smoke suite (export, apply-markup,
annotation extraction, image crops) pass unchanged.

Full Changelog: v0.10.0...v0.10.1

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 07:32

What's new

  • RailReader.Core bumped 0.56.1 → 0.60.2 — picks up several upstream bug-fix releases (OCR request cancellation and threading fixes, scanned-page deskew, and the 0.60.2 GPU layout-detection correctness fix), none of which are breaking for RailMark.
  • New --accelerator cpu|gpu flag for --export — opportunistically runs layout detection on a native WebGPU execution provider, falling back to CPU automatically if no GPU device is available. Always uses the plain FP32 PP-DocLayoutV3 model on GPU (never Heron, never an FP16 export), verified to produce byte-identical Markdown to the CPU path.

Upgrading

railmark --version   # should report 0.10.0

See install.sh in this repo, or download the platform artifact below directly.

v0.9.1 — mirrored screenshot crops, split hyphenated words, Windows image links

Choose a tag to compare

@github-actions github-actions released this 12 Aug 18:44
80c8fa5

Bug-fix release. Two defects in annotation mode, a Core bump, and the release pipeline that now builds and tests these artifacts automatically.

Fixed

  • --images cropped every screenshot from the wrong half of the page. Rectangle and freehand crops were mirrored vertically: an annotation near the top of a page produced an image of the bottom. Only annotations straddling the page's vertical centre came out looking right, which is how it went unnoticed. (#22)
  • Wrap-hyphenated words split apart in annotation output. A highlight spanning a line-break hyphen rendered as inter pretable instead of interpretable. The markup geometry was always correct — this was the extracted text. (#21)
  • Invisible control characters in exported Markdown. --export emitted U+0002 where it rejoined a hyphenated word, so the output read correctly but did not contain the word as a searchable string. Fixed upstream in RailReader.Core 0.56.1 (RailReaderCore#101).

Changed

  • RailReader.Core packages 0.56.0 → 0.56.1.
  • The railmark-markup-plan skill is now self-installing: point Claude Code at its SKILL.md and ask it to install, and it sets up both the CLI and the skill, on Linux or Windows. (#20)

Infrastructure

  • CI builds and tests every push and pull request, and asserts the native libraries land in both the linux-x64 and win-x64 publish output.
  • Tagged releases now build, smoke-test and package all three artifacts automatically, with SHA256SUMS.
  • A synthetic test fixture plus 23 integration checks that run against each release artifact, not just against a dev build. Both fixed defects above were caught by that fixture. (#19)

Acknowledgements

Several text-handling behaviours in annotation mode were adapted from pdfannots by Andrew Baumann — de-hyphenation and its real-hyphen guard, ligature and smart-quote normalisation, Skim duplicate-comment suppression, the caret/strikeout editorial vocabulary, and within-page annotation ordering. See the README's Acknowledgements section.

Artifacts

All self-contained — the .NET runtime, native libraries and the ONNX layout model are bundled, so there is nothing else to install. Verify against SHA256SUMS.

File Platform
railmark-0.9.1-linux-x86_64.AppImage Linux x86-64 — recommended
railmark-0.9.1-linux-x86_64.tar.gz Linux x86-64 — for hosts without FUSE
railmark-0.9.1-win-x64.zip Windows x64

Install

bash <(curl -fsSL https://raw.githubusercontent.com/sjvrensburg/railmark/main/install.sh)

Or download an artifact below and put it on your $PATH.

v0.9.0 — better quote matching, full text-markup rendering

Choose a tag to compare

@sjvrensburg sjvrensburg released this 12 Aug 15:34
bf7ae28

Feature release. Bumps the RailReader.Core packages and adapts several text-handling techniques from 0xabu/pdfannots.

Quote matching is much more forgiving

--apply-markup resolves a quote against the PDF's extracted text. Two forms you'd naturally write previously failed outright:

  • Ligatures — quoting the first difficulty now matches a PDF that encodes fi/ffi as single glyphs.
  • Smart punctuation — straight " and ' match curly “ ” ‘ ’, and ... matches ….
  • Wrap hyphens — quoting interpretable in practice matches a PDF that breaks it as inter-\npretable in practice, and the markup correctly spans both lines. Real hyphens survive (COVID-19, H-1).

Annotation output

  • Fixed: underline, strikeout and squiggly annotations were silently dropped from annotation-mode Markdown. All four text-markup types are now rendered.
  • Fixed: underline and squiggly annotations produced no text at all, because their stored geometry is a thin baseline band rather than the glyph box.
  • Strikeouts and carets now read as editorial intent — a strikeout is a suggested deletion, a caret a suggested insertion, and a caret paired with a strikeout collapses into a single suggested replacement (~~old~~ → **new**).
  • A comment that merely repeats the text it covers — as Skim writes it — is dropped instead of printed twice.
  • Headings are matched by position within the page, so two headings on one page no longer file every annotation under the first.
  • The summary table's Highlights column is now Text markup and counts all four subtypes.

Under the hood

RailReader.Export / Renderer.Skia / Core.Analysis 0.52.1 → 0.56.0. Purely additive — the notable addition is OCR deskew, which corrects sub-degree scanner rotation on scanned pages going through --export, with nothing to configure.

Install

bash <(curl -fsSL https://raw.githubusercontent.com/sjvrensburg/railmark/main/install.sh)

Or download the AppImage below and put it on your $PATH.