refactor: complete three-layer serial refactoring (knowledge→schema→renderer)

Step 1 — Knowledge base: perfect all reference/*.md with type, range, default, unit
- text.md: add DataOrPathProps, path/data, bullet deprecated params
- chart.md: add deprecated section, fix 19 params with missing elements
- slide.md: add bkgd→background deprecated mapping
- output.md: add masterSlide, presLayout

Step 2 — Schema: align schema/presentation.schema.json with KB
- Add catAxisItem and valAxisItem definitions with full sub-property constraints
- Fix 7 enum constraints (barDir, displayBlanksAs, bar3DShape, etc.)
- Add 4 missing fields (verbose, autoPageCharWeight, autoPageLineWeight, notes,
  masterSlide, presLayout)

Step 3 — Translation engine: transform renderers from passthrough to validation
- index.js: add normalizePosition, normalizeColor, validateEnum shared utilities
- All renderers: position validation, color hex normalization, enum validation
- Image: graceful failure handling (catch load errors, warn, continue)
- Chart: chartType enum validation, data parity check
- Table: colspan/rowspan integer validation, border array check
- Full verification: basic.json and full.json generate valid PPTX

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 13:10:01 +08:00
co-authored by Claude Sonnet 5
parent c1a9c2fcda
commit ec7a44d6fb
12 changed files with 538 additions and 21 deletions
+43 -1
View File
@@ -1,5 +1,47 @@
var { normalizePosition, normalizeColor, normalizeColorInOptions, validateEnum } = require('./index');
function renderText(slide, textData) {
slide.addText(textData.text, textData.options);
var opts = {};
if (textData.options) {
for (var k in textData.options) {
if (textData.options.hasOwnProperty(k)) opts[k] = textData.options[k];
}
}
normalizePosition(opts);
normalizeColorInOptions(opts, 'color');
normalizeColorInOptions(opts, 'highlight');
if (opts.underline && typeof opts.underline === 'object') {
normalizeColorInOptions(opts.underline, 'color');
}
if (opts.bullet && typeof opts.bullet === 'object') {
normalizeColorInOptions(opts.bullet, 'color');
}
if (opts.glow && typeof opts.glow === 'object') {
normalizeColorInOptions(opts.glow, 'color');
}
if (opts.outline && typeof opts.outline === 'object') {
normalizeColorInOptions(opts.outline, 'color');
}
validateEnum(opts.align, ['left', 'center', 'right', 'justify'], 'align');
validateEnum(opts.valign, ['top', 'middle', 'bottom'], 'valign');
validateEnum(opts.textDirection, ['horz', 'vert', 'vert270', 'wordArtVert'], 'textDirection');
validateEnum(opts.vert, ['eaVert', 'horz', 'mongolianVert', 'vert', 'vert270', 'wordArtVert', 'wordArtVertRtl'], 'vert');
validateEnum(opts.fit, ['none', 'shrink', 'resize'], 'fit');
if (typeof opts.strike === 'string') {
validateEnum(opts.strike, ['dblStrike', 'sngStrike'], 'strike');
}
if (opts.underline && opts.underline.style !== undefined) {
validateEnum(opts.underline.style, ['dash', 'dashHeavy', 'dashLong', 'dashLongHeavy', 'dbl', 'dotDash', 'dotDashHeave', 'dotDotDash', 'dotDotDashHeavy', 'dotted', 'dottedHeavy', 'heavy', 'none', 'sng', 'wavy', 'wavyDbl', 'wavyHeavy'], 'underline.style');
}
if (opts.bullet && typeof opts.bullet === 'object' && opts.bullet.type !== undefined) {
validateEnum(opts.bullet.type, ['bullet', 'number'], 'bullet.type');
}
slide.addText(textData.text, opts);
}
module.exports = { renderText };