Skip to main content

ofdrw-rs for Python

This README targets 0.5.0. Check the release workflow for distribution status; build matching bindings and native libraries from source while publication is pending. The previous v0.4.0 release remains available.

Python 3.10+ bindings to the Rust OFD core through CFFI. Install the matching version from PyPI:

python -m pip install ofdrw-rs==0.5.0

Check the registry and release pages for version availability. The wheel includes the native library; a Rust toolchain is not needed. Wheels for all six supported platforms are also available from GitHub Releases.

from ofdrw_rs import Document

with Document.new() as document:
    document.add_text_page("你好,OFD", 210, 297)
    document.save("hello.ofd")
    print(document.page_text(0))

Use Document.open(path), Document.from_bytes(data) or Document.from_pdf_bytes(data) to read existing content. Use page_svg(0), page_png(0, 96) or to_pdf() to export. Close documents or use a context manager. Coordinates are millimeters and page indexes start at zero.

Set OFDRW_LIB_PATH to an absolute shared-library path for a custom build. Supported behavior and limitations are documented in the repository README.

混合流布局(0.5.0 起)

with Document.new() as document:
    result = document.add_flow_blocks(0, [
        {"type": "paragraph", "text": "第一段"},
        {"type": "spacer", "height": 4},
        {"type": "paragraph", "spans": [{"text": "第二段", "bold": True}]},
    ], block_gap=2)
    document.add_flow_blocks(0, [{"type": "paragraph", "text": "继续"}],
                             start=result["next"])
    document.save("mixed.ofd")

支持图片、跨页表格和 page_break;图片可传 data 字节或 data_base64。 next 包含全局 page_index 和相对页面顶端的 y。整次调用失败时回滚。 详见混合流布局。

盒模型与浮动行(0.5.0 起)

with Document.new() as document:
    result = document.add_box_layout(0, [
        {"type": "box", "size": [40, 10], "padding": [2] * 4,
         "border": [0.2] * 4, "float": "left", "clear": "none"},
        {"type": "box", "size": [40, 10], "float": "right", "clear": "none"},
    ])
    placed = result["placements"][0]
    document.add_text_object(placed["page_index"], "Hello", placed["content_box"])
    document.save("boxes.ofd")

size 是内容宽高;margin、border、padding 均按上、右、下、左排列。 result["next"] 可作为下一次调用的 start。接口布局已测量尺寸的盒子并绘制装饰; 自动测量文本盒与嵌套混合容器见下文的布局接口。详见盒模型说明。

文本自动测量(0.5.0 起)

with Document.new() as document:
    spans = [{"text": "First line\nSecond line"}]
    metrics = document.measure_flowed_spans(0, spans, 40, font_size=5, line_height=6)
    print(metrics["width"], metrics["height"], metrics["lines"])
    boxes = document.add_box_layout(0, [{"type": "box", "size": [40, metrics["height"]],
        "padding": [2] * 4, "border": [0.2] * 4, "integrity": True}])

测量不会增加页面或资源。width 为最大行前进宽度,height 包含内部空行和段间距; 逐行 x、y、baseline 相对段落左上角。页面几何和 start 不参与测量。 将测得高度用于盒模型,再按内容矩形调用文本流式绘制;尺寸属于逻辑行框, 按行拆分的文本盒与一般嵌套容器见下文的布局接口。

Intrinsic image sizes (since 0.4.0)

Flow images, table-cell images and image blocks accept intrinsic_size: true (IntrinsicSize: true in Go). Omit width/height or leave them zero: pixel sizes are converted at 5 pixels/mm, matching Java OFDRW, regardless of embedded DPI metadata. fit_within: [width, height] (FitWithin: &[2]float64{width, height} in Go) optionally shrinks the image to millimeter bounds while preserving aspect ratio. It never enlarges smaller images. Nonzero explicit dimensions cannot be combined with intrinsic sizing. Without intrinsic sizing, both explicit dimensions remain required. These options share the same native validation in C and all bindings, available since 0.4.0.

