quds_office_engine

pub package pub points likes MIT license platforms Dart 3

A pure Dart Office and PDF engine. Open, build, and write .docx, .xlsx, and .pptx. Open real PDF files. Compose pages with a Flutter-like widget layout. Pick from 100+ direction-aware report templates with distinct skins. Export Office models to native PDF 1.7. Assemble, rotate, stamp, and crop PDF pages without a native library.

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 live in quds_office_editor.

Invoice composed with pdf_widgets RTL formal letter exported to PDF


Why this package exists

Most Dart “Office” libraries stop at a thin ZIP writer, or they shell out to a native binary. This engine owns the stack a real suite needs:

You need What the engine does
Generate reports on a server Fluent builders and 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)
Ship domain reports fast pdf_templates — 100+ skins, not text-only clones
Print and archive from Office Native PDF 1.7 with subsetted TrueType fonts
Rearrange an existing PDF Graft pages so fonts and images survive
Large files in a UI Isolate open and 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 conversion API.


Two packages, one suite

quds_office_engine          pure Dart, no dart:ui
├── OPC / ZIP / OLE         docx · xlsx · pptx
├── Word / Sheet / Slide    models, layout, formulas, motion
├── PdfFile                 open, display list, extract, annotate
├── PdfToolbox              merge, split, rotate, stamp, crop
├── pdf_widgets             constraint layout → PdfDocument
├── pdf_templates           100+ report skins on top of widgets
└── OfficePdfExport         Word / Excel / PowerPoint → PDF 1.7
         │
         ▼
quds_office_editor          Flutter RenderBox only
├── QudsWordEditor
├── QudsSheetEditor
├── QudsSlideEditor
└── QudsPdfViewer / QudsPdfEditor
Package Runtime Role
quds_office_engine Dart VM, web, Flutter Models, formulas, PDF file, widgets, export
quds_office_editor Flutter Custom RenderBox Word, Excel, PowerPoint, and 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.

Three entry points. Do not mix them carelessly:

Import Use it for
package:quds_office_engine/quds_office_engine.dart Office models, builders, formulas, PdfFile, export
package:quds_office_engine/pdf_widgets.dart Flutter-like PDF layout (Document, Text, Table)
package:quds_office_engine/pdf_templates.dart Classified report templates on top of the widgets
package:quds_office_engine/quds_office_engine_optional.dart Optional PowerPoint media hydrate

pdf_widgets is a separate library on purpose. Its Text, TextStyle, and Widget names must not collide with Flutter. Import it with a prefix.

pdf_templates is a third library. It does not re-export the widgets. Import it as tpl in a Flutter file so TextDirection does not collide.

PdfDocument is the writer. PdfFile is an opened ISO 32000 file. They are parallel types. The writer is not renamed, and the file model does not replace export.


Languages and writing directions

Office documents are not English-only, and this engine is not either.

  • 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 BiDi in the same paragraph.
  • UAX #9 — embedding levels and visual reordering.
  • Arabic shaping — joining forms, Lam-Alef ligatures, and a nominal map from presentation forms back to isolated letters (ArabicShaper.nominal).
  • Grapheme-aware line breaking — caret-safe clusters, not naive code-unit splits.
  • Sheet direction — a worksheet can be right-to-left (column A on the reading-start side) or left-to-right.
  • Theme defaultOfficeDocumentTheme.custom(rtl: true) seeds builders. Individual paragraphs and runs can still override.

You do not need a separate “Arabic build”. Pass the strings you have and set direction where the document requires it.

final theme = OfficeDocumentTheme.custom(
  palette: const OfficePalette(
    primary: OfficeColors.wordBlue,
    accent: OfficeColors.antiqueGold,
  ),
  rtl: true,
  page: OfficePageSize.a4Portrait,
);

For PDF, embed a covering TrueType glyf face (or an OfficeFontSet with fallbacks). Without one, Latin may fall back to Helvetica and other scripts will not appear. A rotated watermark is one shaped string, not a letter split along the diagonal — Arabic cannot be cut glyph by glyph and still join.


Install

dependencies:
  quds_office_engine: ^0.5.0
dart pub add quds_office_engine

