Files
parenthese/landing/modeles/generer.mjs
T
IliesAllaliandClaude Opus 5.5 2d8ac66372 Site en anglais : accueil, aide, vie privée, guides et modèles sous /en/
Accueil prérendu en deux langues, huit pages statiques traduites avec hreflang
des deux côtés, captures de l'aide refaites dans l'app anglaise, modèles PDF
générés en français et en anglais, sitemap. Test des liens internes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:01:16 +02:00

98 lines
4.5 KiB
JavaScript

// Génère les modèles PDF A4 et leurs aperçus WebP pour /arbre-genealogique-a-remplir/ (français)
// et /en/printable-family-tree/ (anglais).
//
// node modeles/generer.mjs (depuis landing/, les deux langues)
// node modeles/generer.mjs dessine (seulement les modèles dont le nom de fichier contient « dessine »)
//
// Playwright n'est pas une dépendance de la landing : le script le cherche dans node_modules,
// puis dans le dossier indiqué par PLAYWRIGHT_DIR (ex. D:/Commercial/tools/web-qa/node_modules).
// Chromium est lancé via le canal « chrome » (Chrome installé sur la machine).
// Les polices (Newsreader, DM Sans) viennent de Google Fonts et sont embarquées dans le PDF.
import { createRequire } from 'node:module'
import { mkdirSync, writeFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { MODELES } from './gabarits.mjs'
const require = createRequire(import.meta.url)
function chargerPlaywright() {
const dirs = [process.env.PLAYWRIGHT_DIR, 'D:/Commercial/tools/web-qa/node_modules'].filter(Boolean)
try { return require('playwright') } catch {}
for (const dir of dirs) {
try { return require(require.resolve('playwright', { paths: [dir] })) } catch {}
}
throw new Error('Playwright introuvable. Définir PLAYWRIGHT_DIR vers un node_modules qui le contient.')
}
// Une sortie par langue : dossier de la page et nom de fichier de chaque modèle
const LANGUES = [
{ lang: 'fr', out: fileURLToPath(new URL('../public/arbre-genealogique-a-remplir/', import.meta.url)), nom: (m) => m.slug },
{ lang: 'en', out: fileURLToPath(new URL('../public/en/printable-family-tree/', import.meta.url)), nom: (m) => m.en },
]
const APERCU_LARGEUR = 720
// Recale chaque trait d'écriture juste après son libellé, une fois les polices chargées
function ajusterTraits() {
document.querySelectorAll('g.f').forEach((g) => {
const t = g.querySelector('text')
const l = g.querySelector('line')
const b = t.getBBox()
l.setAttribute('x1', String(Math.round((b.x + b.width + 1.2) * 100) / 100))
})
}
async function main() {
const { chromium } = chargerPlaywright()
const browser = await chromium.launch({ channel: 'chrome' })
const ctx = await browser.newContext({ deviceScaleFactor: 2 })
const page = await ctx.newPage()
const convert = await ctx.newPage()
const seul = process.argv[2]
for (const { lang, out, nom } of LANGUES) for (const m of MODELES) {
const slug = nom(m)
if (seul && !slug.includes(seul)) continue
mkdirSync(out, { recursive: true })
const html = m.build(lang)
const [wmm, hmm] = m.portrait ? [210, 297] : [297, 210]
await page.setViewportSize({ width: Math.ceil(wmm * 96 / 25.4), height: Math.ceil(hmm * 96 / 25.4) })
await page.setContent(html, { waitUntil: 'networkidle' })
const ok = await page.evaluate(async () => {
await Promise.all([
document.fonts.load('400 12px "DM Sans"'),
document.fonts.load('500 12px "DM Sans"'),
document.fonts.load('400 12px "Newsreader"'),
document.fonts.load('italic 400 12px "Newsreader"'),
])
await document.fonts.ready
return document.fonts.check('400 12px "DM Sans"') && document.fonts.check('400 12px "Newsreader"')
})
if (!ok) throw new Error(`Polices non chargées pour ${slug}`)
await page.evaluate(ajusterTraits)
const pdf = `${out}${slug}.pdf`
await page.pdf({ path: pdf, preferCSSPageSize: true, printBackground: true })
// Aperçu : capture de la feuille, réduite et encodée en WebP par le canvas de Chromium
const png = await page.locator('.sheet').screenshot({ type: 'png' })
const dataUrl = await convert.evaluate(async ({ b64, largeur }) => {
const img = new Image()
img.src = `data:image/png;base64,${b64}`
await img.decode()
const c = document.createElement('canvas')
c.width = largeur
c.height = Math.round(img.height * largeur / img.width)
const g = c.getContext('2d')
g.imageSmoothingQuality = 'high'
g.drawImage(img, 0, 0, c.width, c.height)
return { url: c.toDataURL('image/webp', 0.86), w: c.width, h: c.height }
}, { b64: png.toString('base64'), largeur: APERCU_LARGEUR })
const webp = Buffer.from(dataUrl.url.split(',')[1], 'base64')
writeFileSync(`${out}${slug}.webp`, webp)
console.log(`${lang} ${slug} pdf ok apercu ${dataUrl.w}x${dataUrl.h} ${Math.round(webp.length / 1024)} Ko`)
}
await browser.close()
}
main().catch((e) => { console.error(e); process.exit(1) })