- Coming-soon page "Kantarci Markt – Neueröffnung" rebuilt from the delivered package with imprint, privacy and 404 (scripts/reset-kantarci-markt.sh, ports 8100/8101). - Own CSS accepts a short quoted string for `content`, so ::before/::after work. - cms:fonts:fetch replaces only the rules of the fetched families in fonts.css. - A variable font is linked and preloaded once with a weight range instead of once per weight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
4.5 KiB
JavaScript
83 lines
4.5 KiB
JavaScript
/*
|
||
* Bereitet die Bilder der Seite „Kantarci Markt – Neueröffnung“ für die Mediathek vor.
|
||
*
|
||
* Die Mediathek nimmt kein SVG. Das Seitensymbol und der runde Stempel „Frische • Qualität •
|
||
* Vertrauen“ der Vorlage sind aber SVG (der Stempel mit Text auf einem Kreisbogen, das kann kein
|
||
* Baustein). Chromium rastert beide zu PNG. Das Logo der Vorlage (1536 × 1024, 1,3 MB) wird auf
|
||
* seinen sichtbaren Teil zugeschnitten und auf 600 px Breite verkleinert (doppelte Anzeigebreite),
|
||
* angezeigt wird es mit höchstens 300 px.
|
||
*
|
||
* node scripts/build-kantarci-markt-assets.mjs [ordner]
|
||
* → logo.png, symbol.png, stempel.png im Ordner (Standard var/kantarci-markt-assets)
|
||
*/
|
||
import { chromium } from 'playwright';
|
||
import { readFile, writeFile } from 'node:fs/promises';
|
||
|
||
const ordner = process.argv[2] || 'var/kantarci-markt-assets';
|
||
const FORST = '#123b26', GOLD = '#a88332', GOLD_HELL = '#d2b978';
|
||
|
||
const browser = await chromium.launch();
|
||
const seite = await browser.newPage();
|
||
|
||
// Logo: sichtbaren Bereich über den Alphakanal bestimmen, zuschneiden, verkleinern.
|
||
const logo = (await readFile(`${ordner}/kantarci-markt-logo.png`)).toString('base64');
|
||
const logoPng = await seite.evaluate(async (daten) => {
|
||
const bild = new Image();
|
||
bild.src = `data:image/png;base64,${daten}`;
|
||
await bild.decode();
|
||
const quelle = new OffscreenCanvas(bild.width, bild.height);
|
||
const q = quelle.getContext('2d');
|
||
q.drawImage(bild, 0, 0);
|
||
const { data } = q.getImageData(0, 0, bild.width, bild.height);
|
||
let [x0, y0, x1, y1] = [bild.width, bild.height, 0, 0];
|
||
for (let y = 0; y < bild.height; y++) {
|
||
for (let x = 0; x < bild.width; x++) {
|
||
// Der Schein um die Waage läuft sehr flach aus; erst ab etwa 8 % Deckkraft zählt er.
|
||
if (data[(y * bild.width + x) * 4 + 3] > 20) {
|
||
x0 = Math.min(x0, x); y0 = Math.min(y0, y); x1 = Math.max(x1, x); y1 = Math.max(y1, y);
|
||
}
|
||
}
|
||
}
|
||
const rand = 8;
|
||
x0 = Math.max(0, x0 - rand); y0 = Math.max(0, y0 - rand);
|
||
x1 = Math.min(bild.width - 1, x1 + rand); y1 = Math.min(bild.height - 1, y1 + rand);
|
||
const breite = x1 - x0 + 1, hoehe = y1 - y0 + 1;
|
||
const ziel = Math.min(600, breite);
|
||
const leinwand = new OffscreenCanvas(ziel, Math.round(hoehe * ziel / breite));
|
||
const z = leinwand.getContext('2d');
|
||
z.imageSmoothingQuality = 'high';
|
||
z.drawImage(bild, x0, y0, breite, hoehe, 0, 0, leinwand.width, leinwand.height);
|
||
const blob = await leinwand.convertToBlob({ type: 'image/png' });
|
||
const bytes = new Uint8Array(await blob.arrayBuffer());
|
||
let binaer = '';
|
||
for (const b of bytes) binaer += String.fromCharCode(b);
|
||
return { png: btoa(binaer), breite: leinwand.width, hoehe: leinwand.height, ausschnitt: [x0, y0, breite, hoehe] };
|
||
}, logo);
|
||
await writeFile(`${ordner}/logo.png`, Buffer.from(logoPng.png, 'base64'));
|
||
console.log(`logo.png ${logoPng.breite} × ${logoPng.hoehe} (Ausschnitt ${logoPng.ausschnitt.join(', ')})`);
|
||
|
||
const rastern = async (svg, groesse, datei) => {
|
||
await seite.setViewportSize({ width: groesse, height: groesse });
|
||
await seite.setContent(`<!doctype html><style>html,body{margin:0;background:transparent}svg{display:block}</style>${svg}`);
|
||
await seite.screenshot({ path: `${ordner}/${datei}`, omitBackground: true });
|
||
console.log(`${datei} ${groesse} × ${groesse}`);
|
||
};
|
||
|
||
// Seitensymbol: das K mit Blatt aus dem Favicon der Vorlage.
|
||
await rastern(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="256" height="256">
|
||
<rect width="64" height="64" rx="14" fill="#062f24"/>
|
||
<path d="M19 43V18h7v10l10-10h9L33 30l13 13h-9L26 32v11z" fill="${GOLD}"/>
|
||
<path d="M41 10c-7 2-10 6-9 13 6-1 10-5 9-13Z" fill="${GOLD}"/></svg>`, 256, 'symbol.png');
|
||
|
||
// Stempel: goldene Scheibe, Umschrift auf dem Kreis, Blatt in der Mitte. Doppelte Größe für
|
||
// scharfe Kanten auf hochauflösenden Bildschirmen (angezeigt mit 122 px).
|
||
await rastern(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122 122" width="244" height="244">
|
||
<circle cx="61" cy="61" r="61" fill="${GOLD_HELL}"/>
|
||
<g transform="translate(10 10) scale(1.1333)">
|
||
<defs><path id="kreis" d="M45,45 m-31,0 a31,31 0 1,1 62,0 a31,31 0 1,1 -62,0"/></defs>
|
||
<text font-family="Arial, sans-serif" font-weight="700" font-size="8" letter-spacing="1.4" fill="${FORST}"><textPath href="#kreis">FRISCHE • QUALITÄT • VERTRAUEN • </textPath></text>
|
||
<path d="M45 52c-2-16 6-25 22-26-1 16-8 24-22 26Zm0 0c-8-11-17-13-27-7 7 12 16 14 27 7Z" fill="${FORST}"/>
|
||
</g></svg>`, 244, 'stempel.png');
|
||
|
||
await browser.close();
|