CI crates.io (sdocx) npm docs.rs License
Reverse-engineered tooling and SDK for converting Samsung Notes (.sdocx) files.
The static application in web/ provides a local converter and a
continuous document preview. Parsing, rendering, and export happen in the
browser; user-selected documents are not uploaded. The generated site is
configured for Workers Static Assets at sdocx.twango.dev.
sdocx is a reverse-engineered parser, not a drop-in implementation of
Samsung's S Pen SDK. Archive structure, format versions, page ordering, and
supported packed stroke channels follow observed SDK contracts. Standalone text
boxes use bounded native frames and preserve Unicode, placement and rich-text
records. Image objects also use native frames and resolve their displayed asset
through media-manifest bind IDs. Shapes and lines decode native geometry,
outline/fill styles and embedded shape text; common templates, straight lines
and supported curves render to SVG. Text layout, image crop/border effects,
advanced shape styles and other page objects remain best-effort.
A successful parse may omit unsupported objects or properties; it does not guarantee a lossless decode. Preserve original documents and validate output against Samsung Notes when fidelity matters. Protected documents must be unlocked or exported before parsing.
Use parse_detailed or parse_bytes_detailed to inspect ParseReport, including
detected unsupported text/image/shape features and unresolved media. The CLI prints
these findings during conversion, and WASM exposes them through document
inspection. An empty report does not guarantee complete rendering fidelity.
For stored-hash checks, enable ParseOptions.verify_integrity with a detailed
parse API. ParsedDocument.integrity reports matched, mismatched and unavailable
checks for notes, objects, layers, pages and manifest links. These checks follow
Samsung's hash formulas; object hashes exclude geometry and content. See the
integrity findings for coverage.
For optional document metadata, call StoredNote::metadata with the complete
uncompressed note.note bytes. It exposes application/author information, pen
settings, voice and attachment references, and fixed text/background properties.
See note metadata findings
for bounded decoding and unknown-field handling.
Stored math, plot and formula objects have explicit inspection APIs for expressions, styles, embedded strokes and label graphs. They still produce unsupported-object diagnostics during page conversion because math rendering is not implemented. See formula findings for the APIs and remaining gaps.
Native image objects are exposed as PageElement::PlacedImage, including their
media ID, optional resolved asset index, bounds and rotation. Existing
caller-created PageElement::Image values remain renderable. See the
image findings for the supported
fields and resolution rules.
Native shapes and lines are exposed as PageElement::Shape and
PageElement::Line, with explicit geometry, styles and pen-resource references.
See the shape/line findings
for supported templates, paths and remaining rendering limits.
cargo install sdocx-cli
cargo add sdocx
npm install @twango/sdocx
docker pull ghcr.io/twangodev/sdocx
sdocx-cli note.sdocx
To include stored-hash diagnostics and coverage counts during conversion:
sdocx-cli note.sdocx --verify-integrity
Hash mismatches and unavailable checks are reported on stderr and do not stop conversion or change its exit status. A successful conversion with this flag does not establish that every integrity check passed.
For PNG or PDF export, supply font files when the document's fonts are unavailable
locally. Repeat --font for additional faces; explicit faces take precedence
over matching system fonts:
sdocx-cli note.sdocx -o note.png --font /path/to/Roboto-Regular.ttf --font /path/to/Roboto-Italic.ttf
Fonts are loaded once per document. Missing or invalid explicit font files
produce an error. SVG output references font families and does not embed fonts;
--font applies to PNG and PDF export.
PDF export writes all visible pages into one file:
sdocx-cli note.sdocx -o note.pdf --font /path/to/Roboto-Regular.ttf sdocx-cli note.sdocx --format pdf
--format overrides the output extension. SVG remains the default when neither
is supplied. SVG and PNG use separate files for multiple pages; PDF uses one
file. --pdf-dpi 144 sets the physical scale to 144 SVG units per inch; the
default is 96. This option applies only to PDF and does not rasterize vectors.
With Docker:
docker run --rm -v "$(pwd)":/data ghcr.io/twangodev/sdocx /data/note.sdocxuse sdocx::{layout_document, parse}; fn main() -> sdocx::Result<()> { let doc = parse("notes.sdocx")?; let layout = layout_document(&doc); println!( "{} visible page(s), {} stored page record(s)", layout.pages.len(), doc.pages.len() ); for visible_page in &layout.pages { for stroke in &visible_page.page.strokes { println!( "Stroke: {} points, color {:?}, width {}", stroke.points.len(), stroke.color, stroke.pen_width ); for point in &stroke.points { println!(" ({}, {})", point.x, point.y); } } } Ok(()) }
Rust 1.92 or newer is required. Enable the optional pdf feature (cargo add sdocx --features pdf):
use sdocx::{PdfOptions, RenderOptions, parse, render_document_pdf}; fn main() -> Result<(), Box<dyn std::error::Error>> { let document = parse("notes.sdocx")?; let pdf_options = PdfOptions::default(); let bytes = render_document_pdf(&document, &RenderOptions::default(), &pdf_options)?; std::fs::write("notes.pdf", bytes)?; Ok(()) }
render_document_pdf exports all visible pages into one PDF through the shared
SVG renderer. render_svg_pages_pdf accepts an existing Vec<RenderedPage>.
Vectors remain vectors and available fonts are embedded with selectable text.
For controlled fonts, populate sdocx::pdf::fontdb::Database and pass it in an
Arc to PdfOptions::new; that constructor does not discover system fonts.
Page dimensions use 96 SVG units per inch by default (one unit = 0.75 PDF
points). Set PdfOptions::dpi to change physical size. This is an explicit
export scale, not a decoded Samsung print setting. PDF pages are limited to
14,400 points per side. PNG images must decode within a 64 MiB buffer limit.
PDF inherits the SVG renderer's fidelity limits. Font fallback depends on the
provided fonts, some SVG filters rasterize, and PDF link annotations and
semantic document tags are not exported. The pdf feature is independent of
the browser/WASM bindings.
import init, { parse } from "@twango/sdocx"; await init(); const bytes = new Uint8Array(await file.arrayBuffer()); const doc = parse(bytes); for (const page of doc.pages) { for (const stroke of page.strokes) { console.log(`${stroke.points.length} points, color:`, stroke.color); } }
Large test documents and Samsung reference PDFs are kept in the
twangodev/sdocx-compatibility
dataset, tracked at hf/ as a Git submodule. Each SDK revision pins the dataset
commit used by its conformance checks. See
conformance/README.md for Git LFS setup, the locked
manifest and the local runner.
The maintained reverse-engineering documentation describes the archive format, native serializers, parser behavior and remaining fidelity gaps. Start with the file-format map for record layouts and the source map for supporting APK/native evidence.