SDK: Dart 3.12+. Works in Flutter apps, CLI tools, isolates, and on the web (pass Uint8List yourself; nothing in the public path requires dart:io).

import 'package:quds_office_engine/quds_office_engine.dart';
import 'package:quds_office_engine/pdf_widgets.dart' as pw;

Capability map

Area What you can do
Word Build, open, mutate, and write DOCX. Sections, headers, footers, tables, lists, styles, comments, TOC, hyperlinks, fields, footnotes, endnotes, OMML, BiDi, mail merge, captions, citations, revisions
Excel Build, open, and write XLSX. Shared strings, styles, merges, freeze, RTL, charts, pictures, named ranges, sparklines, a formula engine with a dependency graph
PowerPoint Build, open, and write PPTX. Shapes, tables, notes, masters (subset), transitions and animations including Morph, reverse playback clock
Office → PDF Replay Word layout, Excel grid, and slides (or notes pages, including two slides on one page) to PDF 1.7 with subsetted fonts, links, and outlines
pdf_widgets Constraint layout: flex, text, tables, charts, images, TOC, headers, footers, watermarks. Synchronous Document.save()
pdf_templates 100+ classified reports with distinct SheetSkin layouts, LTR/RTL, and brand themes
PdfFile Open PDF 1.7, paint a display list, extract text, annotate, fill AcroForm, incremental save
PdfToolbox Merge, extract, split, reorder, remove, rotate, reverse, insert, mix, stamp, number, Bates, crop — a new file with resources grafted
Platform Own ZIP, Deflate, CFBF/OLE, XML, AES, repair, and isolate open/save

What this package will not pretend to do is listed under Honest limits.


Word

Two layers, one WmlDocument model.

  1. Fluent builderDocxDocumentBuilder for reports and mail-merge style generation.
  2. Typed modelWmlDocument / WmlSection / WmlParagraph / WmlRun for 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: OfficeColors.wordBlue,
    accent: OfficeColors.antiqueGold,
  ),
    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 model covers runs, paragraphs, tables, sections (page size, margins, columns, headers and footers), comments, footnotes and endnotes, hyperlinks, bookmarks, fields, lists, styles, captions, citations, revision marks, mail merge, TOC, drawings, and OMML equations.

Fields that round-trip include PAGE, NUMPAGES, DATE, TIME, REF, SEQ, TOC, CITATION, INDEX, HYPERLINK, FILENAME, FILESIZE, AUTHOR, TITLE, and MERGEFIELD. This is a deliberate subset, not the entire Word field zoo.

WordLayout paginates the model into print-faithful pages. PDF export and the Flutter Word canvas consume the same laid-out pages. A section can be portrait or landscape independently of its neighbors.


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: OfficeColors.wordBlue,
  color: OfficeColors.white,
);
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, LEFT, RIGHT, MID, LEN, TRIM, UPPER, LOWER, SUBSTITUTE
Stat COUNT, COUNTA, COUNTIF, SUMIF, AVERAGEIF, STDEV, VAR, MEDIAN
Date DATE, TODAY, NOW, and related serials

Cross-sheet references and a dependency graph are part of the evaluator. Unknown names return #NAME? rather than a silent wrong answer. A few names are documented aliases; see FormulaGuide. This is a serious subset, not a claim of full Excel compatibility.

Also on SmlWorkbook: styles, shared strings, freeze panes, RTL sheets, charts, pictures, named ranges, tables, sparklines, print area and titles, header and footer models, a simple SUM pivot (not Power Pivot), and a CSV import helper (not Power Query).

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.

Shape presets and stroke are a subset of DrawingML. SmartArt is not implemented. Audio and video are not on the default export; an optional hydrate helper lives in quds_office_engine_optional.dart.


PDF 1.7 — three ways

The writer is native. It does not wrap another PDF library.

1. Office → PDF

final pdf = OfficePdfExport.fromBytes(docx, title: 'Report');