from pathlib import Path
from ofdrw_rs import Document

with Document.new() as doc:
    doc.add_flow_blocks(0, [{
        "type": "image", "data": Path("picture.png").read_bytes(),
        "format": "PNG", "intrinsic_size": True, "fit_within": [40, 30],
    }])
    Path("picture.ofd").write_bytes(doc.to_bytes())

Automatic text boxes (since 0.4.0)

from pathlib import Path
from ofdrw_rs import Document

with Document.new() as doc:
    result = doc.add_text_boxes(0, [{
        "layout": {"size": [40, 0], "padding": [2, 2, 2, 2],
                   "border": [0.2, 0.2, 0.2, 0.2]},
        "spans": [{"text": "Text wraps and grows this decorated box."}],
        "style": {"font_size": 4, "line_height": 5},
    }])
    Path("text-box.ofd").write_bytes(doc.to_bytes())

Content height replaces layout.size[1] / Layout.Size[1] automatically. Width is clamped before wrapping, and complete lines move to the next page. Typography can enable shaped text or paragraph keep rules; page geometry and continuation come from the shared box options. The result contains all fragment rectangles and next (Next in Go), reusable as start (Start). General nested child containers use the mixed container API below. This API is available since 0.4.0.

Nested mixed containers (since 0.4.0)

from pathlib import Path
from ofdrw_rs import Document

with Document.new() as doc:
    result = doc.add_flow_containers(0, [{
        "layout": {"size": [40, 0], "padding": [1, 1, 1, 1]},
        "gap": 1,
        "children": [
            {"type": "paragraph", "spans": [{"text": "Heading"}],
             "style": {"font_size": 4, "line_height": 5}},
            {"type": "container", "container": {
                "layout": {"size": [30, 0], "border": [0.2, 0.2, 0.2, 0.2]},
                "children": [{"type": "paragraph", "spans": [{"text": "Nested text"}],
                              "style": {"font_size": 2, "line_height": 3}}],
            }},
        ],
    }])
    Path("containers.ofd").write_bytes(doc.to_bytes())

Image children accept data bytes or data_base64; font IDs in nested styles can use the integer returned by add_embedded_font.

Each container has layout, optional gap, and children. Supported child types are paragraph, image, container, spacer, table, line_break and page_area_fill. A table child has a table grid and optional table_style (font_size, line_height, font_id, repeat_header). Images accept explicit dimensions or intrinsic sizing and fit bounds. Spacer height defaults to zero. Returned root fragments and next / Next support continuation with shared start / Start options.

Containers support floating rows at every nesting level. Adjacent nested boxes share rows according to float, clear and width rules, with independent page continuation per column. Paragraphs, images, tables and spacers occupy full rows; gap separates rows. Repeated table headers stay with complete body-row groups, and header/body pairs that cannot fit a full page fail. Fragment heights include repeated headers and the space needed to align continuing floating columns. The whole call rolls back on invalid children. Limits are eight container levels and 10,000 container/child entries. This API is available since 0.4.0.

line_break ends the current nonempty floating row; leading, repeated and trailing empty breaks are ignored. page_area_fill consumes the current page's remaining content area. If that area is already full, it starts and fills the next page, so consecutive fills preserve explicit blank pages. A trailing fill does not append another page. Fills reset row gaps and affect only their own floating column. Both controls accept only type; a complete (integrity) container that would span pages fails atomically.

{"children": [{"type": "line_break"}, {"type": "page_area_fill"}]}

Dashed box borders (since 0.4.0)

border_dash (BorderDash in Go) uses Java Div.setBorderDash arguments: [3] means dash/gap [3, 3]; [3, 2] means dash/gap [3, 2]; [1, 3, 2] sets offset 1 followed by dash/gap [3, 2]. Longer even sequences contain dash/gap pairs, and longer odd sequences start with the offset. Null or an empty sequence selects a solid border. Lengths must be positive and finite, with at most 256 lengths; offsets below 0.000001 are omitted, matching Java. Invalid patterns fail atomically, including placeholders.

