<p align="center">
  <img src="screenshots/hero.png" width="380">
</p>

<h1 align="center">msoffice2pdf</h1>

<p align="center">
  Node.js library to convert Microsoft Office documents to PDF.<br>
  Powered by LibreOffice under the hood.
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/msoffice2pdf"><img src="https://img.shields.io/npm/v/msoffice2pdf.svg" alt="npm version"></a>
  <a href="https://www.npmjs.com/package/msoffice2pdf"><img src="https://img.shields.io/npm/dm/msoffice2pdf.svg" alt="npm downloads"></a>
  <a href="https://github.com/shumatsumonobu/msoffice2pdf/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/msoffice2pdf.svg" alt="license"></a>
</p>

<p align="center">
  <code>.pptx</code> <code>.ppt</code> <code>.docx</code> <code>.doc</code> <code>.xlsx</code> <code>.xls</code> → <code>.pdf</code>
</p>

## Quick Start

```sh
npm install msoffice2pdf
```

```js
import msoffice2pdf from 'msoffice2pdf';

await msoffice2pdf('presentation.pptx', 'output.pdf');
```

That's it. Word, Excel, PowerPoint — all converted to PDF in one line.

## Platforms

Linux, macOS, and Windows are all supported.

## Prerequisites

**LibreOffice 5.3+** is required.
See the [official install guide](https://www.libreoffice.org/get-help/install-howto/) for your platform.

| OS | Command |
|---|---|
| Ubuntu / Debian | `sudo apt install -y libreoffice` |
| Fedora / RHEL 8+ | `sudo dnf install -y libreoffice` |
| macOS | `brew install --cask libreoffice` |
| Windows | [Download installer](https://www.libreoffice.org/download/download/) |

**Amazon Linux 2023** does not include LibreOffice in its repositories. Install from RPM.
Check [the latest version here](https://download.documentfoundation.org/libreoffice/stable/) and replace the version number below:

```sh
sudo dnf install -y libXinerama cups-libs

cd /tmp
wget https://download.documentfoundation.org/libreoffice/stable/26.2.1/rpm/x86_64/LibreOffice_26.2.1_Linux_x86-64_rpm.tar.gz
tar -xzf LibreOffice_26.2.1_Linux_x86-64_rpm.tar.gz
sudo dnf install -y LibreOffice_26.2.1*_Linux_x86-64_rpm/RPMS/*.rpm
```

> **Note:** RPM installs place the binary at `/opt/libreoffice*/program/soffice` instead of `/usr/bin/libreoffice`. If msoffice2pdf cannot find it automatically, specify the path with the [`libreOfficeBinaryPath`](#options) option.

> Japanese fonts can be placed in `~/.local/share/fonts/`.

## API

### `msoffice2pdf(inputPath, outputPath, options?)`

Converts a Microsoft Office document to PDF using LibreOffice in headless mode.
Each call spawns an isolated LibreOffice process with its own user profile, so multiple conversions can run in parallel safely.

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `inputPath` | `string` | Yes | Path to the source document. Supported formats: `.ppt`, `.pptx`, `.doc`, `.docx`, `.xls`, `.xlsx` |
| `outputPath` | `string` | Yes | Destination path for the generated PDF. Parent directories are created automatically if they don't exist |
| `options` | [`Options`](#options) | No | Configuration object |

**Returns** `Promise<void>` — Resolves when the PDF has been written to `outputPath`. Rejects with a typed error on failure (see [Errors](#errors)).

### Options

| Property | Type | Default | Description |
|---|---|---|---|
| `libreOfficeBinaryPath` | `string` | Auto-detected | Full path to the LibreOffice `soffice` binary. When omitted, the binary is [auto-detected](#libreoffice-auto-detection) from standard install locations |
| `language` | `string` | System default | Locale code passed to LibreOffice's `--language` flag (e.g. `'ja'`, `'en-us'`, `'de'`). See [Supported Languages](#supported-languages) for all valid codes |

### Examples

**Basic conversion**

```js
import msoffice2pdf from 'msoffice2pdf';

await msoffice2pdf('report.docx', 'report.pdf');
```

**With language option**

```js
await msoffice2pdf('invoice.xlsx', 'invoice.pdf', {
  language: 'ja',
});
```

**Custom LibreOffice path**

```js
await msoffice2pdf('slides.pptx', 'slides.pdf', {
  libreOfficeBinaryPath: '/opt/libreoffice26.2/program/soffice',
});
```

**CommonJS**

```js
const msoffice2pdf = require('msoffice2pdf');

await msoffice2pdf('report.docx', 'report.pdf');
```

### Errors

All errors extend `Error` and have a descriptive `name` property for programmatic handling.

| Error | `name` | Message example | Thrown when |
|---|---|---|---|
| `UnsupportedOSError` | `'UnsupportedOSError'` | `"aix is not supported. msoffice2pdf supports Linux, macOS, and Windows"` | Running on an unsupported platform |
| `LibreOfficeNotFoundError` | `'LibreOfficeNotFoundError'` | `"Could not find LibreOffice binary. Please install LibreOffice or specify libreOfficeBinaryPath"` | No LibreOffice binary found at any [search path](#libreoffice-auto-detection) |
| `SpecifiedLibreOfficeNotFoundError` | `'SpecifiedLibreOfficeNotFoundError'` | `"The specified LibreOffice binary was not found: /invalid/path"` | `options.libreOfficeBinaryPath` does not exist |
| `InputFileNotFoundError` | `'InputFileNotFoundError'` | `"Input file not found: report.docx"` | `inputPath` does not exist |
| `UnsupportedFileError` | `'UnsupportedFileError'` | `"Unsupported file format. Supported formats: .ppt, .pptx, .doc, .docx, .xls, .xlsx"` | File extension is not one of the supported formats |
| `UnsupportedLanguageError` | `'UnsupportedLanguageError'` | `"Unsupported language code: xyz"` | `options.language` is not a valid locale code |

```js
import msoffice2pdf from 'msoffice2pdf';

try {
  await msoffice2pdf('report.docx', 'report.pdf');
} catch (err) {
  switch (err.name) {
    case 'InputFileNotFoundError':
      console.error('File does not exist:', err.message);
      break;
    case 'LibreOfficeNotFoundError':
      console.error('Please install LibreOffice first');
      break;
    default:
      throw err;
  }
}
```

### LibreOffice Auto-Detection

When `libreOfficeBinaryPath` is omitted, the following paths are searched in order. The first accessible binary is used.

| OS | Search paths |
|---|---|
| Linux | `/usr/bin/libreoffice`, `/usr/bin/soffice`, `/snap/bin/libreoffice`, `/opt/libreoffice*/program/soffice`, `/usr/lib/libreoffice*/program/soffice`, `/usr/lib64/libreoffice*/program/soffice` |
| macOS | `/Applications/LibreOffice.app/Contents/MacOS/soffice` |
| Windows | `C:\Program Files\LibreOffice*\program\soffice.exe`, `C:\Program Files (x86)\LibreOffice*\program\soffice.exe` |

Paths with `*` are expanded using glob, so version-numbered directories (e.g. `/opt/libreoffice26.2/`) are matched automatically.

### Supported Languages

| Language | Code | | Language | Code | | Language | Code |
|---|---|---|---|---|---|---|---|
| Afrikaans | `af` | | French (Standard) | `fr` | | Norwegian (Nynorsk) | `nn` |
| Albanian | `sq` | | French (Switzerland) | `fr-ch` | | Polish | `pl` |
| Arabic (Algeria) | `ar-dz` | | Gaelic (Scotland) | `gd` | | Portuguese (Brazil) | `pt-br` |
| Arabic (Bahrain) | `ar-bh` | | German (Austria) | `de-at` | | Portuguese (Portugal) | `pt` |
| Arabic (Egypt) | `ar-eg` | | German (Liechtenstein) | `de-li` | | Punjabi | `pa` |
| Arabic (Iraq) | `ar-iq` | | German (Luxembourg) | `de-lu` | | Rhaeto-Romanic | `rm` |
| Arabic (Jordan) | `ar-jo` | | German (Standard) | `de` | | Romanian | `ro` |
| Arabic (Kuwait) | `ar-kw` | | German (Switzerland) | `de-ch` | | Romanian (Moldova) | `ro-md` |
| Arabic (Lebanon) | `ar-lb` | | Greek | `el` | | Russian | `ru` |
| Arabic (Libya) | `ar-ly` | | Hebrew | `he` | | Russian (Moldova) | `ru-md` |
| Arabic (Morocco) | `ar-ma` | | Hindi | `hi` | | Serbian | `sr` |
| Arabic (Oman) | `ar-om` | | Hungarian | `hu` | | Slovak | `sk` |
| Arabic (Qatar) | `ar-qa` | | Icelandic | `is` | | Slovenian | `sl` |
| Arabic (Saudi Arabia) | `ar-sa` | | Indonesian | `id` | | Sorbian | `sb` |
| Arabic (Syria) | `ar-sy` | | Irish | `ga` | | Spanish (Argentina) | `es-ar` |
| Arabic (Tunisia) | `ar-tn` | | Italian (Standard) | `it` | | Spanish (Spain) | `es` |
| Arabic (U.A.E.) | `ar-ae` | | Italian (Switzerland) | `it-ch` | | Spanish (Mexico) | `es-mx` |
| Arabic (Yemen) | `ar-ye` | | Japanese | `ja` | | Swedish | `sv` |
| Basque | `eu` | | Korean | `ko` | | Swedish (Finland) | `sv-fi` |
| Belarusian | `be` | | Kurdish | `ku` | | Thai | `th` |
| Bulgarian | `bg` | | Latvian | `lv` | | Tsonga | `ts` |
| Catalan | `ca` | | Lithuanian | `lt` | | Tswana | `tn` |
| Chinese (Hong Kong) | `zh-hk` | | Macedonian | `mk` | | Turkish | `tr` |
| Chinese (PRC) | `zh-cn` | | Malayalam | `ml` | | Ukrainian | `ua` |
| Chinese (Singapore) | `zh-sg` | | Malaysian | `ms` | | Urdu | `ur` |
| Chinese (Taiwan) | `zh-tw` | | Maltese | `mt` | | Venda | `ve` |
| Croatian | `hr` | | Norwegian | `no` | | Vietnamese | `vi` |
| Czech | `cs` | | Norwegian (Bokmal) | `nb` | | Welsh | `cy` |
| Danish | `da` | | Dutch (Belgium) | `nl-be` | | Xhosa | `xh` |
| Dutch (Standard) | `nl` | | English | `en` | | Yiddish | `ji` |
| English (Australia) | `en-au` | | Estonian | `et` | | Zulu | `zu` |
| English (Canada) | `en-ca` | | Faeroese | `fo` | | | |
| English (Ireland) | `en-ie` | | Farsi | `fa` | | | |
| English (U.K.) | `en-gb` | | Finnish | `fi` | | | |
| English (U.S.) | `en-us` | | French (Belgium) | `fr-be` | | | |

## Conversion Examples

| Format | Source | Result |
|---|---|---|
| PowerPoint | ![](screenshots/evidence/pptx-source.png) | ![](screenshots/evidence/pptx-result.png) |
| Word | ![](screenshots/evidence/docx-source.png) | ![](screenshots/evidence/docx-result.png) |
| Excel | ![](screenshots/evidence/xlsx-source.png) | ![](screenshots/evidence/xlsx-result.png) |

## Author

**shumatsumonobu** — [GitHub](https://github.com/shumatsumonobu) · [X](https://x.com/shumatsumonobu) · [Facebook](https://www.facebook.com/takuya.motoshima.7)

## License

[MIT](LICENSE)