OfficePdfExport.word(document, font: font, title: 'Word');
OfficePdfExport.workbook(workbook, font: font, title: 'Excel');
OfficePdfExport.presentation(
  presentation,
  font: font,
  title: 'Slides',
  mode: PdfSlideExportMode.notesPages,
  slidesPerPage: 2, // two slides stacked on one A4 page; default is 1
);
Source What is drawn
Word Laid-out pages, tables, headers and footers, visuals, TOC links, /Outlines
Excel Evaluated grid: column widths, row heights, merges, print area, print titles, header and footer
PowerPoint One page per slide (masters included), or notes pages, with bookmarks and shape URI annotations

Embedded subsetting requires a TrueType face with a glyf table (Calibri, Arial, DejaVu, Liberation, Noto, …). CFF/OTF-only faces are not subset. Pass an OfficeFontSet when one face cannot cover every script.

OfficePrint applies a page range, copy count, and optional landscape flag on the same export path.

Direct reports that never touch OOXML:

final pdf = (PdfReportBuilder(
      theme: theme,
      header: 'Quds',
      footer: 'Page',
    )
      ..titleText('Quarterly')
      ..body('Narrative…')
      ..kpiRow([(label: 'NPS', value: '72')])
      ..table([
        ['A', 'B'],
        ['1', '2'],
      ]))
    .build();

Optional catalog extras (PdfSaveOptions.pdfA) write XMP, an OutputIntent, and MarkInfo oriented toward PDF/A-2b. Tagged structure can be emitted from outlines. That is not a certified PDF/A or PDF/UA file. Readers can detect the XMP profile; a validator may still fail, especially after incremental markup.

2. Widget composer (pdf_widgets.dart)

Constraint layout on a PdfCanvas. Flutter-shaped Widget.layout and 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,
  fontBold: bold, // Type0 /F3 — real bold, not a faux stroke
);
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),
        ],
      ),
      pw.Watermark.text('ATLAS'),
    ],
  ),
);
final Uint8List pdf = doc.save();

Implemented boxes include Row, Column, Expanded, Wrap, Stack, Positioned, GridView, Table (cells fill the row), Image, Chart, UrlLink, Header, Footer, Paragraph, a two-pass TableOfContent, and Watermark.

A watermark is one rotated string. The content stream carries visual-order glyphs under a rotation matrix, plus ActualText, so Arabic stays joined. Splitting the word into upright letters is the wrong model for a connected script.

Word does not have this DSL. OOXML is flow, not boxes. Generate DOCX with DocxDocumentBuilder. Compose PDF pages with pdf_widgets.

Report templates (pdf_templates.dart)

100+ ready reports that look different from each other. Labels are the language. TextDirection is the layout. TemplateTheme.copyWith is the brand. SheetSkin is the page composition — invoice, receipt, letter, certificate, payslip split, checklist, route, voucher, grades, identity, notice, journal, comparison, and more. Amounts stay formatted strings.

Category Templates
Commerce Invoice, quote, receipt, delivery, purchase order, credit note, packing list, and more
Finance Statement, expense, payslip, profit and loss, aging, vouchers, journal
People Letter, certificate, offer, appointment, attendance, profile
Operations Agenda, minutes, checklist, inspection, roster, incident, permit
Narrative Memo, briefing, proposal, policy, decision
Data KPI sheet, listing, scorecard, timesheet, risk register
Education Report card, transcript, syllabus, fee receipt
Property Rent receipt, lease summary, tenant statement
Logistics Bill of lading, pick list, waybill, manifest
Programs Grant report, distribution, donation receipt

TemplateCatalog.all is the full index. Studio gallery samples are two pages: English, then Arabic, in one PDF via joinTemplateFiles.

Named classes stay open for customization — grow with SheetTemplate, SheetSkin, and TemplateDocument.save instead of forking a closed file.

import 'package:quds_office_engine/pdf_templates.dart' as tpl;

final bytes = tpl.InvoiceTemplate(
  direction: tpl.TextDirection.rtl,
  theme: tpl.TemplateThemes.commerce.copyWith(accent: '0F766E'),
  labels: const tpl.TradeLabels(document: 'فاتورة', from: 'من', to: 'إلى'),
  seller: const tpl.TemplateParty(name: 'الورشة'),
  buyer: const tpl.TemplateParty(name: 'الحيّ'),
  totals: const tpl.MoneyTotals(subtotal: '100', total: '100'),
).save();

Swap composition without rewriting the document:

tpl.SheetTemplate(
  kind: tpl.SheetKind.trade,
  skin: tpl.SheetSkin.receipt, // or invoice, voucher, route, …
  direction: tpl.TextDirection.ltr,
  theme: tpl.TemplateThemes.commerce,
  owner: const tpl.TemplateParty(name: 'North Dock'),
  labels: const tpl.SheetLabels(document: 'Receipt'),
).save();

3. Open a PDF (PdfFile)

import 'package:quds_office_engine/pdf_file.dart';

final PdfFile file = PdfFile.open(bytes, password: password);
print(file.pageCount);
print(PdfExtract.pageText(file, 0));
print(PdfExtract.documentText(file));

final PdfDisplayList list = file.displayList(0);
for (final PdfTextRun run in list.runs) {
  print('${run.text} @ ${run.x},${run.y}');
}

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,
);

Open covers xref tables and streams, object streams, and incremental updates. Standard security revisions 2–4 open with a password; revisions 5 and 6 are attempted. The display list includes paths through the CTM, fill and stroke (including b / b*), clips, stroke style, DeviceRGB / Gray / CMYK (no ICC apply — /ICCBased uses /N only), ExtGState opacity and a small blend set, JPEG / Flate / LZW / CCITT images, image masks, indexed palettes, form XObjects, inline images, Type 3 charprocs, axial and radial shadings, optional content, and text with ToUnicode, WinAnsi, MacRoman, PDFDoc, and ActualText.

Annotations and appearance streams, AcroForm fill, XFDF, viewer preferences, and page insert / delete / rotate live on PdfFile. /XFA is flagged, not extracted. /StructTreeRoot is exposed; PdfExtract.readingOrder walks Alt and ActualText. Signature fields report ByteRange coverage (unverified / broken / unsupported). PKCS#7 and CMS bytes are not verified, and this package does not create signatures.

Isolate open: OfficeIsolateOpen.pdf. Print: OfficePrint.pdfFile.


PDF toolbox

PdfToolbox writes a new PDF 1.7 and grafts each page dictionary, its content streams, and inherited Resources. Fonts and images survive. Do not use incremental appendPages / mergeFrom for real assembly — those snapshots can drop Resources.

import 'package:quds_office_engine/pdf_file.dart';

// Concatenate, taking several range groups from one file.
final merged = PdfToolbox.merge(<PdfPageSource>[
  PdfPageSource(a, ranges: <String>['1-3', '8-10', '15']),
  PdfPageSource(b, ranges: <String>['2-4']),
]);

// One file, or one file per range group.
final one = PdfToolbox.extract(<PdfPageSource>[
  PdfPageSource(a, ranges: <String>['1-3', '8-']),
], mode: PdfExtractMode.oneFile);

final parts = PdfToolbox.extract(<PdfPageSource>[
  PdfPageSource(a, ranges: <String>['1-3', '8-10']),
], mode: PdfExtractMode.perRange);

final burst = PdfToolbox.split(bytes, const PdfSplitSpec.burst());
final chunks = PdfToolbox.split(bytes, const PdfSplitSpec.every(2));
final odds = PdfToolbox.split(bytes, const PdfSplitSpec.odd());
final byOutline = PdfToolbox.split(bytes, const PdfSplitSpec.bookmarks());

final turned = PdfToolbox.rotate(bytes, degrees: 90, ranges: <String>['1-3']);
final numbered = PdfToolbox.numberPages(bytes, pattern: '{n} / {N}');
final stamped = PdfToolbox.stamp(bytes, text: 'DRAFT', opacity: 0.18);
final cropped = PdfToolbox.crop(
  bytes,
  left: 12,
  bottom: 12,
  right: 12,
  top: 12,
);

Range language is 1-based: 1-3, 5, 8-. A page may repeat if the range repeats it. PdfExtractMode.oneFile concatenates groups. perRange yields one file per group, so one source can contribute several outputs.

/Rotate is a clockwise quarter-turn (0 / 90 / 180 / 270), added to the existing value and normalized. It does not swap MediaBox or reflow the page. PdfPageView.toView / fromView map between unrotated crop space and the view box (the view size swaps at 90° and 270°; that is the view box, not an orientation conversion).