This field works in measured boxes, automatic text boxes and nested mixed containers. Equal-width borders use one closed stroked path; different edge widths use separate paths with horizontal edges owning the corners. Page cuts preserve the existing top/bottom border fragmentation rules. Dash phase starts at each emitted path; allocation, text wrapping and continuation are unchanged.

{"size": [40, 10], "border": [0.5, 0.5, 0.5, 0.5], "border_dash": [1, 3, 2]}

Box painting layers (since 0.4.0)

Set layout.layer (Layout.Layer in Go) to background, body or foreground. Measured box items accept the same layer field directly. Decoration, ordinary/shaped text, underline paths, images and table children use the selected layer across page fragments. A nested container without a layer inherits its parent; an explicit layer overrides it for that subtree. Roots default to Body. Painting follows OFD Background → Body → Foreground order independently of the order in which boxes were submitted.

Existing direct page layers of the chosen type are reused; another layer is created when needed. Existing empty Body layers on blank pages are retained. Layer selection does not change measured dimensions, pagination or returned placements. Later ordinary drawing calls continue to use Body. Invalid layer values fail atomically. The same rules apply to static and advanced container pagination and to existing pages with nonzero canvas origins.

document.add_box_layout(0, [{"type": "box", "size": [40, 10],
    "background_color": [240, 240, 240], "layer": "background"}])

Fixed page containers (since 0.4.0)

Available since 0.4.0; use bindings with their matching native library. add_fixed_containers draws on an existing global page and returns a placement list:

from pathlib import Path
from ofdrw_rs import Document

with Document.new() as doc:
    doc.add_blank_page(80, 60)
    placements = doc.add_fixed_containers(0, [{
        "position": [10, 20],
        "container": {
            "layout": {"size": [40, 0]},
            "children": [{"type": "paragraph", "spans": [{"text": "Hello"}]}],
        },
    }])
    Path("fixed.ofd").write_bytes(doc.to_bytes())

Positions are absolute OFD millimeters. Zero height measures the content; a positive height controls allocation and decorations while children may overflow. The call never adds pages. It supports the same nested text/image/table content, offsets, layers and placeholders as flow containers. Page-area fillers are ignored. Invalid later content rolls back the entire call. Images accept bytes in data.

Inline placeholders (since 0.4.0)

A span with empty text, placeholder_width in millimeters and font_size as height reserves an indivisible inline rectangle without painting objects or font resources. Width zero is allowed. For example:

with Document.new() as doc:
    metrics = doc.measure_flowed_spans(0, [
        {"text": "Label:"},
        {"text": "", "placeholder_width": 8, "font_size": 4},
        {"text": "value"},
    ], 50)
    assert metrics["lines"][0]["text"] == "Label:value"

The same spans work in styled flow, text boxes, flow blocks and fixed/flow containers. Reserved geometry participates in wrapping and maximum line height; measured/extracted text omits placeholders. Invalid later input rolls back the call. A leading placeholder absorbs a configured first-line indent once. For character-count reservations use count * (font_size + letter_spacing) as the width. Available since 0.4.0; use matching bindings and native library.

Protected content ranges (unreleased source API)

Use matching source bindings and native library; this addition is outside 0.5.0. Protect complete measured lines while placing/decorating a box:

metrics = doc.measure_flowed_spans(0, [{"text": "A\nB\nC\nD"}], 8,
                                   font_size=2, line_height=3)
policies = [{"source_index": 0, "ranges": [[line["y"], line["y"] + line["height"]]
             for line in metrics["lines"]]}]
result = doc.add_box_layout_with_content_ranges(0,
    [{"type": "box", "size": [8, metrics["height"]]}], policies,
    page_size=[20, 14], margins=[2] * 4)

Intervals use original content coordinates and must be ordered, positive, disjoint and inside the box. A cut inside an interval moves it intact to the next page. The result uses ordinary box-layout fragment fields and continuation; callers draw child content separately. Empty policies retain geometric cuts. Invalid policies roll back the entire call.

