theseus/bundled-addons/docx-editor/lib/doc-css.js
Local Dev 56b3f2f206 feat(docx-editor): ship it in the build, updated over the first-party channel
Moving it out of the build left it with no way in. Settings can only install
from the community catalogue, so a first-party extension that isn't bundled
has a working update channel and no first copy for anyone to update — the
mechanism was all there and the front door was missing.

So it goes back beside screenshot, aegis and pdf-editor: seeded into every
profile by the build, listed under "Built into Theseus", and kept current
between releases by the operator-signed channel at
theseus.x/extensions/docx-editor/. That is the arrangement docs/ADDON-UPDATES.md
describes, and the one the signing script was written for.

About 400 KB compressed in the installer, most of it the vendored editor
libraries — next to the ~4 MB of pdf.js that pdf-editor already ships, the
weight argument for keeping it out didn't survive contact with the numbers.

The end-to-end driver goes back to checking that a fresh profile seeds it,
which is the property that actually matters now.
2026-09-21 22:47:13 +02:00

109 lines
5.5 KiB
JavaScript

// How a document looks — the typography of the page itself, as opposed to
// the editor's furniture around it (that lives in editor.css).
//
// It is a string rather than a stylesheet because two different documents
// need exactly these rules: the editor's own page, and the standalone HTML
// handed to Chromium when exporting a PDF. Loading a shared .css file into
// the export isn't possible — it is rendered in a throwaway window from a
// temp file, with no add-on folder to resolve relative URLs against — and
// keeping two copies in step by hand is how a PDF starts quietly disagreeing
// with what the user was looking at.
//
// Defaults follow Word's: an 11pt serif body with a little space after each
// paragraph, so an untouched document looks about the same in both places.
(function (root, factory) {
const api = factory();
if (typeof module === "object" && module.exports) module.exports = api;
root.DocxEditor = Object.assign(root.DocxEditor || {}, { docCss: api });
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
"use strict";
const DOC_CSS = `
.sheet { font: 11pt/1.5 Georgia, "Times New Roman", serif; color: #14161a; }
.sheet p { margin: 0 0 8pt; }
.sheet h1, .sheet h2, .sheet h3, .sheet h4, .sheet h5, .sheet h6 {
font-family: "Segoe UI Semibold", "Segoe UI", Calibri, system-ui, sans-serif;
color: #1f4e79; font-weight: 600; margin: 14pt 0 6pt; line-height: 1.25;
}
.sheet h1 { font-size: 20pt; } .sheet h2 { font-size: 16pt; }
.sheet h3 { font-size: 13pt; } .sheet h4 { font-size: 12pt; }
.sheet h5 { font-size: 11pt; } .sheet h6 { font-size: 10.5pt; color: #2e74b5; }
.sheet blockquote { margin: 8pt 0 8pt 24pt; padding-left: 10pt;
border-left: 3px solid rgba(0,0,0,.15); color: #404040; font-style: italic; }
.sheet pre { font: 10pt/1.4 Consolas, "Courier New", monospace;
background: rgba(0,0,0,.04); border: 1px solid rgba(0,0,0,.08);
border-radius: 3px; padding: 8pt 10pt; margin: 8pt 0; white-space: pre-wrap; }
.sheet hr { border: 0; border-top: 1px solid #808080; margin: 10pt 0; }
.sheet ul, .sheet ol { margin: 0 0 8pt; padding-left: 28pt; }
.sheet li { margin: 0 0 2pt; }
.sheet li > p { margin: 0 0 2pt; }
.sheet a { color: #0563c1; text-decoration: underline; }
.sheet img { max-width: 100%; height: auto; vertical-align: baseline; }
.sheet table { border-collapse: collapse; margin: 8pt 0; width: 100%; table-layout: fixed; }
.sheet td, .sheet th { border: 1px solid #999; padding: 4pt 6pt; vertical-align: top;
position: relative; min-width: 1em; }
.sheet th { background: rgba(0,0,0,.04); font-weight: 600; text-align: left; }
.sheet td > p:last-child, .sheet th > p:last-child { margin-bottom: 0; }
.sheet .docx-note-ref { color: #0563c1; font-size: .7em; padding: 0 1px;
border-bottom: 1px dotted #0563c1; }
/* A page break is a labelled dashed rule on screen and a real break on
paper. printToPDF renders with print media, so this covers the PDF
export and Ctrl+P both. */
.sheet .docx-page-break { border-top: 1px dashed #b00; margin: 14pt 0; text-align: center;
user-select: none; position: relative; }
.sheet .docx-page-break span { font: 9pt/1 system-ui, sans-serif; color: #b00;
background: #fff; padding: 0 8px; position: relative;
top: -6pt; letter-spacing: .04em; }
@media print {
.sheet .docx-page-break { border: 0; margin: 0; break-after: page; page-break-after: always; }
.sheet .docx-page-break span { display: none; }
.sheet table, .sheet tr, .sheet img { break-inside: avoid; }
.sheet h1, .sheet h2, .sheet h3, .sheet h4, .sheet h5, .sheet h6 { break-after: avoid; }
}
`;
// Twips are Word's unit (1440 to the inch); CSS wants inches. Rounded to
// thousandths, which is finer than any printer cares about and keeps the
// @page rule readable.
const inches = (twips, fallback) => {
const n = Number(twips);
return (Number.isFinite(n) && n > 0 ? n / 1440 : fallback).toFixed(3);
};
// An @page rule from the document's own section setup, so the PDF comes
// out on the paper the author chose instead of Chromium's default Letter.
function pageRule(setup) {
const page = (setup && setup.page) || {};
const size = page.size || {};
const m = page.margin || {};
const w = inches(size.width, 8.27); // A4 unless the file says otherwise
const h = inches(size.height, 11.69);
const top = inches(m.top, 1);
const right = inches(m.right, 1);
const bottom = inches(m.bottom, 1);
const left = inches(m.left, 1);
return `@page { size: ${w}in ${h}in; margin: ${top}in ${right}in ${bottom}in ${left}in; }`;
}
/**
* A standalone HTML page of the document, ready to be handed to Chromium's
* print pipeline. Everything is inline: no stylesheet to resolve, no
* scripts, and images are already data URLs by the time they reach here.
*/
function printableHtml({ title, bodyHtml, setup }) {
const esc = (s) => String(s || "").replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
return `<!doctype html>
<html><head><meta charset="utf-8"><title>${esc(title || "Document")}</title>
<style>
${pageRule(setup)}
html, body { margin: 0; padding: 0; background: #fff; }
${DOC_CSS}
/* The page box is the paper; the sheet must not add a second margin. */
.sheet { max-width: none; margin: 0; padding: 0; background: #fff; box-shadow: none; }
</style></head>
<body><div class="sheet">${bodyHtml}</div></body></html>`;
}
return { DOC_CSS, printableHtml, pageRule };
});