Files
pdfme/playground/scripts/generate-templates-thumbnail.mjs
Kyohei Fukuda bed77e5713 [codex] add list schema implementation plan (#1460)
* docs: add list schema implementation plan

* feat(schemas): add list schema

* fix(schemas): improve list form editing

* fix(schemas): localize list editing labels

* fix(schemas): align table control buttons

* fix(schemas): keep list designer editing during actions

* fix(tsconfig): resolve list schema subpath

* fix(schemas): keep list action clicks isolated

* fix(schemas): support empty list state

* fix(schemas): allow editing list items in designer

* fix(schemas): keep list designer editing on enter

* fix(schemas): ignore IME enter in list editor

* fix(schemas): make enter insert list item line breaks

* test(generator): add list plugin to playground snapshots

* refactor(schemas): render list items with text ui

* fix(schemas): adapt list prop panel fields by style

* fix(schemas): simplify list options and nested numbering

* Reorder import statements in generate-templates-thumbnail

* Reorder List plugin in getPlugins function

* fix(ui): reflow schemas after dynamic list resize

* chore: remove obsolete list plan

* fix(ui): reflow form list height changes

* fix(ui): keep designer height changes local

* fix(schemas): commit list item line breaks immediately

* fix(schemas): restore list focus after line break rerender

* fix(schemas): keep form list focused after enter

* test(generator): update dynamic list snapshots

* fix(schemas): store list content as json arrays

* fix(schemas): address list review cleanup

* test(ui): update designer snapshot

* chore: trim list pr noise

* fix(schemas): align list markers in ui
2026-05-03 13:34:51 +09:00

155 lines
3.8 KiB
JavaScript

import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import pLimit from 'p-limit';
import { getDefaultFont, getInputFromTemplate } from '@pdfme/common';
import { pdf2img } from '@pdfme/converter';
import { generate } from '@pdfme/generator';
import {
multiVariableText,
text,
barcodes,
image,
signature,
svg,
line,
table,
list,
rectangle,
ellipse,
dateTime,
date,
time,
select,
checkbox,
radioGroup,
} from '@pdfme/schemas';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const fontDir = path.resolve(__dirname, '../../packages/generator/__tests__/assets/fonts');
const readFont = (fileName) => fs.readFileSync(path.join(fontDir, fileName));
const plugins = {
multiVariableText,
text,
list,
qrcode: barcodes.qrcode,
japanpost: barcodes.japanpost,
ean13: barcodes.ean13,
code128: barcodes.code128,
image,
svg,
line,
table,
rectangle,
ellipse,
dateTime,
date,
time,
select,
checkbox,
radioGroup,
signature,
};
const font = {
...getDefaultFont(),
'PinyonScript-Regular': {
fallback: false,
data: readFont('PinyonScript-Regular.ttf'),
},
NotoSerifJP: {
fallback: false,
data: readFont('NotoSerifJP-Regular.ttf'),
},
NotoSansJP: {
fallback: false,
data: readFont('NotoSansJP-Regular.ttf'),
},
};
const limit = pLimit(4);
function calcHash(content) {
return crypto.createHash('md5').update(content, 'utf8').digest('hex');
}
async function createThumbnailFromTemplate(templatePath, thumbnailPath) {
try {
const templateJsonStr = fs.readFileSync(templatePath, 'utf-8');
const templateJson = JSON.parse(templateJsonStr);
const pdf = await generate({
template: templateJson,
inputs: getInputFromTemplate(templateJson),
options: { font },
plugins,
});
const images = await pdf2img(pdf.buffer, {
imageType: 'png',
range: { end: 1 },
});
const thumbnail = images[0];
fs.writeFileSync(thumbnailPath, Buffer.from(thumbnail));
} catch (err) {
console.error(`Failed to create thumbnail from ${templatePath}:`, err);
throw err;
}
}
async function main() {
const playgroundPath = path.resolve(__dirname, '..');
const templatesPath = path.join(playgroundPath, 'public', 'template-assets');
const hashMapPath = path.join(__dirname, 'thumbnail-hash-map.json');
let hashMap = {};
if (fs.existsSync(hashMapPath)) {
try {
hashMap = JSON.parse(fs.readFileSync(hashMapPath, 'utf-8'));
} catch (error) {
console.warn('Failed to parse thumbnail-hash-map.json. Initializing empty map.');
}
}
const dirs = fs
.readdirSync(templatesPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
const processDir = async (dir) => {
const templateJsonPath = path.join(templatesPath, dir, 'template.json');
if (!fs.existsSync(templateJsonPath)) {
return;
}
const templateJsonStr = fs.readFileSync(templateJsonPath, 'utf-8');
const currentHash = calcHash(templateJsonStr);
if (hashMap[dir] && hashMap[dir] === currentHash) {
console.log(`No changes in ${dir}. Skipping thumbnail generation.`);
return;
}
const thumbnailPngPath = path.join(templatesPath, dir, 'thumbnail.png');
await createThumbnailFromTemplate(templateJsonPath, thumbnailPngPath);
hashMap[dir] = currentHash;
console.log(`Generated thumbnail for ${dir}.`);
};
await Promise.all(dirs.map((dir) => limit(() => processDir(dir))));
fs.writeFileSync(hashMapPath, JSON.stringify(hashMap, null, 2), 'utf-8');
console.log('Thumbnails generation process completed!');
}
main().catch((err) => {
console.error(err);
process.exit(1);
});