| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- import fs from 'node:fs/promises';
- import path from 'node:path';
- import sharp from 'sharp';
- const buildDir = 'C:/Users/30214/Desktop/sub2本地/sub2新站/.codex-artifacts/juheai-astra-20260915';
- const revision = process.argv[2] || '初稿';
- const metrics = JSON.parse(await fs.readFile(path.join(buildDir, `text-metrics-${revision}.json`), 'utf8'));
- const checks = [];
- const failures = [];
- const collisions = [];
- for (let slideNumber = 1; slideNumber <= 10; slideNumber++) {
- const file = path.join(buildDir, `rendered-${revision}`, `slide-${slideNumber}.png`);
- const { data, info } = await sharp(file).removeAlpha().raw().toBuffer({ resolveWithObject: true });
- const scaleX = info.width / 1280;
- const scaleY = info.height / 720;
- const slideTexts = metrics.filter(x => x.slide === slideNumber);
- for (const item of slideTexts) {
- const expected = [1, 3, 5].map(i => Number.parseInt(item.color.slice(i, i + 2), 16));
- const x1 = Math.max(0, Math.floor(item.left * scaleX));
- const y1 = Math.max(0, Math.floor(item.top * scaleY));
- const x2 = Math.min(info.width, Math.ceil((item.left + item.width) * scaleX));
- const y2 = Math.min(info.height, Math.ceil((item.top + item.height) * scaleY));
- let pixelsWithTextColor = 0;
- for (let y = y1; y < y2; y++) {
- for (let x = x1; x < x2; x++) {
- const offset = (y * info.width + x) * info.channels;
- if (Math.abs(data[offset] - expected[0]) < 28 && Math.abs(data[offset + 1] - expected[1]) < 28 && Math.abs(data[offset + 2] - expected[2]) < 28) pixelsWithTextColor++;
- }
- }
- const pass = pixelsWithTextColor >= 12;
- checks.push({ slide: slideNumber, name: item.name, text: item.value, pixelsWithTextColor, pass });
- if (!pass) failures.push({ slide: slideNumber, name: item.name, reason: 'No visible text-color pixels' });
- }
- const boxes = slideTexts.map(item => ({
- name: item.name,
- left: item.name.startsWith('folio-') ? item.left + item.width - item.measuredWidth : item.left,
- right: item.name.startsWith('folio-') ? item.left + item.width : item.left + item.measuredWidth,
- top: item.top + (item.height - item.measuredHeight) / 2,
- bottom: item.top + (item.height + item.measuredHeight) / 2,
- }));
- for (let a = 0; a < boxes.length; a++) {
- for (let b = a + 1; b < boxes.length; b++) {
- const x = Math.min(boxes[a].right, boxes[b].right) - Math.max(boxes[a].left, boxes[b].left);
- const y = Math.min(boxes[a].bottom, boxes[b].bottom) - Math.max(boxes[a].top, boxes[b].top);
- if (x > 1 && y > 1) collisions.push({ slide: slideNumber, first: boxes[a].name, second: boxes[b].name, overlapWidth: x, overlapHeight: y });
- }
- }
- }
- const result = { claimBoundary: 'Pixel presence and measured text geometry only. No human visual review or OCR.', slideCount: 10, textRegionsChecked: checks.length, failures, collisions, checks };
- await fs.writeFile(path.join(buildDir, `render-check-${revision}.json`), JSON.stringify(result, null, 2));
- console.log(JSON.stringify({ slideCount: 10, textRegionsChecked: checks.length, failures, collisions }, null, 2));
- if (failures.length || collisions.length) process.exitCode = 1;
|