119 lines
3.9 KiB
JavaScript
119 lines
3.9 KiB
JavaScript
/**
|
|
* Exporte les déclinaisons brand (PNG + JPG) depuis les masters raster :
|
|
* - public/brand/ultimail-original.png (stacked + bandeau texte)
|
|
* - public/brand/ultimail-header-icon.png (mark / horizontal)
|
|
*
|
|
* Ne vectorise pas : les SVG VTracer dégradent couleurs et cadrage.
|
|
*
|
|
* pnpm run brand:raster
|
|
*/
|
|
import { existsSync } from "node:fs"
|
|
import { dirname, join } from "node:path"
|
|
import { fileURLToPath } from "node:url"
|
|
import sharp from "sharp"
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const root = join(__dirname, "..")
|
|
const brandDir = join(root, "public/brand")
|
|
|
|
function findOriginalPng() {
|
|
const a = join(brandDir, "ultimail-original.png")
|
|
const b = join(brandDir, "utlimail-original.png")
|
|
if (existsSync(a)) return a
|
|
if (existsSync(b)) return b
|
|
throw new Error("Missing public/brand/ultimail-original.png (or legacy utlimail-original.png).")
|
|
}
|
|
|
|
function headerIconPath() {
|
|
const p = join(brandDir, "ultimail-header-icon.png")
|
|
if (!existsSync(p)) {
|
|
throw new Error("Missing public/brand/ultimail-header-icon.png — run: pnpm run brand:header-icon")
|
|
}
|
|
return p
|
|
}
|
|
|
|
async function writePngJpg(input, outBase, w, h, bg = "#ffffff") {
|
|
const resize = {
|
|
width: w,
|
|
height: h,
|
|
fit: "contain",
|
|
background: { r: 255, g: 255, b: 255, alpha: 1 },
|
|
}
|
|
const pngOut = join(brandDir, `${outBase}.png`)
|
|
const jpgOut = join(brandDir, `${outBase}.jpg`)
|
|
await sharp(input).resize(resize).png({ compressionLevel: 9 }).toFile(pngOut)
|
|
await sharp(input).resize(resize).flatten({ background: bg }).jpeg({ quality: 92, mozjpeg: true }).toFile(jpgOut)
|
|
console.log(`Wrote ${outBase}.png / .jpg`)
|
|
}
|
|
|
|
/** Bande basse du master ≈ wordmark « Ultimail ». */
|
|
async function extractWordmarkTextStrip(originalPath) {
|
|
const meta = await sharp(originalPath).metadata()
|
|
const W = meta.width ?? 0
|
|
const H = meta.height ?? 0
|
|
if (!W || !H) throw new Error("Could not read original dimensions")
|
|
|
|
const splitY = Math.min(H - 32, Math.max(80, Math.round(H * 0.62)))
|
|
const strip = await sharp(originalPath)
|
|
.extract({ left: 0, top: splitY, width: W, height: H - splitY })
|
|
.flatten({ background: "#ffffff" })
|
|
.png()
|
|
.toBuffer()
|
|
|
|
// Deux appels sharp séparés : évite un bug sharp (trim en chaîne après autres exports).
|
|
return sharp(strip).trim({ threshold: 12 }).png().toBuffer()
|
|
}
|
|
|
|
async function buildHorizontalWordmark(originalPath, headerIcon) {
|
|
const iconH = 240
|
|
const iconBuf = await sharp(headerIcon)
|
|
.resize({ height: iconH, fit: "inside" })
|
|
.png()
|
|
.toBuffer()
|
|
const textBuf = await extractWordmarkTextStrip(originalPath)
|
|
const textResized = await sharp(textBuf)
|
|
.resize({ height: Math.round(iconH * 0.72), fit: "inside" })
|
|
.png()
|
|
.toBuffer()
|
|
|
|
const iw = (await sharp(iconBuf).metadata()).width ?? 0
|
|
const ih = (await sharp(iconBuf).metadata()).height ?? 0
|
|
const tw = (await sharp(textResized).metadata()).width ?? 0
|
|
const th = (await sharp(textResized).metadata()).height ?? 0
|
|
const padX = 48
|
|
const gap = 56
|
|
const canvasW = iw + tw + padX * 2 + gap
|
|
const canvasH = Math.max(ih, th) + 32
|
|
|
|
return sharp({
|
|
create: {
|
|
width: canvasW,
|
|
height: canvasH,
|
|
channels: 3,
|
|
background: { r: 255, g: 255, b: 255 },
|
|
},
|
|
})
|
|
.composite([
|
|
{ input: iconBuf, left: padX, top: Math.round((canvasH - ih) / 2) },
|
|
{ input: textResized, left: padX + iw + gap, top: Math.round((canvasH - th) / 2) },
|
|
])
|
|
.png()
|
|
.toBuffer()
|
|
}
|
|
|
|
async function main() {
|
|
const original = findOriginalPng()
|
|
const headerIcon = headerIconPath()
|
|
|
|
const horizontalBuf = await buildHorizontalWordmark(original, headerIcon)
|
|
|
|
await writePngJpg(headerIcon, "ultimail-mark", 256, 256)
|
|
await writePngJpg(original, "ultimail-wordmark-stacked", 800, 800)
|
|
await writePngJpg(horizontalBuf, "ultimail-wordmark-horizontal", 1600, 460)
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e)
|
|
process.exit(1)
|
|
})
|