Stamp, page numbers, and Bates are a Helvetica overlay with /ca opacity. They are Latin literals. Arabic in stamp throws — use pdf_widgets Watermark when the word must shape and rotate as one string. Crop insets the CropBox only. It does not rewrite the content stream.

Also: reorder, remove, reverse, insertBlank, insertFrom, mix, and resolveRange.


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, presentations, and PDF. 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'),
);

final page = PdfExtract.pageText(PdfFile.open(pdf), 0);
final found = OfficeFind.inPdf(
  PdfFile.open(pdf),
  const OfficeFindOptions(query: page.substring(0, 4)),
);

Text extraction covers DOCX, XLSX, PPTX, OpenDocument, and PDF page text (display-list runs, with redaction rectangles skipped). PDF find returns hits with a page index. Replacing inside a PDF content stream is not supported — Office replace is for Word, sheets, and slides.

Core and app document properties (OfficeDocumentProperties) round-trip with the package. 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: OfficeColors.wordBlue,
    accent: OfficeColors.excelGreen,
    muted: OfficeColors.gray,
    highlight: OfficeColors.antiqueGold,
  ),
  rtl: true,
  page: OfficePageSize.a4Portrait,
);

Honest limits

These are not hidden TODOs. They are the contract.

Out of scope What you get instead
Legacy .doc / .xls / .ppt, VBA, ActiveX OOXML only
Full Excel LAMBDA, Power Pivot, Power Query Formula subset, simple SUM pivot, CSV helper
PDF body reflow (edit text like Word) Annotations, form fill, page assembly
OCR, image recompress, PDF-to-Office layout recovery Text extract from the content stream only
Creating signatures, verifying CMS / PAdES ByteRange coverage status
Certified PDF/A or PDF/UA Optional XMP extras; detection, not certification
In-engine CFF / Type 1 raster, JBIG2, JPEG2000 OTTO wrap for a host loader; those filters stay unsupported
True content-stream redaction Overlay plus extract filter
Arabic PdfToolbox.stamp Helvetica overlay; use pw.Watermark for a shaped string
N-up booklet, grayscale, image extract Not in PdfToolbox

Package layout

lib/
├── quds_office_engine.dart              default export (no Flutter)
├── quds_office_engine_optional.dart     optional extras
├── pdf_file.dart                        PdfFile, PdfToolbox, extract
├── pdf_widgets.dart                     constraint layout (separate library)
├── pdf_templates.dart                   classified report templates
└── src/
    ├── opc/        ZIP, relationships, OLE, crypto, repair, isolates
    ├── word/       WML model, layout, OMML, serialize
    ├── sheet/      SML model, styles, formula AST + functions
    ├── slide/      PML model, DrawingML, animations, Morph
    ├── pdf/        writer, widgets, and file/ open-annotate-graft
    ├── bidi/       UAX #9, shaping, line breaker, direction
    ├── fonts/      SFNT parse, metrics, subset, 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/pdf_widgets_invoice.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 Yes PdfFile open; native 1.7 write and toolbox graft
Password-protected OOXML Yes Yes When a password is supplied
Password-protected PDF Yes Incremental Standard security Rev 2–4; Rev 5/6 attempted

Files are intended to open in Microsoft Office, LibreOffice, and OnlyOffice. Fidelity is a library contract, not a claim against any single vendor product.


Design rules

  • 100% type-safe — no dynamic, no untyped maps in the public API.
  • Uint8List in, Uint8List out — you own storage and networking.
  • No Flutter — embeddable in servers and isolates.
  • One layout, many surfaces — pagination is computed here; PDF and the Flutter editor replay it.
  • Writer and file stay separatePdfDocument writes. PdfFile opens.

Interactive editing lives in quds_office_editor.


License

MIT. See LICENSE.

Libraries

pdf_file
PDF-as-a-file API (ISO 32000). Parallel to the OOXML writer.
pdf_templates
Ready report templates on top of pdf_widgets.
pdf_widgets
Flutter-like PDF widgets. Pure Dart — no dart:ui.
quds_office_engine
Pure Dart Office Open XML engine.
quds_office_engine_optional
Optional APIs that are not part of the default engine surface.