Feature graphic fits the phone: shots.feature.fit 'contain' (default) sizes the device so its rotated box stays inside 1024x500 with a 24 px margin in both templates, 'bleed' keeps the crop, scale 0.5-1.5, config validated; shots render records measured device boxes (report.json devices) and shots check fails a feature graphic whose device leaves the canvas (warns for a screenshot's front device off a side or the top); tests/feature-fit.test.ts (rotated-box geometry, both templates rendered at 1024x500, bleeding fixture) (pitfall 55)
This commit is contained in:
parent
6e2dbf03cc
commit
19d34286d5
15 changed files with 239 additions and 12 deletions
|
|
@ -3,13 +3,31 @@
|
|||
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { PLAY_TABLET_SIZES, SIZES } from './lib';
|
||||
import { boxOutside, type DeviceBox, PLAY_TABLET_SIZES, SIZES } from './lib';
|
||||
import { fontIssues, type FontUsage } from './glyphs';
|
||||
import { checkCount, checkImage, checkVideo, type ImageInfo, imageInfo, type Issue, IMAGE_SPECS, parseProbe, PREVIEW_RULES, type ProbeInfo, VIDEO_TARGETS } from './spec';
|
||||
|
||||
/** <renders>/report.json: path relative to the renders dir -> flags found while rendering. missing = 404s (frames);
|
||||
* fonts = which face drew the text (glyphs.ts FontUsage): automatic fallbacks and system-font glyphs are errors. */
|
||||
export type RenderReport = Record<string, { overflow: string[]; missing?: string[]; fonts?: FontUsage }>;
|
||||
export type RenderReport = Record<string, { overflow: string[]; missing?: string[]; fonts?: FontUsage;
|
||||
/** Device frames' bounding boxes (the rotated outline, from the page's layout) in image px; bleed = cropped on purpose. */
|
||||
devices?: DeviceBox[]; bleed?: boolean }>;
|
||||
|
||||
/** Device frames that leave the image. Feature graphic: an error unless shots.feature.fit is 'bleed' (Play shows the
|
||||
* phone cut off). Screenshots: the templates let the phone run off the bottom by design, so only top / left / right
|
||||
* count, as a warning. */
|
||||
export function deviceIssues(entry: RenderReport[string] | undefined, size: string, W: number, H: number): Issue[] {
|
||||
if (!entry?.devices?.length || entry.bleed) return [];
|
||||
const feature = !!SIZES[size]?.feature;
|
||||
return entry.devices.flatMap((b, i) => {
|
||||
const sides = boxOutside(b, W, H).filter(s => feature || !s.startsWith('bottom'));
|
||||
if (!sides.length) return [];
|
||||
const which = entry.devices!.length > 1 ? `device ${i + 1}` : 'the device';
|
||||
return [feature
|
||||
? { level: 'error' as const, code: 'device-crop', msg: `${which} is cut off by the canvas (${sides.join(', ')}): render again (shots.feature.fit 'contain' fits it) or set fit 'bleed' if the crop is intended` }
|
||||
: { level: 'warn' as const, code: 'device-crop', msg: `${which} runs off the image (${sides.join(', ')}): check the scene's frame x / dy / scale / rotate` }];
|
||||
});
|
||||
}
|
||||
|
||||
export function readReport(dir: string): RenderReport {
|
||||
try { return JSON.parse(readFileSync(join(dir, 'report.json'), 'utf8')) as RenderReport; } catch { return {}; }
|
||||
|
|
@ -54,6 +72,7 @@ export function scanRenders(dir: string, o: { langs: string[]; variants?: string
|
|||
const entry = report[rel.replace(/\.jpe?g$/i, '.png')];
|
||||
for (const msg of entry?.overflow ?? []) issues.push({ level: 'warn', code: 'overflow', msg });
|
||||
issues.push(...fontIssues(entry?.fonts));
|
||||
issues.push(...deviceIssues(entry, size, info.w, info.h));
|
||||
if (entry && !entry.fonts) issues.push({ level: 'warn', code: 'fonts', msg: 'font usage not recorded (rendered by an older storebound): run shots fonts + shots render again so missing glyphs are caught' });
|
||||
out.images.push({ rel, lang, variant, size, file, info, issues });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue