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.
109 lines
5.5 KiB
JavaScript
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) => ({ "&": "&", "<": "<", ">": ">" }[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 };
|
|
});
|