contentpanel - Aushangworkflow um docx Vorlage erweitern

This commit is contained in:
2026-09-02 19:52:51 +02:00
parent 420a73a0fe
commit c5c052ac87
39 changed files with 5124 additions and 1270 deletions
@@ -0,0 +1,6 @@
[
{"family": "Suisse Int'l Light", "alias": "SuisseIntlLight", "file": "SuisseIntl-Light.otf", "style": "normal"},
{"family": "Suisse Int'l Light", "alias": "SuisseIntlLight", "file": "SuisseIntl-LightItalic.otf", "style": "italic"},
{"family": "Suisse Int'l Medium", "alias": "SuisseIntlMedium", "file": "SuisseIntl-Medium.otf", "style": "normal"},
{"family": "Suisse Int'l Semi Bold", "alias": "SuisseIntlSemiBold", "file": "SuisseIntl-SemiBold.otf", "style": "normal"}
]
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contentpanel DOCX Renderer</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/renderer.jsx"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"name": "contentpanel-betteroffice-renderer",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "vite build"
},
"dependencies": {
"@betteroffice/docx": "0.1.0",
"@betteroffice/docx-react": "0.1.0",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
"vite": "8.2.2"
}
}
@@ -0,0 +1,37 @@
import faces from '../fonts.json';
const buffers = new Map();
export async function loadTemplateFonts() {
await Promise.all(faces.map(async (face) => {
const response = await fetch(`/fonts/${face.file}`);
if (!response.ok) throw new Error(`Schriftdatei fehlt: ${face.file} (HTTP ${response.status})`);
const bytes = await response.arrayBuffer();
// One set of OTF bytes for both native shaping and browser painting.
const font = new FontFace(face.alias, bytes, { style: face.style, weight: '400' });
await font.load();
document.fonts.add(font);
buffers.set(face.file, bytes);
}));
await document.fonts.ready;
window.__betterofficeFontsReady = true;
}
export const measurementFontProvider = {
resolve(family, bold, italic) {
const normalized = family.toLowerCase();
// Empty paragraphs in this template inherit Calibri. Give those paragraphs
// the same brand metrics instead of allowing synthetic line measurements.
let face = faces.find((f) =>
(f.alias.toLowerCase() === normalized || f.family.toLowerCase() === normalized ||
(normalized === 'calibri' && f.alias === 'SuisseIntlLight')) &&
f.style === (italic ? 'italic' : 'normal'));
if (bold && !italic && face) face = faces.find((f) => f.alias === 'SuisseIntlSemiBold');
if (!face || (bold && italic)) throw new Error(`Nicht unterstützte Schrift: ${family} (bold=${bold}, italic=${italic})`);
return async () => {
const bytes = buffers.get(face.file);
if (!bytes) throw new Error(`Schrift wurde nicht geladen: ${face.file}`);
return bytes;
};
},
};
@@ -0,0 +1,12 @@
/* Browser and native layout share the OTF buffers loaded by fonts.js. */
html,
body,
#root {
min-height: 100%;
margin: 0;
}
body {
overflow: visible;
background: #fff;
}
@@ -0,0 +1,26 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { DocxEditor } from '@betteroffice/docx-react';
import { setGoogleFontsEnabled } from '@betteroffice/docx/utils';
import '@betteroffice/docx-react/styles.css';
import './renderer.css';
import { loadTemplateFonts, measurementFontProvider } from './fonts';
setGoogleFontsEnabled(false); // The renderer is entirely local, including fonts.
const reportError = error => { window.__betterofficeError ||= String(error?.stack || error); };
window.addEventListener('error', event => reportError(event.error || event.message));
window.addEventListener('unhandledrejection', event => reportError(event.reason));
try {
await loadTemplateFonts();
const response = await fetch('/input.docx');
if (!response.ok) throw new Error(`DOCX HTTP ${response.status}`);
const bytes = new Uint8Array(await response.arrayBuffer());
createRoot(document.getElementById('root')).render(
<DocxEditor documentBuffer={bytes} measurementFontProvider={measurementFontProvider}
ref={editor => { window.__betterofficeEditor = editor; }} onError={reportError}
mode="viewing" showToolbar={false} showRuler={false} showZoomControl={false} />
);
} catch (error) {
reportError(error);
}
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite';
export default defineConfig({
build: {
target: 'es2022',
rollupOptions: { output: { entryFileNames: 'app.js' } },
},
});