Device hairlines (0.5.0 candidate)

Pass line_width=0 to add_path_object, add_composite_path_object, or add_path_annotation for a one-device-pixel path stroke. Existing paths accept set_page_path_stroke_style(0, object_id, {"line_width": 0}). PNG retains device width across DPI and transforms; PDF preserves a native zero-width stroke. Use the matching unreleased native library.

Vector canvas batches (0.5.0 candidate)

Use a matching native library built from the same unreleased source.

from ofdrw_rs import Document

with Document.new() as document:
    document.add_blank_page(40, 30)
    ids = document.draw_canvas(0, (0, 0, 40, 30), [
        {"type": "set_stroke_color", "color": [0, 80, 200]},
        {"type": "rect", "rect": [2, 3, 12, 8]},
        {"type": "stroke"},
    ])
    document.save("canvas.ofd")

The batch commits atomically and returns painted object IDs as strings. Coordinates are millimeters; angles are degrees. Use save/restore for paths and drawing state, and begin_path to clear the path. Unknown commands and fields are rejected. Set global alpha to None to clear it. See the command reference for all vector operations and remaining text limitations.

Canvas images (0.5.0 candidate)

Image commands accept bytes, bytearray or memoryview in data. Alternatively, use data_base64; providing both is an error. draw_image_at uses natural pixel dimensions divided by pixels_per_mm (default 3.78). draw_image_region converts its source rectangle from millimeters to integer pixels by truncation, matching Java; portions outside the source are transparent. Negative target widths/heights mirror the image, and zero dimensions collapse it.

from pathlib import Path
from ofdrw_rs import Document

image = Path("picture.png").read_bytes()
with Document.new() as document:
    document.add_blank_page(40, 30)
    document.draw_canvas(0, (0, 0, 40, 30), [
        {"type": "draw_image", "data": image, "format": "png", "rect": [2, 2, 12, 8]},
        {"type": "draw_image_region", "data": image, "format": "png",
         "source": [0, 0, 1, 1], "rect": [20, 2, 8, 8], "pixels_per_mm": 2},
    ])
    document.save("canvas-images.ofd")

Canvas text (0.5.0 candidate)

set_font configures size, tracking, weight, italic, alignment and quarter-turn reading/character directions. Omitted fields use SimSun, 1 mm, weight 400 and start alignment. measure_canvas_text returns logical width, first glyph offset and signed advances without modifying the document. These metrics exclude kerning and shaping. Embedded fonts provide reproducible measurements.

from ofdrw_rs import Document

font = {"size": 4, "align": "center"}
with Document.new() as document:
    document.add_blank_page(40, 30)
    metrics = document.measure_canvas_text(0, "Canvas", font)
    document.draw_canvas(0, (0, 0, 40, 30), [
        {"type": "set_font", "font": font},
        {"type": "fill_text", "text": "Canvas", "point": [20, 10]},
    ])
    document.save("canvas-text.ofd")
    print(metrics["width"])

Metadata

Release files for ofdrw-rs 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for ofdrw-rs 0.5.0
File Actions
ofdrw_rs-0.5.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details for ofdrw_rs-0.5.0-py3-none-win_arm64.whl
ofdrw_rs-0.5.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for ofdrw_rs-0.5.0-py3-none-win_amd64.whl
ofdrw_rs-0.5.0-py3-none-manylinux_2_35_x86_64.manylinux_2_39_x86_64.whl Python 3 none Linux glibc 2.35+ x86-64, Linux glibc 2.39+ x86-64 Details for ofdrw_rs-0.5.0-py3-none-manylinux_2_35_x86_64.manylinux_2_39_x86_64.whl
ofdrw_rs-0.5.0-py3-none-manylinux_2_35_aarch64.manylinux_2_39_aarch64.whl Python 3 none Linux glibc 2.35+ ARM64, Linux glibc 2.39+ ARM64 Details for ofdrw_rs-0.5.0-py3-none-manylinux_2_35_aarch64.manylinux_2_39_aarch64.whl
ofdrw_rs-0.5.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for ofdrw_rs-0.5.0-py3-none-macosx_11_0_arm64.whl
ofdrw_rs-0.5.0-py3-none-macosx_10_13_x86_64.whl Python 3 none macOS 10.13+ x86-64 Details for ofdrw_rs-0.5.0-py3-none-macosx_10_13_x86_64.whl

