@concordance-wiki/plugin-convert-libreoffice
v1.0.0
Published
Converts docx, pptx and xlsx documents to PDF through headless LibreOffice, with a fingerprint cache.
Readme
Why
A document the wiki can only offer for download is a document nobody opens. This plugin turns the .docx, .pptx and .xlsx files of your repositories into PDF through headless LibreOffice, so that the site previews them without the original application, and extracts the text of every page with pdf.js, for .pdf sources too. That text is what lets the build recognise the words of your business inside a slide deck and cite the page where they appear. It is carried by @concordance-wiki/concordance and by the container image, LibreOffice included; install it on its own next to @concordance-wiki/cli, with LibreOffice on the machine that builds.
Quick start
npm install --save-dev @concordance-wiki/plugin-convert-libreofficeThen declare it in concordance.yaml, and make sure soffice --version answers on the build machine:
plugins:
- "@concordance-wiki/plugin-convert-libreoffice"Every office document and PDF of a source gets a PDF representation and the text of its pages, cached by fingerprint; the conversion block bounds the work:
conversion:
timeout_s: 120
max_size_mb: 50
cache: .concordance-cacheA source that must stay downloadable without a preview declares convert: false.
What you get
- A preview in the page: the PDF of every document opened in the site's viewer, page by page, with a find box over the extracted text.
- Every page searchable: the text of each page goes through the same recognition as a note, so a term cites the page of a deck the way it cites the line of a note.
- Converted once: the cache key is the SHA-256 of the source bytes; an unchanged document is never reconverted nor re-read, whatever its path.
- Bounded work:
conversion.timeout_sandconversion.max_size_mbcap each conversion; a failure is aW-CONV-FAILEDfinding and the document stays downloadable. - Suspect output named: a large source whose PDF holds no text is a
W-CONV-SUSPECTfinding, reproduced on every build. - Graceful without LibreOffice: LibreOffice is an optional dependency; when
sofficeis missing the build says so once (W-PLUGIN-DISABLED, informational), PDF sources are still read, and every office document gets aW-CONV-FAILEDnaming the missing command, downloadable all the same. - Nothing written next to a source: conversions run in a temporary folder under the cache, with their own user profile, in parallel.
Documentation
- Configuration reference, the
conversionblock andconvert: falseon a source - Plugins, the converter contribution point
- Operations, LibreOffice in the container image and the cache between builds
- W-CONV-FAILED and W-CONV-SUSPECT
- Home page, the demo wiki and the changelog
Part of Concordance, MIT licence.
Contribution
Two converter contributions, both producing pdf and text: one for the three office extensions, one for .pdf, whose sources are kept as their own PDF representation without running LibreOffice. The manifest declares LibreOffice as a system dependency detected through soffice --version: when the command is missing, the registry disables the plugin with a W-PLUGIN-DISABLED finding and the documents stay downloadable entities, PDF sources included. Thumbnails are a later story.
Behaviour
- The cache key is the SHA-256 of the source bytes: the PDF is written to
<cache>/convert/<sha256>.pdf, the text of its pages next to it as<sha256>.text.json({ "pages": string[] }, one entry per page), and an unchanged document is never reconverted nor re-read, whatever its path. A cache of a previous version holding the PDF alone gets its text extracted once. - The source is copied to a temporary folder under
<cache>/convert/work/<sha256>/andsoffice --headless --norestore --convert-to pdfruns there with its own user profile (-env:UserInstallation), so that parallel instances do not block each other; the folder is removed afterwards. Nothing is written next to the source. - A document above
maxSizeBytesis not converted; a conversion pasttimeoutMsis killed. Both yield aW-CONV-FAILEDfinding, as does a non-zero exit (the message carries the exit code and the first line of stderr) or a run that produces no PDF. The output has no representation in that case: the document remains available for download. - A source above 100 KiB whose PDF has no extractable text yields a
W-CONV-SUSPECTfinding. The finding is reproduced on cache hits so that a second build reports the same anomalies. - The text comes from
pdfjs-dist, which declares@napi-rs/canvasas an optional dependency for drawing; the plugin never draws (it readsgetTextContentalone), so the native module, installed when a binary exists for the platform and skipped otherwise, is never loaded. extractPdfPages(bytes)gives the text of every page in reading order, the items of a page joined by spaces, and no page for bytes that are not a PDF;extractPdfTextjoins them, one line per page.convertMany(inputs, parallelism, convert)runs conversions through a small pool and returns the results in input order, whatever the completion order.
Testing
Unit tests replace LibreOffice with a fake command runner. The integration test runs the installed soffice only when CONCORDANCE_INTEGRATION=1 is set, which the conversion job of the pipeline does after installing LibreOffice; it is skipped otherwise, it fails when the opt-in finds no command, and every line is covered without it.
