>_STRYKE-OFFICE
Read and write the whole office suite — no LibreOffice required. Excel/Calc (xlsx/ods), Word/Writer (docx/odt), PowerPoint/Impress (pptx/odp), PDF, and a full PIL-style image surface (png/jpeg/gif/bmp/webp/tiff — open/save/crop/resize/convert/paste/draw), entirely in native Rust. No soffice / LibreOffice / pandoc subprocess; the cdylib is self-contained.
Install
# from a release tarball s pkg install -g github.com/MenkeTechnologies/stryke-office # from a local checkout cd ~/projects/stryke-office cargo build --release s pkg install -g . # cdylib lands in ~/.stryke/store/office@<version>/ # one-liner make install
This is a library — there is no CLI launcher. The cdylib is dlopened in-process on the first use Office.
Quick start
use Office
# spreadsheet: write xlsx, read back, re-emit as ods
Office::sheet_write("report.xlsx", [
{ name => "Sales", rows => [["product", "units"], ["widget", 120]] },
])
val $sheets = Office::sheet_read("report.xlsx")
Office::sheet_write("report.ods", $sheets)
# document: build a docx from structured blocks
Office::doc_write("memo.docx", [
{ kind => "heading", level => 1, text => "Quarterly Report" },
{ kind => "para", text => "Revenue grew 18%." },
])
# presentation + pdf
Office::slides_write("deck.pptx", [{ title => "Intro", body => ["point one"] }])
Office::pdf_write("out.pdf", ["Line one", "Line two"])
Format matrix
| Spreadsheet | read xlsx / ods / xls / csv / tsv · write xlsx, ods, csv, tsv, html, md |
| Document | read docx / odt / html / md / rtf / txt · write docx, odt, html, md, rtf, txt, pdf |
| Presentation | read + write pptx, odp |
| read text + pages · write text (A4) · merge/split/rotate/encrypt/forms/outline | |
| Image | read + write png/jpeg/gif/bmp/webp/tiff/avif/ico/tga/qoi/pnm/dds/hdr/exr/ff · resize/crop/rotate/convert/paste · draw rect/line/circle/text · filters / color science / distortions (PIL-complete) |
Output format comes from the path extension; override with format => "...". Every raster codec the image crate ships is enabled, so read and write cover the full image list above.
Surface at a glance
The Office namespace exposes 465 public functions across these families. The full reference for each is in the tables below.
| sheet_* — 175 | read/write + dataframe analytics (group-by, pivot, join, stats, time-series, finance, sampling, export) |
| img_* — 111 | PIL-complete handle-based image surface |
| pdf_* — 47 | build/merge/split/encrypt/forms/outline/annotations/attachments |
| doc_* — 30 | read/write, structure, stats, summary, diff, convert |
| slides_* — 26 | read/write, reorder, merge, stats, convert |
| text_* — 20 | plain-text toolkit (grep/sort/uniq/sed/cut/…) |
| range/a1/r1c1 — 20 | A1 / R1C1 / range math (no workbook opened) |
| chart_* — 5 | 60+ chart types, raster + SVG + PDF |
| barcode_* — 4 | QR + 1D → image handle |
| converters & misc — ~27 | csv/md/html/xml/json/ndjson ↔ sheets, metadata, color, media extraction, mail-merge |
Core I/O
| Office::version() | package version string |
| Office::info($path) | universal inspector — identify any office/image file + type summary |
| Office::sheet_read($path, %opts) | arrayref of { name, rows } — numbers stay numeric, empty cells undef; formulas => 1 attaches formula strings; delimiter for csv/tsv |
| Office::sheet_write($path, $sheets, %opts) | $sheets: [{ name, rows => [[...]] }]; writes xlsx/ods/csv/tsv + html/md tables; format / defined_names / properties opts |
| Office::doc_read($path) | list of paragraph strings (docx/odt) |
| Office::doc_write($path, $blocks, %opts) | block: { kind => "para"|"heading"|"list"|"link"|"table"|"image"|"pagebreak", … }; header/footer/page_numbers/page_size |
| Office::slides_read($path) | arrayref of { text => [...], notes => [...] } (pptx/odp) |
| Office::slides_write($path, $slides, %opts) | slide: { title, body => [...] } |
| Office::pdf_read($path) | { pages => [...], text } |
| Office::pdf_write($path, $lines) | $lines: arrayref of strings (A4) |
| Office::pdf_build($path, $elements, %opts) | multi-page: heading/paragraph/text/image/rect/line/table/pagebreak; page_size/margin |
Spreadsheet analytics (sheet_*)
175 functions turn a spreadsheet path into a dataframe-style toolkit. Each opens the file, applies the operation, and (when an $output is given) writes the result; the target format is taken from the output extension. A few representative groups:
| Inspect | sheet_info, sheet_names, sheet_dims, sheet_stats, sheet_describe, sheet_profile, sheet_dtypes, sheet_count, sheet_nunique, sheet_mode, sheet_duplicates |
| Stats | sheet_quantile, sheet_corr, sheet_cov, sheet_regress, sheet_forecast, sheet_ttest, sheet_anova, sheet_chisq, sheet_moments, sheet_means, sheet_mad, sheet_entropy, sheet_gini, sheet_histogram, sheet_outliers |
| Finance | sheet_npv, sheet_irr, sheet_cagr, sheet_drawdown, sheet_amortize, sheet_returns, sheet_bollinger, sheet_sumproduct, sheet_autocorr |
| Reshape | sheet_pivot, sheet_unpivot, sheet_join, sheet_union, sheet_merge, sheet_hstack, sheet_cross, sheet_transpose, sheet_explode, sheet_split_column, sheet_concat_columns, sheet_aggregate, sheet_group_concat |
| Filter / select | sheet_filter, sheet_where, sheet_grep, sheet_select, sheet_drop, sheet_sort, sheet_multisort, sheet_top, sheet_head, sheet_dedupe, sheet_sample, sheet_train_test_split |
| Clean / transform | sheet_cast, sheet_clean, sheet_strip, sheet_fillna, sheet_impute, sheet_interpolate, sheet_dropna, sheet_recode, sheet_map, sheet_normalize, sheet_standardize, sheet_clamp, sheet_winsorize, sheet_round |
| Time-series | sheet_movavg, sheet_rolling, sheet_ewm, sheet_cumsum, sheet_pct_change, sheet_delta, sheet_shift, sheet_resample, sheet_date_part, sheet_date_diff, sheet_date_add, sheet_networkdays, sheet_weekday |
| Cells / structure | sheet_get_cell, sheet_set_cell, sheet_get_range, sheet_set_range, sheet_insert_rows, sheet_delete_rows, sheet_freeze, sheet_autofilter, sheet_merge_cells, sheet_autosize, sheet_protect, sheet_add, sheet_copy, sheet_remove, sheet_reorder |
| Excel-style | sheet_lookup (VLOOKUP), sheet_countif, sheet_sumif, sheet_sumifs, sheet_rank, sheet_subtotal, sheet_totals, sheet_pct, sheet_calc, sheet_row_stats |
| Export | sheet_to_csv, sheet_to_json, sheet_to_ndjson, sheet_to_xml, sheet_to_sql, sheet_to_md, sheet_to_html, sheet_to_latex, sheet_to_text, sheet_to_pdf, sheet_to_doc, sheet_to_slides |
Rich formatting
Spreadsheet cells and document runs accept styling, not just scalars. A cell can be a scalar or a rich object; a docx block can carry styled runs.
Office::sheet_write("r.xlsx", [{ name => "S", rows => [
[{ v => "Header", bold => 1, color => "#FF0000", bg => "#FFFF00", align => "center" }],
[{ v => 42, num_format => "0.00", italic => 1 }],
[{ f => "=A2*2" }], # formula cell
] }])
# cell keys: v|value, f|formula, bold, italic, underline, font, size,
# color, bg, align (left/center/right), num_format, border
Office::doc_write("r.docx", [
{ kind => "para", align => "center", runs => [
{ text => "Bold ", bold => 1, color => "#0000FF", size => 18 },
{ text => "and italic", italic => 1 },
] },
])
Sheet-level structure (xlsx) also supports merges, frozen panes, autofilter, worksheet tables, conditional formatting, data-validation dropdowns, sparklines, notes, embedded images, outline grouping, page setup, defined names, and workbook properties. ODF (ods/odt) and PDF are written unstyled — the lo_odf/lo_core serializers do not expose per-run styling (a documented crate limitation, not faked).
Documents (doc_*) & presentations (slides_*)
| doc_read / doc_paragraphs / doc_lines | paragraph + line text (docx/odt; lines also pdf) |
| doc_blocks / doc_outline / doc_tables / doc_links | ordered structural read, heading outline, table grids, hyperlinks |
| doc_stats / doc_wordfreq / doc_readability | Word-style counts, word-frequency ranking, Flesch / Flesch–Kincaid |
| doc_sentences / doc_summary / doc_extract / doc_diff | sentence split, extractive summary, regex extraction, LCS paragraph diff |
| doc_comments / doc_footnotes / doc_add_toc | review comments, footnotes/endnotes, generated Table of Contents |
| doc_merge / doc_append / doc_split | concatenate, append blocks, split at headings |
| doc_to_md / _html / _text / _pdf / _csv / _sheet / _slides | convert a docx/odt out to any of these (and md/html/pdf back in via md_to_doc / html_to_doc) |
| slides_read / slides_count / slides_names / slides_outline | read text + notes, count, titles, outline |
| slides_add_image / slides_add_text / slides_set_notes | embed a picture / text box / speaker notes onto a pptx slide |
| slides_insert / _delete / _reorder / _reverse / _merge / _append | edit a deck in place |
| slides_to_doc / _pdf / _md / _html / _text / _sheet | convert a deck out; md_to_slides builds one from a Markdown outline |
PDF (pdf_*)
| pdf_build / pdf_blank / images_to_pdf / sheet_to_pdf | generate from a content model, blank pages, image-per-page, bordered table |
| pdf_merge / _split / _extract / _delete / _reorder / _reverse | page-level surgery (1-based; pdf_extract takes "1-3,5,8-10" range specs) |
| pdf_burst / _chunk / _split_ranges / _split_bookmarks | split into many files (per page, fixed chunks, ranges, chapter bookmarks) |
| pdf_rotate / _crop / _compress / _remove_blank | rotate 90° multiples, set crop box, prune+deflate, drop empty pages |
| pdf_encrypt / _decrypt | AES-128 or RC4 standard security handler with per-op permission grants |
| pdf_form_fields / _fill_form | list + fill AcroForm fields (checkbox takes a bool; sets /NeedAppearances) |
| pdf_outline / _set_outline | read + write the bookmark navigation tree (nested via children) |
| pdf_add_link / _links / _highlight / _annotations / _remove_annotations | URI links, highlights, annotation listing + stripping |
| pdf_attach / _attachments | embed (EmbeddedFiles name tree) + list/extract attachments |
| pdf_watermark / _stamp_image / _page_numbers / _add_text / _draw_rect / _draw_line | overlays + stamps on existing pages |
| pdf_info / _page_sizes / _stats / _search | geometry + metadata, per-page sizes, word/char stats, full-text search |
| pdf_to_doc / _to_slides / _to_sheet / _to_text | convert a PDF's text out (page breaks preserved) |
# inspect a form, then fill it (checkbox takes a bool)
val $f = Office::pdf_form_fields("application.pdf")
Office::pdf_fill_form("application.pdf", {
"applicant_name" => "Jane Doe",
"agree_terms" => \1,
}, output => "application-filled.pdf")
# password-protect with AES-128, allow printing only, then strip it back off
Office::pdf_encrypt("report.pdf", "report-locked.pdf",
owner_password => "admin", user_password => "view",
aes => \1, permissions => ["print"])
Office::pdf_decrypt("report-locked.pdf", "report-open.pdf", password => "admin")
Images (img_*) — handle-based, like a PIL Image
Image ops return a numeric handle: open once, transform many times, then img_save / img_close. Geometry-changing ops return a new {handle, width, height, mode}; in-place filters mutate the handle.
| I/O | img_open, img_new, img_save, img_info, img_convert, img_close, img_to_base64, img_from_base64, img_data_uri, img_resize_file |
| Geometry | img_resize, img_thumbnail, img_scale, img_fit, img_crop, img_crop_aspect, img_rotate, img_flip, img_transpose, img_transverse, img_canvas, img_trim, img_warp |
| Compose | img_paste, img_blend, img_blend_mode, img_composite, img_montage, img_concat, img_border, img_watermark, img_drop_shadow, img_caption |
| Draw | img_draw_rect, img_draw_rounded_rect, img_draw_line, img_draw_circle, img_draw_ellipse, img_draw_arc, img_draw_polygon, img_draw_polyline, img_draw_text, img_draw_text_multiline, img_flood_fill |
| Filters | img_blur, img_box_blur, img_median, img_sharpen, img_edges, img_emboss, img_convolve, img_brighten, img_contrast, img_gamma, img_invert, img_grayscale, img_sepia, img_tint, img_posterize, img_threshold, img_solarize, img_autocontrast, img_equalize, img_pixelate, img_vignette, img_noise, img_dilate, img_erode |
| Color science | img_levels, img_curves, img_hsl, img_huerotate, img_temperature, img_channel_mixer, img_point, img_colorize, img_gradient, img_replace_color, img_swap_channels, img_dither, img_quantize |
| Distortions | img_swirl, img_wave, img_fisheye, img_kaleidoscope, img_seam_carve |
| Analysis | img_histogram, img_extrema, img_entropy, img_count_colors, img_dominant_colors, img_average_color, img_brightness, img_bbox, img_compare, img_phash, img_get_pixel |
| Animation / batch | img_open_frames, img_save_animated, img_spritesheet, img_split, img_merge, img_reduce, img_favicon, contact_sheet |
val $im = Office::img_open("photo.jpg")
Office::img_resize($im->{handle}, 800, 600, filter => "lanczos3")
Office::img_grayscale($im->{handle})
Office::img_save($im->{handle}, "out.png") # transcode by extension
Office::img_close($im->{handle})
Charting (chart_*)
Render charts natively (imageproc + the vendored font — no plotters, no system fonts). 60+ types render identically in raster and SVG.
| Office::chart_render($type, $series, %opts) | → raster image handle (then img_save to any format) |
| Office::chart_svg($type, $series, %opts) | → vector SVG markup (or write to path =>) |
| Office::chart_save($type, $path, %opts) | write straight to a file, format by extension: .svg, .pdf, or any raster |
| Office::chart_from_sheet($path, $output, %opts) | read a spreadsheet's columns and render in one call |
| Office::chart_grid($charts, %opts) | tile many specs into one dashboard image / pdf |
Types include bar/column/stacked, line/area/step/combo, scatter/bubble, pie/donut, histogram, radar, sankey, waterfall, ohlc/candlestick, boxplot, funnel, gauge, heatmap, treemap, sunburst, calendar, plus a ggplot2-style set (density, violin, ecdf, qq, ribbon, jitter, rug, beeswarm, contour, ridgeline, smooth/loess, bin2d, pairs, dendrogram).
for val $spec ([["bar","png"], ["line","jpg"], ["pie","webp"]]) {
val ($type, $fmt) = @$spec
val $c = Office::chart_render($type, [{ name => "Sales", data => \@sales }],
title => "Q sales", categories => \@cats)
Office::img_save($c->{handle}, "out-$type.$fmt")
Office::img_close($c->{handle})
}
Barcodes & QR codes (barcode_*)
Both produce an image handle, so the result composes with the entire image surface — save, paste onto a label canvas, or embed in a PDF/docx. Generated natively (qrcode + barcoders).
| barcode_qr(%opts) | QR; data, ec (L/M/Q/H), scale, quiet, fg/bg |
| barcode_1d(%opts) | 1D; symbology code128/39/93/11, codabar, ean13/8, upca, itf, std2of5 |
| barcode_save($data, $output, %opts) | generate straight to an image file |
| barcode_sheet($path, $column, $dir, %opts) | batch one barcode/QR per column value into a directory |
Metadata, media, templates, text & converters
| meta_read / meta_write / meta_strip | read/set/blank document properties losslessly (xlsx/docx/pptx, ods/odt/odp, pdf) |
| extract_images($path, %opts) | pull every picture out as a live image handle (OOXML/ODF/PDF) |
| replace_text / mail_merge / text_template | fill {{placeholder}} templates (run-coalescing for OOXML); one document per record |
| text_* (20) | plain-text toolkit: text_grep, text_sort, text_uniq, text_sed, text_cut, text_wrap, text_tr, text_paste, text_comm, text_join, text_shuf, text_base64, text_hash, text_redact, text_tac, text_head, text_stats, … |
| csv_to_sheet / csv_to_xlsx / xlsx_to_csv | CSV ↔ spreadsheet (RFC-4180 quoting) |
| md_to_sheet / html_to_sheet / json_to_sheet / ndjson_to_sheet / xml_to_sheet | import structured formats into a sheet |
| color_info / color_contrast | full color breakdown (hex/rgb/hsl/luminance) + WCAG contrast ratio |
Cell-reference helpers (no workbook)
Pure A1 / R1C1 / range math — no file is opened, so these run standalone.
| parse_a1 / parse_a1_abs / a1_of / a1_abs_of | A1 ↔ 0-based row/col, with $-absolute support |
| a1_to_r1c1 / r1c1_to_a1 | Excel R1C1 notation (absolute or anchor-relative) |
| offset_a1 / cell_delta | shift an A1 ref / the offset between two cells |
| col_to_letter / letter_to_col | column letters ↔ 0-based index |
| parse_range / range_of / expand_range / bounding_range | range parsing, building, enumeration, bounding box |
| range_intersection / range_union / range_contains / range_offset / range_to_absolute / range_to_relative | range set-ops and transforms |
Troubleshooting
| doc_read drops a table | By design — doc_read returns paragraph text. Use doc_tables for table grids or doc_blocks for ordered structure. |
| ODF / PDF output is unstyled | The lo_odf / lo_core serializers do not expose per-run styling. Rich formatting applies to xlsx + docx only. |
| Formulas read but don't evaluate | sheet_read(…, formulas => 1) returns formula strings; values are whatever the writing app computed. There is no formula evaluator (Roadmap item). |
| No docx→pdf "laid out like Word" | Generic render-conversion needs a layout engine, which the no-external-binary rule excludes. doc_to_pdf renders from the content model, not a Word layout. |
| Leaked image handles | Every img_open / img_new / geometry op returns a handle into an in-process table; call img_close when done. |
| extract_images skipped a picture | $r->{skipped} counts recognized-but-undecodable media (e.g. JPEG2000). |
No external binaries
Each format maps to a vendored Rust crate, statically linked: calamine (xlsx/xls read), rust_xlsxwriter (xlsx write), lo_odf (ods/odt/odp write), docx-rs (docx write), zip+quick-xml (OOXML/ODF read + pptx write), and lo_core (PDF read+write, no font files). There is deliberately no soffice call — so there is no generic render-conversion (docx→pdf laid out like Word), which needs a layout engine. You get structured read, structured write, and PDF generation from a content model.
Sibling packages
Part of the stryke package family. Browse the others via the MenkeTechnologiesMeta umbrella repo:
- stryke-arrow — Apache Arrow / Parquet / Feather
- stryke-aws — S3, DynamoDB, SQS, Lambda, STS
- stryke-azure — Blob, Queues, Cosmos DB, Key Vault, Entra
- stryke-clickhouse — ClickHouse
- stryke-demo — live demos for every connector
- stryke-docker — Docker daemon API
- stryke-duckdb — embedded DuckDB SQL engine
- stryke-email — email + campaign client
- stryke-fleet — parallel expect/PTY automation
- stryke-gcp — Cloud Storage, Pub/Sub, Secret Manager, BigQuery, Firestore
- stryke-grpc — reflection-based gRPC client
- stryke-gui — mouse / keyboard / screen / clipboard automation
- stryke-k8s — Kubernetes
- stryke-kafka — Apache Kafka
- stryke-mcpd — MCP servers without a runtime
- stryke-mongo — MongoDB
- stryke-mssql — Microsoft SQL Server
- stryke-mysql — MySQL / MariaDB
- stryke-neo4j — Neo4j graph (Bolt)
- stryke-parquet — Parquet file inspector
- stryke-polars — Polars + ndarray + linalg + FFT
- stryke-postgres — PostgreSQL
- stryke-redis — Redis / Valkey
- stryke-scrape — web scraping / crawling
- stryke-scylla — ScyllaDB / Cassandra
- stryke-search — Elasticsearch / OpenSearch
- stryke-selenium — browser automation (WebDriver)
- stryke-spark — Spark Connect (no JVM)
- stryke-utils — pure-stryke utility belt
- stryke-zmq — ZeroMQ messaging