Search by

burhan15 / office-pack

burhan15

Universal Office Document SDK (PHP) — first-party engines

Package info

github.com/burhan15/office-pack

Language:JavaScript

pkg:composer/burhan15/office-pack

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-10-10 14:36 UTC

This package is auto-updated.

Last update: 2026-11-11 09:05:45 UTC


README

A first-party Office library for PHP and Node.js. Install it into your app and create spreadsheets, Word documents, PowerPoint decks, and PDFs with one API.

The files are standard OOXML (.xlsx, .docx, .pptx) and PDF, so they open in Microsoft Office, Google Sheets, Google Docs, Google Slides, and LibreOffice. This package does not wrap PhpSpreadsheet, ExcelJS, SheetJS, PHPWord, LibreOffice, or OnlyOffice.

Requirements

Runtime Version
PHP 8.1+, ext-zip, ext-dom, ext-openssl
Node.js 18+ (ESM)

Install

composer require burhan15/office-pack
npm install @burhan15/office-pack
import { Office } from '@burhan15/office-pack';
use Vendor\Office\Office;

Spreadsheet

import { Office } from '@burhan15/office-pack';

const workbook = Office.excel().create();
const sheet = workbook.sheet('Sales');

sheet.setCell('A1', 'Product');
sheet.setCell('B1', 'Price');
sheet.setCell('C1', 'Qty');
sheet.setCell('B2', 10);
sheet.setCell('C2', 3);
sheet.setFormula('D2', '=B2*C2');

await workbook.save('report.xlsx');

const opened = await Office.excel().open('report.xlsx');
opened.calculate();
console.log(opened.sheet('Sales').getCell('D2').value); // 30
<?php
require __DIR__ . '/vendor/autoload.php';

use Vendor\Office\Office;

$workbook = Office::excel()->create();
$sheet = $workbook->sheet('Sales');

$sheet->setCell('A1', 'Product');
$sheet->setCell('B1', 'Price');
$sheet->setCell('C1', 'Qty');
$sheet->setCell('B2', 10);
$sheet->setCell('C2', 3);
$sheet->setFormula('D2', '=B2*C2');

$workbook->save('report.xlsx');

$opened = Office::excel()->open('report.xlsx');
$opened->calculate();
echo $opened->sheet('Sales')->getCell('D2')->value; // 30

Calling sheet('Sales') on a new workbook names the first sheet Sales. It does not leave an empty Sheet1 behind.

Formulas

Formulas use Excel-style English (=SUM(A1:A3)). PHP and Node return the same results for:

SUM AVERAGE COUNT COUNTA MIN MAX IF IFS AND OR NOT ROUND ROUNDUP ROUNDDOWN ABS CEILING FLOOR MOD POWER SQRT CONCAT CONCATENATE LEFT RIGHT MID LEN LOWER UPPER TRIM DATE DAY MONTH YEAR TODAY NOW VLOOKUP HLOOKUP XLOOKUP INDEX MATCH SUMIF SUMIFS COUNTIF COUNTIFS AVERAGEIF AVERAGEIFS

Arithmetic (+ - * / ^) and comparisons (= <> < > <= >=) work too. Any other function evaluates to #NAME?.

VLOOKUP and HLOOKUP take a range and a FALSE argument for an exact match:

sheet.setFormula('E2', '=VLOOKUP("banana",A2:C10,2,FALSE)');

Spreadsheet API

Action PHP Node
Create Office::excel()->create() Office.excel().create()
Open Office::excel()->open($path) await Office.excel().open(path)
Sheet $wb->sheet('Sales') wb.sheet('Sales')
Value $sheet->setCell('A1', 'Hi') sheet.setCell('A1', 'Hi')
Formula $sheet->setFormula('B1', '=A1*2') sheet.setFormula('B1', '=A1*2')
Many rows $sheet->fromRows($rows) sheet.fromRows(rows)
Skip calc on save $wb->calculateOnSave = false Office.excel({ calculateOnSave: false })
Recalculate $wb->calculate() wb.calculate()
Save $wb->save('out.xlsx') await wb.save('out.xlsx')
HTTP bytes echo $wb->toString() wb.toBuffer()
CSV text Office::csvFromRows($rows) Office.csvFromRows(rows)
Large file — await wb.streamToFile(path)

withPassword() stores an SDK envelope (AES-256-GCM). PHP and Node can open that file with the same password. Excel’s own password format is not supported.

Word

const doc = await Office.document().create();
doc.addParagraph('Hello {{name}}');
doc.setHeader('Confidential');
doc.setFooter('Page footer');
doc.addPageBreak();
doc.setVariables({ name: 'Ada' });
await doc.save('letter.docx');
$doc = Office::document()->create();
$doc->addParagraph('Hello {{name}}');
$doc->setHeader('Confidential');
$doc->setFooter('Page footer');
$doc->addPageBreak();
$doc->setVariables(['name' => 'Ada']);
$doc->save('letter.docx');

PowerPoint

const deck = await Office.presentation().create();
deck.slide(1).addText('Quarterly review', { asTitle: true }).addText('Revenue is up');
await deck.save('deck.pptx');
$deck = Office::presentation()->create();
$deck->slide(1)->addText('Quarterly review')->addText('Revenue is up');
$deck->save('deck.pptx');

In PHP, the first addText() on a slide is the title.

PDF

const pdf = await Office.pdf().create();
pdf.addPage('Hello from the SDK');
await pdf.save('note.pdf');
$pdf = Office::pdf()->create();
$pdf->addPage('Hello from the SDK');
$pdf->save('note.pdf');

enableTaggedPdf() writes a page tree with MarkInfo and a language tag. Merge, split, and text redaction are on the PDF object. This is not a veraPDF-certified PDF/UA suite.

HTTP download

import { Office, Mime } from '@burhan15/office-pack';

const wb = Office.excel().create();
wb.sheet('Sales').setCell('A1', 'Hi');
res.writeHead(200, Mime.downloadHeaders('report.xlsx'));
res.end(wb.toBuffer());
use Vendor\Office\Mime;

$wb = Office::excel()->create();
$wb->sheet('Sales')->setCell('A1', 'Hi');
foreach (Mime::downloadHeaders('report.xlsx') as $name => $value) {
    header("$name: $value");
}
echo $wb->toString();

Verify

From a clone of this repository:

npm test
php tests/php/spreadsheet_test.php
php tests/php/formula_test.php

CLI, after npm install @burhan15/office-pack:

node node_modules/@burhan15/office-pack/packages/cli/src/cli.js inspect report.xlsx
node node_modules/@burhan15/office-pack/packages/cli/src/cli.js validate report.xlsx
node node_modules/@burhan15/office-pack/packages/cli/src/cli.js health

Documentation

Topic Doc
Integrator guide docs/guides/integration.md
Excel and Google compatibility docs/guides/ms-google-compatibility.md
Feature matrix docs/research/feature-matrix.md
Architecture docs/architecture/system-architecture.md
Security SECURITY.md

License

Apache-2.0. See LICENSE.