Total release size: 70.9 MB

Release files / ofdrw_rs-0.5.0-py3-none-win_arm64.whl

Download URL ofdrw_rs-0.5.0-py3-none-win_arm64.whl
Size 10.9 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
51b528ac1ce8ae002945dee4ec6a82ccc1035403e3225989d50862218ea1cb19
BLAKE2b-256 checksum
How to use checksums
d7b6b2541e472657bc6e749d81dea18e79d5a26b22b6ca53d0d757323e2236e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / ofdrw_rs-0.5.0-py3-none-win_amd64.whl

Download URL ofdrw_rs-0.5.0-py3-none-win_amd64.whl
Size 11.7 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
f54c3b6181936ce8cffd7ee69224521acc4a7b0b1026abbddddff8144fd8b58a
BLAKE2b-256 checksum
How to use checksums
1aa338f4213ced4b373da3ff01e25260c9997d48808053cf391aae0ec5d4613a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / ofdrw_rs-0.5.0-py3-none-manylinux_2_35_x86_64.manylinux_2_39_x86_64.whl

Download URL ofdrw_rs-0.5.0-py3-none-manylinux_2_35_x86_64.manylinux_2_39_x86_64.whl
Size 12.6 MB
Tags Linux glibc 2.35+ x86-64 Linux glibc 2.39+ x86-64 Python 3
SHA-256 checksum
How to use checksums
5a886aadb88e476c075e17ac75c7f733e45317e54d942fa76e5077847520b819
BLAKE2b-256 checksum
How to use checksums
611ab2b2a3ea6ea8db560f60b156ab217c4549bc69e6401055b0aa650279020a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / ofdrw_rs-0.5.0-py3-none-manylinux_2_35_aarch64.manylinux_2_39_aarch64.whl

Download URL ofdrw_rs-0.5.0-py3-none-manylinux_2_35_aarch64.manylinux_2_39_aarch64.whl
Size 12.1 MB
Tags Linux glibc 2.35+ ARM64 Linux glibc 2.39+ ARM64 Python 3
SHA-256 checksum
How to use checksums
904e21301565e6a4baea7f98df54783a56cf0966250cc7f53ff505e944718cbf
BLAKE2b-256 checksum
How to use checksums
ed7e24ea1296dee4a5826cfd712a750b9cd1128f14fe54e9d78f47171f638f73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / ofdrw_rs-0.5.0-py3-none-macosx_11_0_arm64.whl

Download URL ofdrw_rs-0.5.0-py3-none-macosx_11_0_arm64.whl
Size 11.5 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
54631b36d2b7ed8d8a09532259a43d6e9c05d3905c18b6ff4ff1612eecbfc4c0
BLAKE2b-256 checksum
How to use checksums
af6f80d54823cc7d6865522a8c760748ceec06eb106d3590ae374c20a9adf362
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / ofdrw_rs-0.5.0-py3-none-macosx_10_13_x86_64.whl

Download URL ofdrw_rs-0.5.0-py3-none-macosx_10_13_x86_64.whl
Size 12.1 MB
Tags Python 3 macOS 10.13+ x86-64
SHA-256 checksum
How to use checksums
7efea0274cbe9d0ded58cee521334680c45ea8c454631b0fbd660003c4096a48
BLAKE2b-256 checksum
How to use checksums
41552df9604bb6efef2c6e5b9526b3b63d2c361c5380c69e227ca092e8b4634c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Security for ofdrw-rs 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page