quds_office_engine 0.3.1
quds_office_engine: ^0.3.1 copied to clipboard
Pure Dart Office + PDF engine: DOCX, XLSX, PPTX, formulas, BiDi/RTL, PdfFile open/display, pdf_widgets composition, and PDF 1.7 export — no Flutter.
quds_office_engine #
A pure Dart Office + PDF engine. Open, build, and mutate .docx, .xlsx,
and .pptx — and open real PDF files (PdfFile), compose pages with
Flutter-like pdf_widgets, or export Office models to native PDF 1.7.
No Flutter. No dart:ui. No Microsoft Office, LibreOffice, or cloud conversion
step. You pass Uint8List in and you get Uint8List out.
This is the model, layout, and IO half of
Quds Office Suite.
Interactive canvases (including PDF view/edit) live in
quds_office_editor.
Why this package exists #
Most Dart “Office” libraries stop at a thin ZIP + XML writer, or they shell out to a native binary. This engine owns the stack that a real suite needs:
| You need | What the engine does |
|---|---|
| Generate reports on a server | Fluent builders + pdf_widgets return bytes |
| Open files people actually send | OPC packages and ISO 32000 PdfFile |
| Spreadsheets that calculate | Formula AST, Excel-style functions, dependency graph |
| Text that reads correctly worldwide | Unicode, LTR / RTL / mixed BiDi, Arabic shaping |
| Compose polished PDFs | Constraint layout (Document / Table / MultiPage) |
| Print and archive from Office | Native PDF 1.7 export with subsetted fonts |
| Large files in a UI | Isolate open / save so the UI isolate stays responsive |
| Damaged or encrypted packages | Repair heuristics and password-aware encryption |
The bytes are yours. There is no hidden “call a conversion API” step.
Two packages, one suite #
flowchart LR
subgraph engine [quds_office_engine — pure Dart]
OPC[OPC / ZIP / OLE]
WML[Word model + layout]
SML[Sheet model + formulas]
PML[Slide model + motion]
PDFf[PdfFile open / display list]
PDFw[pdf_widgets compose]
PDFx[OfficePdfExport]
OPC --> WML & SML & PML
WML & SML & PML --> PDFx
PDFw --> PDFx
end
subgraph editor [quds_office_editor — Flutter]
Word[QudsWordEditor]
Sheet[QudsSheetEditor]
Slide[QudsSlideEditor]
Pdf[QudsPdfViewer / Editor]
end
engine --> editor
PDFf --> Pdf
| Package | Runtime | Role |
|---|---|---|
quds_office_engine |
Dart VM, web, Flutter | Office models, formulas, PdfFile, pdf_widgets, export |
quds_office_editor |
Flutter | Custom RenderBox Word / Excel / PowerPoint / PDF surfaces |
Use the engine alone for CLI tools, isolates, backends, and codegen. Add the editor when a human needs to type, select, present, or annotate a PDF.
Languages and writing directions #
Office documents are not English-only, and this engine is not either.
The text pipeline is built for many languages and many directions in the same file:
- Unicode throughout — Latin, Arabic, Hebrew, CJK, and mixed scripts in one run, paragraph, cell, or shape.
- Paragraph and run direction — left-to-right, right-to-left, and mixed-direction (BiDi) text.
- UAX #9 BiDi — embedding levels and visual reordering for mixed LTR/RTL.
- Arabic shaping — contextual forms and joining so connected scripts paint as words, not isolated letters.
- Grapheme-aware line breaking — caret-safe clusters, not naive
codeUnitsplits. - Sheet direction — worksheets can be right-to-left (column A on the reading-start side) or left-to-right.
- Theme-level default —
OfficeDocumentTheme.custom(rtl: true)seeds builders; individual paragraphs and runs can still override.
You do not need a separate “Arabic build” or “CJK build”. Pass the strings you have; set direction where the document requires it.
final theme = OfficeDocumentTheme.custom(
palette: const OfficePalette(primary: '2B579A', accent: 'C9A227'),
rtl: true, // default writing direction for generated content
page: OfficePageSize.a4Portrait,
);
For PDF, embed a covering TrueType face (or an OfficeFontSet with fallbacks)
so glyphs from every script you emit are subset into the file. Without a
covering glyf font, Latin may fall back to Helvetica and other scripts will
not appear.
Install #
dependencies:
quds_office_engine: ^0.3.1
dart pub add quds_office_engine
SDK: Dart 3.12+. Works in Flutter apps, CLI tools, isolates, and web
(where dart:io is not required — pass Uint8List yourself).
Optional Flutter-like PDF composer (pure Dart, no dart:ui):
import 'package:quds_office_engine/pdf_widgets.dart' as pw;
Word generation stays on DocxDocumentBuilder + WmlDocument. Constraint
layout belongs on PDF pages, not in OOXML flow.
Word #
Two layers, same WmlDocument model:
- Fluent builder —
DocxDocumentBuilderfor reports and mail-merge-like generation. - Typed model —
WmlDocument/WmlSection/WmlParagraph/WmlRunfor load, mutate, and round-trip serialize.
Build a .docx #
import 'dart:io';
import 'package:quds_office_engine/quds_office_engine.dart';
void main() {
final theme = OfficeDocumentTheme.custom(
palette: const OfficePalette(primary: '2B579A', accent: 'C9A227'),
rtl: true,
);
final bytes = (DocxDocumentBuilder(theme: theme)
..header(left: 'Quds Office', right: 'Confidential')
..footer(left: 'Engine', right: 'Page')
..heading('Quarterly report')
..paragraph(
'Executive summary in any Unicode script, including mixed direction.',
)
..note('Generated without Microsoft Office.')
..bulletList(['Word', 'Excel', 'PowerPoint'])
..table([
['KPI', 'Q1', 'Q2'],
['Documents', '120', '148'],
['Slides', '36', '41'],
])
..hyperlink(
'Documentation',
'https://pub.flutter-io.cn/packages/quds_office_engine',
)
..pageBreak()
..heading('Appendix', level: 2)
..barChart(
title: 'Volume',
series: const [
ChartPoint(label: 'Q1', value: 120),
ChartPoint(label: 'Q2', value: 148),
],
))
.build();
File('report.docx').writeAsBytesSync(bytes);
}
Open and inspect #
final document = WordDeserializer().readBytes(bytes);
final extracted = OfficeTextExtractor.extract(bytes, name: 'report.docx');
print(extracted.plainString);
final plain = DocxPlainReader.read(bytes);
print(plain.paragraphs);
print(plain.tables);
print(plain.hyperlinks);
The WmlDocument model covers runs, paragraphs, tables, sections (page
size, margins, columns, headers/footers), comments, footnotes/endnotes,
hyperlinks, bookmarks, fields, lists, styles, captions, citations, revision
marks, mail merge, TOC, drawings/frames, and OMML equations.
Layout (WordLayout) paginates that model into print-faithful pages. PDF
export and the Flutter Word canvas both consume the same laid-out pages.
Excel #
XlsxWorkbookBuilder is a styled writer. SmlWorkbook is the calculating
model.
Write a workbook #
final book = XlsxWorkbookBuilder(theme: theme);
final header = book.style(bold: true, fillRgb: '2B579A', color: 'FFFFFFFF');
final sheet = book.addSheet('Budget')
..rightToLeft = true
..freezeRows = 1
..addRow(['Item', 'Qty', 'Price'], style: header)
..addRow(['Paper', 40, 1.25])
..addRow(['Ink', 12, 8.5]);
sheet.columnWidths.add((min: 1, max: 3, width: 18));
File('budget.xlsx').writeAsBytesSync(book.build());
Calculate formulas #
final sheet = SmlWorksheet(name: 'Calc', sheetId: 1);
sheet.cellA1('A1').value = 10;
sheet.cellA1('A2').value = 20;
final total = sheet.cellA1('A3')..formula = '=SUM(A1:A2)';
final workbook = SmlWorkbook(sheets: [sheet]);
FormulaDepGraph(workbook).recalculate();
print(total.asNumber); // 30
final xlsx = SheetSerializer().writeBytes(workbook);
Supported families include:
| Family | Examples |
|---|---|
| Math | SUM, AVERAGE, MIN, MAX, ROUND*, PRODUCT, POWER, SQRT, MOD, GCD, LCM, SUMPRODUCT |
| Logic | IF, IFS, IFERROR, IFNA, AND, OR, XOR, NOT, SWITCH |
| Lookup | VLOOKUP, INDEX, MATCH |
| Text | CONCAT / CONCATENATE, LEFT, RIGHT, MID, LEN, TRIM, UPPER, LOWER, SUBSTITUTE |
| Stat | COUNT, COUNTA, COUNTIF(S), SUMIF(S), AVERAGEIF, STDEV, VAR, MEDIAN |
| Date | DATE, TODAY, NOW, and related serials |
Cross-sheet references and a dependency graph are part of the evaluator. This is a serious subset, not a claim of full Excel compatibility.
Beyond the grid: styles, shared strings, freeze panes, RTL sheets, charts,
sparklines, and higher-level helpers (analysis, Power Query-shaped transforms,
solver) sit on the same SmlWorkbook model.
final names = XlsxGridReader.listSheets(xlsx);
final rows = XlsxGridReader.readSheet(xlsx, sheetIndex: 0);
PowerPoint #
final deck = (PptxDeckBuilder(theme: theme, showSlideNumber: true)
..addCoverSlide(
title: 'Quds Office Engine',
subtitle: 'OPC · Formulas · PDF',
)
..addKpiSlide(
title: 'This quarter',
cards: [
(label: 'Documents', value: '148'),
(label: 'Decks', value: '41'),
],
)
..addTitleBodySlide(
title: 'Agenda',
bullets: ['Builders', 'Models', 'Export'],
)
..addPieChartSlide(
title: 'Mix',
series: const [
ChartPoint(label: 'Word', value: 50),
ChartPoint(label: 'Excel', value: 30),
ChartPoint(label: 'Slides', value: 20),
],
)
..addClosingSlide(title: 'Thank you', subtitle: 'quds_office_engine'))
.build();
File('deck.pptx').writeAsBytesSync(deck);
PmlPresentation stores shapes, tables, notes, hidden slides, animations,
and transitions (including Morph). PmlSlideShow is a click-driven clock
the editor uses for a real slideshow:
final show = PmlSlideShow(presentation)..start(from: 0);
show.next(); // fire the next click, or transition forward
show.elapse(16); // advance the clock
show.previous(); // reverse the last animation or the last transition
print(show.transitionProgress);
print(show.sample(shapeId).opacity);
previous() is not a jump. Entrances fly back out along the same path;
transitions replay as if the show were a video in reverse.
PDF 1.7 #
The PDF writer is native. It does not wrap another PDF library.
// Any OOXML package:
final pdf = OfficePdfExport.fromBytes(docx, title: 'Report');
// Or from models:
OfficePdfExport.word(document, font: font, title: 'Word');
OfficePdfExport.workbook(workbook, font: font, title: 'Excel');
OfficePdfExport.presentation(
presentation,
font: font,
title: 'Slides',
mode: PdfSlideExportMode.notesPages,
);
| Source | What is drawn |
|---|---|
| Word | Laid-out pages, tables, headers/footers, visuals, TOC links, /Outlines |
| Excel | Evaluated grid: column widths, row heights, merges, optional printArea |
| PowerPoint | One page per slide (masters included), or notes pages, with bookmarks |
Fonts: embedded subsetting requires a TrueType face with a glyf table
(Calibri, Arial, DejaVu, Liberation, Noto, …). CFF/OTF-only faces are not
subset today. Pass an OfficeFontSet when one face cannot cover every script
in the file.
Direct PDF reports (no OOXML in the middle):
final pdf = (PdfReportBuilder(
theme: theme,
header: 'Quds',
footer: 'Page',
)
..titleText('Quarterly')
..body('Narrative…')
..kpiRow([(label: 'NPS', value: '72')])
..table([
['A', 'B'],
['1', '2'],
]))
.build();
Widget-style PDF (pdf_widgets.dart) #
Constraint layout on a PdfCanvas — Flutter-shaped Widget.layout /
PwBox.paint, no dart:ui. Document.save() is synchronous and writes a
native PdfDocument. Subclass Widget for custom boxes.
import 'package:quds_office_engine/pdf_widgets.dart' as pw;
final doc = pw.Document(title: 'Invoice', font: font);
doc.addPage(
pw.MultiPage(
pageFormat: pw.PdfPageFormat.a4,
header: (c) => pw.Text('Quds Office'),
footer: (c) => pw.Footer(),
build: (c) => <pw.Widget>[
pw.Header(level: 1, text: 'Invoice'),
pw.Row(children: <pw.Widget>[
pw.Expanded(child: pw.Paragraph(text: 'Bill to…')),
pw.Expanded(child: pw.Paragraph(text: 'Due Net 14')),
]),
pw.Table.fromTextArray(
headers: ['Item', 'Amount'],
data: [
['License', '2,400'],
],
),
pw.Chart(
type: pw.ChartType.bar,
points: const [
pw.ChartPoint(label: 'Q1', value: 12),
pw.ChartPoint(label: 'Q2', value: 18),
],
),
],
),
);
final Uint8List pdf = doc.save();
Row / Column / Expanded, Wrap, Stack / Positioned, GridView,
Table, Image, Chart, UrlLink, Watermark, and a two-pass
TableOfContent are implemented. Word does not have this DSL — OOXML is
flow, not boxes.
OfficePrint applies a page range, copy count, and optional landscape flag on
top of the same export path.
Open PDF #
PdfDocument is the writer. PdfFile is an opened ISO 32000 file
(parallel stack — it does not replace export).
import 'package:quds_office_engine/pdf_file.dart';
final PdfFile file = PdfFile.open(bytes, password: password);
final PdfDisplayList list = file.displayList(0);
final hits = OfficeFind.inPdf(file, const OfficeFindOptions(query: 'Hello'));
file.addAnnot(0, PdfAnnot(id: 0, subtype: 'Highlight', rect: rect));
final Uint8List saved = PdfIncrementalSave.write(
originalBytes: bytes,
file: file,
);
Isolate open: OfficeIsolateOpen.pdf. Print: OfficePrint.pdfFile.
Page merge / extract / rotate live on PdfFile. XFDF: PdfXfdf.
file.structTree is the tagged tree when present; PdfExtract.readingOrder
walks Alt/ActualText. file.signatures reports ByteRange coverage only.
file.viewerPrefs and file.form.hasXfa are read flags. Raw CFF /FontFile3
is wrapped as OTTO for a host FontLoader — the engine does not rasterize
CFF. PDF/A is detected, never certified. JBIG2, JPX, CMS verify, and
in-engine CFF raster stay unsupported. Content-stream reflow editing is out
of scope.
Isolates, find, stats, properties #
final payload = await OfficeIsolateOpen.word(
bytes,
onProgress: (p) => print('${p.stage} ${(p.value * 100).round()}%'),
);
final document = payload.document;
final laidOut = payload.laidOut;
The same isolate pattern exists for workbooks and presentations. Saves can run
on a worker isolate (OfficeIsolateSave).
final extracted = OfficeTextExtractor.extract(bytes, name: 'file.docx');
print(extracted.kind); // word / sheet / slide / ODF / …
print(extracted.plainString);
final stats = OfficeTextStats.ofWord(document, laidOut: laidOut);
print('${stats.words} words · ${stats.pages} pages');
OfficeFind.replaceWord(
document,
const OfficeFindOptions(query: 'Q1', replaceWith: 'Q2'),
);
DOCX, XLSX, PPTX, and OpenDocument text/sheet/presentation extraction are supported. PDF text extraction is not in this version — export to PDF, or extract from OOXML.
Core/app document properties (OfficeDocumentProperties) round-trip with the
package. A lightweight spell helper (OfficeSpell) flags issues when a host
supplies a dictionary; the engine does not ship a full language corpus.
Theming #
Branding is injected through OfficeDocumentTheme. The engine does not
hardcode a product name into documents.
final theme = OfficeDocumentTheme.custom(
palette: const OfficePalette(
primary: '2B579A',
accent: '217346',
muted: '5B5B5B',
highlight: 'C9A227',
),
rtl: true,
page: OfficePageSize.a4Portrait,
);
Package layout #
lib/
├── quds_office_engine.dart default export (no Flutter)
├── quds_office_engine_optional.dart optional extras
├── pdf_widgets.dart Flutter-like PDF layout (Document / MultiPage)
└── src/
├── opc/ ZIP, relationships, content types, OLE, crypto, repair, isolates
├── xml/ Streaming reader / writer, Office namespaces
├── word/ WML model, layout, OMML math, serialize
├── sheet/ SML model, styles, formula AST + functions
├── slide/ PML model, DrawingML, animations, Morph, serialize
├── pdf/ PDF 1.7 writer + `file/` open/annotate model
├── bidi/ UAX #9, shaping, line breaker, office direction
├── fonts/ SFNT parse, metrics, subset, outlines, font set
├── builders/ Fluent DOCX / XLSX / PPTX writers
├── office/ find, print, stats, spell, properties
└── extract/ Plain-text extraction
Examples #
| Script | Writes |
|---|---|
example/engine_quickstart.dart |
One DOCX + PDF |
example/word_report_sample.dart |
Word report via DocxDocumentBuilder |
example/pdf_widgets_invoice.dart |
Invoice (Row, Table, UrlLink) |
example/pdf_widgets_report.dart |
Quarterly report (TOC, charts, landscape) |
example/pdf_widgets_proposal.dart |
Proposal (cover band, GridView) |
example/rich_export_gallery.dart |
Word, Excel, PowerPoint galleries + PDF |
example/word_gallery.dart |
Word-only |
example/sheet_gallery.dart |
Excel-only |
example/slide_gallery.dart |
PowerPoint-only |
cd packages/quds_office_engine
dart run example/engine_quickstart.dart
dart run example/word_report_sample.dart
dart run example/pdf_widgets_invoice.dart
dart run example/pdf_widgets_report.dart
dart run example/pdf_widgets_proposal.dart
dart run example/rich_export_gallery.dart
Compatibility #
| Format | Read | Write | Notes |
|---|---|---|---|
.docx |
Yes | Yes | Sections, tables, drawings, comments, notes, TOC, OMML |
.xlsx |
Yes | Yes | Styles, formulas, freeze, RTL, charts |
.pptx |
Yes | Yes | Shapes, notes, hidden slides, motion |
.odt / .ods / .odp |
Extract | — | Text extraction |
.pdf |
— | Yes | Native 1.7 compile |
| Password-protected OOXML | Yes | Yes | When a password is supplied |
Files are intended to open in Microsoft Office, LibreOffice, and OnlyOffice.
Exotic VBA / ActiveX / legacy binary (.doc / .xls / .ppt) is out of scope.
Design rules #
- 100% type-safe — no
dynamic, no untyped maps in the public API. Uint8Listin,Uint8Listout — you own storage, networking, and encryption at rest.- No Flutter — this package must stay embeddable in
dart:ioservers and isolates. - One layout, many surfaces — pagination is computed here; PDF and the Flutter editor replay it.
Interactive editing lives in
quds_office_editor.
License #
MIT. See LICENSE.
