@hirokisakabe/pom 9.1.1 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/buildContext.js +2 -6
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts +2 -1
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +4 -9
- package/dist/buildPptx.js.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/registry/definitions/icon.js +1 -1
- package/dist/registry/definitions/list.js.map +1 -1
- package/dist/renderPptx/nodes/chart.js +172 -47
- package/dist/renderPptx/nodes/chart.js.map +1 -1
- package/dist/renderPptx/nodes/flow.js +98 -90
- package/dist/renderPptx/nodes/flow.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +36 -24
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/image.js +5 -22
- package/dist/renderPptx/nodes/image.js.map +1 -1
- package/dist/renderPptx/nodes/list.js +94 -111
- package/dist/renderPptx/nodes/list.js.map +1 -1
- package/dist/renderPptx/nodes/matrix.js +95 -70
- package/dist/renderPptx/nodes/matrix.js.map +1 -1
- package/dist/renderPptx/nodes/processArrow.js +74 -35
- package/dist/renderPptx/nodes/processArrow.js.map +1 -1
- package/dist/renderPptx/nodes/pyramid.js +37 -16
- package/dist/renderPptx/nodes/pyramid.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +66 -37
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/svg.js +2 -5
- package/dist/renderPptx/nodes/svg.js.map +1 -1
- package/dist/renderPptx/nodes/table.js +89 -59
- package/dist/renderPptx/nodes/table.js.map +1 -1
- package/dist/renderPptx/nodes/text.js +1 -18
- package/dist/renderPptx/nodes/text.js.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +114 -74
- package/dist/renderPptx/nodes/timeline.js.map +1 -1
- package/dist/renderPptx/nodes/tree.js +81 -79
- package/dist/renderPptx/nodes/tree.js.map +1 -1
- package/dist/renderPptx/pptxAuthoring.js +381 -0
- package/dist/renderPptx/pptxAuthoring.js.map +1 -0
- package/dist/renderPptx/renderPptx.js +158 -128
- package/dist/renderPptx/renderPptx.js.map +1 -1
- package/dist/renderPptx/textOptions.js +7 -8
- package/dist/renderPptx/textOptions.js.map +1 -1
- package/dist/renderPptx/units.js +3 -3
- package/dist/renderPptx/units.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +70 -48
- package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
- package/dist/renderPptx/utils/contentArea.js +1 -1
- package/dist/renderPptx/utils/contentArea.js.map +1 -1
- package/dist/renderPptx/utils/glimpsePicture.js +111 -0
- package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
- package/dist/renderPptx/utils/glimpseShape.js +71 -0
- package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
- package/dist/renderPptx/utils/glimpseTextBox.js +107 -0
- package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
- package/dist/renderPptx/utils/straightLine.js +33 -15
- package/dist/renderPptx/utils/straightLine.js.map +1 -1
- package/dist/renderPptx/utils/visualStyle.js +3 -56
- package/dist/renderPptx/utils/visualStyle.js.map +1 -1
- package/dist/renderPptx/writablePptx.d.ts +23 -0
- package/dist/renderPptx/writablePptx.d.ts.map +1 -0
- package/dist/renderPptx/writablePptx.js +74 -0
- package/dist/renderPptx/writablePptx.js.map +1 -0
- package/dist/shared/measureImage.js.map +1 -1
- package/package.json +4 -5
- package/dist/renderPptx/glimpseTextBoxes.js +0 -347
- package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
- package/dist/renderPptx/glowEffects.js +0 -157
- package/dist/renderPptx/glowEffects.js.map +0 -1
- package/dist/renderPptx/gradientFills.js +0 -154
- package/dist/renderPptx/gradientFills.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pptxAuthoring.js","names":[],"sources":["../../src/renderPptx/pptxAuthoring.ts"],"sourcesContent":["import {\n addChart,\n addPicture,\n addShape,\n addTable,\n addTextBox,\n asEmu,\n asHundredthPt,\n asOoxmlAngle,\n asOoxmlPercent,\n asPt,\n createPptx,\n setSlideBackground,\n type AddChartInput,\n type AddPictureInput,\n type AddShapeColorInput,\n type AddShapeCustomGeometryPathCommandInput,\n type AddShapeEffectsInput,\n type AddShapeFillInput,\n type AddShapeGeometryInput,\n type AddShapeInput,\n type AddShapeOutlineInput,\n type AddTableInput,\n type AddTextBoxGradientFillInput,\n type AddTextBoxInput,\n type AddTextBoxParagraphInput,\n type AddTextBoxRunPropertiesInput,\n type PptxSourceModel,\n type SourceHandle,\n} from \"@pptx-glimpse/document\";\nimport { parseGradient, parseLinearGradient } from \"../shared/gradient.ts\";\nimport type {\n BorderStyle,\n PositionedNode,\n ShadowStyle,\n TextGlow,\n TextOutline,\n Underline,\n} from \"../types.ts\";\nimport { createTextOptions, resolveSubSup } from \"./textOptions.ts\";\nimport { EMU_PER_IN, pxToEmu, pxToPt } from \"./units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\ninterface GlimpseTextRun {\n text: string;\n properties: AddTextBoxRunPropertiesInput;\n href?: string;\n}\n\nexport function cleanHex(color: string | undefined): string | undefined {\n const hex = color?.replace(/^#/, \"\").toUpperCase();\n return hex?.length === 3\n ? [...hex].map((character) => character.repeat(2)).join(\"\")\n : hex;\n}\n\nexport function toColorInput(\n color: string | undefined,\n opacity?: number,\n): AddShapeColorInput | undefined {\n const hex = cleanHex(color);\n if (!hex) return undefined;\n return {\n kind: \"srgb\",\n hex,\n transforms:\n opacity === undefined\n ? undefined\n : [\n {\n kind: \"alpha\",\n value: asOoxmlPercent(Math.round(opacity * 100000)),\n },\n ],\n };\n}\n\nfunction toUnderlineInput(underline: Underline | undefined) {\n if (underline === undefined || underline === false) return undefined;\n if (underline === true) return true;\n return {\n style: underline.style,\n color: toColorInput(underline.color),\n };\n}\n\nfunction toBaselineInput(\n subscript: boolean | undefined,\n superscript: boolean | undefined,\n) {\n if (subscript) {\n return {\n type: \"percent\" as const,\n value: asOoxmlPercent(-40000),\n };\n }\n if (superscript) {\n return {\n type: \"percent\" as const,\n value: asOoxmlPercent(30000),\n };\n }\n return undefined;\n}\n\nfunction toGlowInput(glow: TextGlow | undefined) {\n if (!glow) return undefined;\n return {\n radius: asEmu(Math.round(pxToEmu(glow.size ?? 8))),\n color: toColorInput(glow.color ?? \"FFFFFF\", glow.opacity ?? 0.75)!,\n };\n}\n\nfunction toOutlineInput(outline: TextOutline | undefined) {\n if (!outline) return undefined;\n return {\n width: asEmu(Math.round(pxToEmu(outline.size ?? 1))),\n color: toColorInput(outline.color ?? \"FFFFFF\"),\n };\n}\n\nfunction toCharSpacing(letterSpacingPx: number | undefined) {\n if (letterSpacingPx === undefined) return undefined;\n return Math.round(pxToPt(letterSpacingPx) * 100);\n}\n\nfunction toTextGradientInput(\n value: string | undefined,\n): AddTextBoxGradientFillInput | undefined {\n if (!value) return undefined;\n const linear = parseLinearGradient(value);\n if (!linear) return undefined;\n const dmlAngle = (((linear.angle - 90) % 360) + 360) % 360;\n return {\n gradientType: \"linear\",\n angle: asOoxmlAngle(Math.round(dmlAngle * 60000)),\n stops: linear.stops.map((stop) => ({\n position: asOoxmlPercent(Math.round(stop.position * 1000)),\n color: toColorInput(stop.color)!,\n })),\n };\n}\n\nfunction stripUndefined<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\nfunction resolveUnderline(\n node: TextPositionedNode,\n run: NonNullable<TextPositionedNode[\"runs\"]>[number] | undefined,\n): Underline | undefined {\n if (run?.underline !== undefined) return run.underline;\n if (node.underline !== undefined) return node.underline;\n return run?.href ? true : undefined;\n}\n\nfunction buildRunProperties(\n node: TextPositionedNode,\n run: NonNullable<TextPositionedNode[\"runs\"]>[number] | undefined,\n gradientFill: AddTextBoxGradientFillInput | undefined,\n): AddTextBoxRunPropertiesInput {\n const fontSizePx = run?.fontSize ?? node.fontSize ?? 24;\n const subSup = run ? resolveSubSup(run, node) : node;\n return stripUndefined({\n fontFace: run?.fontFamily ?? node.fontFamily ?? \"Noto Sans JP\",\n fontSize: asPt(pxToPt(fontSizePx)),\n color: gradientFill ? undefined : toColorInput(run?.color ?? node.color),\n gradientFill,\n bold: run?.bold ?? node.bold,\n italic: run?.italic ?? node.italic,\n underline: toUnderlineInput(resolveUnderline(node, run)),\n strike: run?.strike ?? node.strike,\n baseline: toBaselineInput(subSup.subscript, subSup.superscript),\n highlight: toColorInput(run?.highlight ?? node.highlight),\n glow: toGlowInput(node.glow),\n outline: toOutlineInput(node.outline),\n charSpacing: toCharSpacing(run?.letterSpacing ?? node.letterSpacing),\n });\n}\n\nfunction buildParagraphs(node: TextPositionedNode): AddTextBoxParagraphInput[] {\n const gradientFill = toTextGradientInput(node.textGradient);\n const sourceRuns: GlimpseTextRun[] =\n node.runs && node.runs.length > 0\n ? node.runs.map((run) => ({\n text: run.text,\n properties: buildRunProperties(node, run, gradientFill),\n href: run.href,\n }))\n : [\n {\n text: node.text ?? \"\",\n properties: buildRunProperties(node, undefined, gradientFill),\n },\n ];\n const paragraphRuns: GlimpseTextRun[][] = [[]];\n for (const run of sourceRuns) {\n const lines = run.text.replace(/\\r*\\n/g, \"\\n\").split(\"\\n\");\n lines.forEach((line, index) => {\n if (index > 0) paragraphRuns.push([]);\n paragraphRuns.at(-1)?.push({ ...run, text: line });\n });\n }\n const lineHeight = node.lineHeight ?? 1.3;\n const fontSizePx = node.fontSize ?? 24;\n return paragraphRuns.map((runs) => ({\n properties: {\n align: node.textAlign,\n marginLeft: asEmu(0),\n indent: asEmu(0),\n bullet: { type: \"none\" },\n lineSpacing: asHundredthPt(\n Math.round(pxToPt(fontSizePx * lineHeight) * 100),\n ),\n },\n runs: runs.map((run) => ({\n text: run.text,\n properties: run.properties,\n hyperlink: run.href,\n })),\n }));\n}\n\nexport type CustomGeometryXmlInput = {\n width: number;\n height: number;\n points: readonly ({ x: number; y: number } | { close: true })[];\n};\n\nexport type GlimpseShapeXmlOptions = {\n fillColor?: string;\n fillOpacity?: number;\n backgroundGradient?: string;\n outlineGradient?: string;\n outlineOpacity?: number;\n glow?: TextGlow;\n shadow?: ShadowStyle;\n rectRadius?: number;\n dashType?: BorderStyle[\"dashType\"];\n flipH?: boolean;\n flipV?: boolean;\n zeroWidth?: boolean;\n zeroHeight?: boolean;\n customGeometry?: CustomGeometryXmlInput;\n};\n\nfunction toGradientFill(\n value: string,\n opacity?: number,\n): AddShapeFillInput | undefined {\n const gradient = parseGradient(value);\n if (!gradient) return undefined;\n const stops = gradient.value.stops.map((stop) => ({\n position: asOoxmlPercent(Math.round(stop.position * 1000)),\n color: toColorInput(stop.color, opacity)!,\n }));\n if (gradient.kind === \"linear\") {\n const dmlAngle = (((gradient.value.angle - 90) % 360) + 360) % 360;\n return {\n kind: \"gradient\",\n gradientType: \"linear\",\n angle: asOoxmlAngle(Math.round(dmlAngle * 60000)),\n stops,\n };\n }\n return {\n kind: \"gradient\",\n gradientType: \"radial\",\n centerX: asOoxmlPercent(Math.round(gradient.value.centerX * 1000)),\n centerY: asOoxmlPercent(Math.round(gradient.value.centerY * 1000)),\n stops,\n };\n}\n\nfunction withFillOpacity(\n fill: AddShapeFillInput | undefined,\n opacity: number | undefined,\n): AddShapeFillInput | undefined {\n if (!fill || opacity === undefined || fill.kind !== \"solid\") return fill;\n return {\n ...fill,\n color: toColorInput(fill.color.hex, opacity)!,\n };\n}\n\nfunction toShadowEffects(\n shadow: ShadowStyle | undefined,\n): AddShapeEffectsInput | undefined {\n if (!shadow) return undefined;\n const common = {\n blurRadius: asEmu(Math.round((shadow.blur ?? 3) * 12700)),\n distance: asEmu(Math.round((shadow.offset ?? 23000 / 12700) * 12700)),\n direction: asOoxmlAngle(Math.round((shadow.angle ?? 90) * 60000)),\n color: toColorInput(shadow.color ?? \"000000\", shadow.opacity ?? 0.35)!,\n };\n return shadow.type === \"inner\"\n ? { innerShadow: common }\n : {\n outerShadow: {\n ...common,\n alignment: \"bl\",\n rotateWithShape: true,\n },\n };\n}\n\nfunction customGeometry(\n input: CustomGeometryXmlInput,\n): AddShapeGeometryInput | undefined {\n const commands: AddShapeCustomGeometryPathCommandInput[] = [];\n let moved = false;\n for (const point of input.points) {\n if (\"close\" in point) {\n if (moved) commands.push({ kind: \"close\" });\n continue;\n }\n commands.push({\n kind: moved ? \"lineTo\" : \"moveTo\",\n x: Math.round(point.x * EMU_PER_IN),\n y: Math.round(point.y * EMU_PER_IN),\n });\n moved = true;\n }\n if (!moved) return undefined;\n return {\n kind: \"custom\",\n paths: [\n {\n width: Math.round(input.width * EMU_PER_IN),\n height: Math.round(input.height * EMU_PER_IN),\n commands,\n },\n ],\n };\n}\n\nfunction enrichShapeInput(\n input: AddShapeInput,\n options: GlimpseShapeXmlOptions | undefined,\n): AddShapeInput {\n const preset = input.geometry.kind === \"preset\" ? input.geometry.preset : \"\";\n const geometry = options?.customGeometry\n ? (customGeometry(options.customGeometry) ?? input.geometry)\n : preset === \"roundRect\" && options?.rectRadius !== undefined\n ? {\n ...input.geometry,\n adjustValues: {\n adj: Math.round(options.rectRadius * 50000),\n },\n }\n : input.geometry;\n const gradientFill = options?.backgroundGradient\n ? toGradientFill(options.backgroundGradient, options.fillOpacity)\n : undefined;\n const outlineGradient = options?.outlineGradient\n ? toGradientFill(\n options.outlineGradient,\n options.outlineOpacity ?? options.fillOpacity,\n )\n : undefined;\n const outline: AddShapeOutlineInput | undefined = input.outline\n ? {\n ...input.outline,\n fill:\n outlineGradient ??\n withFillOpacity(input.outline.fill, options?.outlineOpacity),\n dash: options?.dashType ?? input.outline.dash,\n }\n : outlineGradient\n ? { fill: outlineGradient }\n : undefined;\n const glow = options?.glow\n ? {\n radius: asEmu(Math.round(pxToEmu(options.glow.size ?? 8))),\n color: toColorInput(\n options.glow.color ?? \"FFFFFF\",\n options.glow.opacity ?? 0.75,\n )!,\n }\n : input.effects?.glow;\n const effects: AddShapeEffectsInput = {\n ...input.effects,\n ...toShadowEffects(options?.shadow),\n ...(glow ? { glow } : {}),\n };\n const width =\n preset === \"line\" && options?.zeroWidth ? asEmu(0) : input.width;\n let height =\n preset === \"line\" && options?.zeroHeight ? asEmu(0) : input.height;\n if (preset === \"line\" && width === 0 && height === 0) height = asEmu(1);\n return {\n ...input,\n geometry,\n width,\n height,\n fill: gradientFill ?? withFillOpacity(input.fill, options?.fillOpacity),\n outline,\n effects: Object.keys(effects).length > 0 ? effects : undefined,\n flipHorizontal: options?.flipH ?? input.flipHorizontal,\n flipVertical: options?.flipV ?? input.flipVertical,\n };\n}\n\nfunction applyHyperlinks(\n input: AddTextBoxInput,\n hyperlinks: readonly (string | undefined)[] | undefined,\n): AddTextBoxInput {\n if (!hyperlinks?.some(Boolean) || !input.paragraphs) return input;\n let index = 0;\n return {\n ...input,\n paragraphs: input.paragraphs.map((paragraph) => ({\n ...paragraph,\n runs: paragraph.runs.map((run) => ({\n ...run,\n hyperlink: hyperlinks[index++],\n })),\n })),\n };\n}\n\nexport class PptxAuthoringRegistry {\n private currentSource: PptxSourceModel | undefined;\n private currentSlideHandle: SourceHandle | undefined;\n private useLayoutTextMargins = false;\n private textCount = 0;\n private shapeCount = 0;\n private pictureCount = 0;\n private tableCount = 0;\n private chartCount = 0;\n\n constructor() {\n const source = createPptx();\n this.currentSource = source;\n this.currentSlideHandle = source.slides[0]?.handle;\n }\n\n initialize(source: PptxSourceModel, useLayoutTextMargins = false): void {\n this.currentSource = source;\n this.useLayoutTextMargins = useLayoutTextMargins;\n }\n\n selectSlide(handle: SourceHandle): void {\n this.currentSlideHandle = handle;\n }\n\n get source(): PptxSourceModel {\n if (!this.currentSource)\n throw new Error(\"glimpse authoring is not initialized\");\n return this.currentSource;\n }\n\n get entries(): readonly { kind: \"shape\"; xml: string }[] {\n return (this.source.edits ?? []).flatMap((edit) =>\n (edit.kind === \"addTextBox\" || edit.kind === \"addShape\") && \"xml\" in edit\n ? [{ kind: \"shape\" as const, xml: edit.xml }]\n : [],\n );\n }\n\n replaceSource(source: PptxSourceModel): void {\n this.currentSource = source;\n }\n\n private get target(): SourceHandle {\n if (!this.currentSlideHandle)\n throw new Error(\"glimpse slide is not selected\");\n return this.currentSlideHandle;\n }\n\n register(node: TextPositionedNode): void {\n const textOptions = createTextOptions(node);\n this.registerTextBox({\n offsetX: asEmu(Math.round(textOptions.x * EMU_PER_IN)),\n offsetY: asEmu(Math.round(textOptions.y * EMU_PER_IN)),\n width: asEmu(Math.round(textOptions.w * EMU_PER_IN)),\n height: asEmu(Math.round(textOptions.h * EMU_PER_IN)),\n rotation:\n node.rotate === undefined\n ? undefined\n : asOoxmlAngle(Math.round(node.rotate * 60000)),\n body: this.useLayoutTextMargins\n ? undefined\n : {\n anchor: \"top\",\n marginLeft: asEmu(0),\n marginRight: asEmu(0),\n marginTop: asEmu(0),\n marginBottom: asEmu(0),\n },\n paragraphs: buildParagraphs(node),\n });\n }\n\n registerTextBox(\n input: AddTextBoxInput,\n options?: {\n name?: string;\n hyperlinks?: readonly (string | undefined)[];\n },\n ): void {\n const name = options?.name ?? `Text ${++this.textCount}`;\n this.currentSource = addTextBox(\n this.source,\n this.target,\n applyHyperlinks({ ...input, name }, options?.hyperlinks),\n );\n }\n\n registerShape(\n input: AddShapeInput,\n options?: GlimpseShapeXmlOptions & { name?: string },\n ): void {\n const name = options?.name ?? `Shape ${++this.shapeCount}`;\n this.currentSource = addShape(\n this.source,\n this.target,\n enrichShapeInput({ ...input, name }, options),\n );\n }\n\n registerPicture(\n input: AddPictureInput,\n options?: { name?: string; shadow?: ShadowStyle },\n ): void {\n const name = options?.name ?? `Picture ${++this.pictureCount}`;\n this.currentSource = addPicture(this.source, this.target, {\n ...input,\n name,\n effects: toShadowEffects(options?.shadow),\n });\n }\n\n registerTable(input: AddTableInput, options?: { name?: string }): void {\n const name = options?.name ?? `Table ${++this.tableCount}`;\n this.currentSource = addTable(this.source, this.target, { ...input, name });\n }\n\n registerChart(input: AddChartInput, options?: { name?: string }): void {\n const name = options?.name ?? `Chart ${++this.chartCount}`;\n this.currentSource = addChart(this.source, this.target, { ...input, name });\n }\n\n setSlideBackgroundGradient(\n backgroundGradient: string,\n opacity?: number,\n ): boolean {\n const fill = toGradientFill(backgroundGradient, opacity);\n if (!fill || fill.kind !== \"gradient\") return false;\n const background =\n fill.gradientType === \"linear\"\n ? {\n kind: \"gradient\" as const,\n gradientType: \"linear\" as const,\n stops: fill.stops,\n angle: fill.angle ?? asOoxmlAngle(0),\n }\n : {\n kind: \"gradient\" as const,\n gradientType: \"radial\" as const,\n stops: fill.stops,\n centerX: fill.centerX,\n centerY: fill.centerY,\n };\n this.currentSource = setSlideBackground(\n this.source,\n this.target,\n background,\n );\n return true;\n }\n\n setSlideBackgroundSolid(color: string): void {\n this.currentSource = setSlideBackground(this.source, this.target, {\n kind: \"solid\",\n color: toColorInput(color)!,\n });\n }\n\n setSlideBackgroundImage(bytes: Uint8Array): void {\n this.currentSource = setSlideBackground(this.source, this.target, {\n kind: \"image\",\n bytes,\n });\n }\n}\n"],"mappings":";;;;;AAkDA,SAAgB,SAAS,OAA+C;CACtE,MAAM,MAAM,OAAO,QAAQ,MAAM,EAAE,CAAC,CAAC,YAAY;CACjD,OAAO,KAAK,WAAW,IACnB,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,cAAc,UAAU,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,IACxD;AACN;AAEA,SAAgB,aACd,OACA,SACgC;CAChC,MAAM,MAAM,SAAS,KAAK;CAC1B,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,OAAO;EACL,MAAM;EACN;EACA,YACE,YAAY,KAAA,IACR,KAAA,IACA,CACE;GACE,MAAM;GACN,OAAO,eAAe,KAAK,MAAM,UAAU,GAAM,CAAC;EACpD,CACF;CACR;AACF;AAEA,SAAS,iBAAiB,WAAkC;CAC1D,IAAI,cAAc,KAAA,KAAa,cAAc,OAAO,OAAO,KAAA;CAC3D,IAAI,cAAc,MAAM,OAAO;CAC/B,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,aAAa,UAAU,KAAK;CACrC;AACF;AAEA,SAAS,gBACP,WACA,aACA;CACA,IAAI,WACF,OAAO;EACL,MAAM;EACN,OAAO,eAAe,IAAM;CAC9B;CAEF,IAAI,aACF,OAAO;EACL,MAAM;EACN,OAAO,eAAe,GAAK;CAC7B;AAGJ;AAEA,SAAS,YAAY,MAA4B;CAC/C,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,OAAO;EACL,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC;EACjD,OAAO,aAAa,KAAK,SAAS,UAAU,KAAK,WAAW,GAAI;CAClE;AACF;AAEA,SAAS,eAAe,SAAkC;CACxD,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,OAAO;EACL,OAAO,MAAM,KAAK,MAAM,QAAQ,QAAQ,QAAQ,CAAC,CAAC,CAAC;EACnD,OAAO,aAAa,QAAQ,SAAS,QAAQ;CAC/C;AACF;AAEA,SAAS,cAAc,iBAAqC;CAC1D,IAAI,oBAAoB,KAAA,GAAW,OAAO,KAAA;CAC1C,OAAO,KAAK,MAAM,OAAO,eAAe,IAAI,GAAG;AACjD;AAEA,SAAS,oBACP,OACyC;CACzC,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,MAAM,SAAS,oBAAoB,KAAK;CACxC,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,MAAM,aAAc,OAAO,QAAQ,MAAM,MAAO,OAAO;CACvD,OAAO;EACL,cAAc;EACd,OAAO,aAAa,KAAK,MAAM,WAAW,GAAK,CAAC;EAChD,OAAO,OAAO,MAAM,KAAK,UAAU;GACjC,UAAU,eAAe,KAAK,MAAM,KAAK,WAAW,GAAI,CAAC;GACzD,OAAO,aAAa,KAAK,KAAK;EAChC,EAAE;CACJ;AACF;AAEA,SAAS,eAAkD,OAAa;CACtE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;AAEA,SAAS,iBACP,MACA,KACuB;CACvB,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,IAAI;CAC7C,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,KAAK;CAC9C,OAAO,KAAK,OAAO,OAAO,KAAA;AAC5B;AAEA,SAAS,mBACP,MACA,KACA,cAC8B;CAC9B,MAAM,aAAa,KAAK,YAAY,KAAK,YAAY;CACrD,MAAM,SAAS,MAAM,cAAc,KAAK,IAAI,IAAI;CAChD,OAAO,eAAe;EACpB,UAAU,KAAK,cAAc,KAAK,cAAc;EAChD,UAAU,KAAK,OAAO,UAAU,CAAC;EACjC,OAAO,eAAe,KAAA,IAAY,aAAa,KAAK,SAAS,KAAK,KAAK;EACvE;EACA,MAAM,KAAK,QAAQ,KAAK;EACxB,QAAQ,KAAK,UAAU,KAAK;EAC5B,WAAW,iBAAiB,iBAAiB,MAAM,GAAG,CAAC;EACvD,QAAQ,KAAK,UAAU,KAAK;EAC5B,UAAU,gBAAgB,OAAO,WAAW,OAAO,WAAW;EAC9D,WAAW,aAAa,KAAK,aAAa,KAAK,SAAS;EACxD,MAAM,YAAY,KAAK,IAAI;EAC3B,SAAS,eAAe,KAAK,OAAO;EACpC,aAAa,cAAc,KAAK,iBAAiB,KAAK,aAAa;CACrE,CAAC;AACH;AAEA,SAAS,gBAAgB,MAAsD;CAC7E,MAAM,eAAe,oBAAoB,KAAK,YAAY;CAC1D,MAAM,aACJ,KAAK,QAAQ,KAAK,KAAK,SAAS,IAC5B,KAAK,KAAK,KAAK,SAAS;EACtB,MAAM,IAAI;EACV,YAAY,mBAAmB,MAAM,KAAK,YAAY;EACtD,MAAM,IAAI;CACZ,EAAE,IACF,CACE;EACE,MAAM,KAAK,QAAQ;EACnB,YAAY,mBAAmB,MAAM,KAAA,GAAW,YAAY;CAC9D,CACF;CACN,MAAM,gBAAoC,CAAC,CAAC,CAAC;CAC7C,KAAK,MAAM,OAAO,YAEhB,IADkB,KAAK,QAAQ,UAAU,IAAI,CAAC,CAAC,MAAM,IACjD,CAAC,CAAC,SAAS,MAAM,UAAU;EAC7B,IAAI,QAAQ,GAAG,cAAc,KAAK,CAAC,CAAC;EACpC,cAAc,GAAG,EAAE,CAAC,EAAE,KAAK;GAAE,GAAG;GAAK,MAAM;EAAK,CAAC;CACnD,CAAC;CAEH,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,YAAY;CACpC,OAAO,cAAc,KAAK,UAAU;EAClC,YAAY;GACV,OAAO,KAAK;GACZ,YAAY,MAAM,CAAC;GACnB,QAAQ,MAAM,CAAC;GACf,QAAQ,EAAE,MAAM,OAAO;GACvB,aAAa,cACX,KAAK,MAAM,OAAO,aAAa,UAAU,IAAI,GAAG,CAClD;EACF;EACA,MAAM,KAAK,KAAK,SAAS;GACvB,MAAM,IAAI;GACV,YAAY,IAAI;GAChB,WAAW,IAAI;EACjB,EAAE;CACJ,EAAE;AACJ;AAyBA,SAAS,eACP,OACA,SAC+B;CAC/B,MAAM,WAAW,cAAc,KAAK;CACpC,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,MAAM,QAAQ,SAAS,MAAM,MAAM,KAAK,UAAU;EAChD,UAAU,eAAe,KAAK,MAAM,KAAK,WAAW,GAAI,CAAC;EACzD,OAAO,aAAa,KAAK,OAAO,OAAO;CACzC,EAAE;CACF,IAAI,SAAS,SAAS,UAAU;EAC9B,MAAM,aAAc,SAAS,MAAM,QAAQ,MAAM,MAAO,OAAO;EAC/D,OAAO;GACL,MAAM;GACN,cAAc;GACd,OAAO,aAAa,KAAK,MAAM,WAAW,GAAK,CAAC;GAChD;EACF;CACF;CACA,OAAO;EACL,MAAM;EACN,cAAc;EACd,SAAS,eAAe,KAAK,MAAM,SAAS,MAAM,UAAU,GAAI,CAAC;EACjE,SAAS,eAAe,KAAK,MAAM,SAAS,MAAM,UAAU,GAAI,CAAC;EACjE;CACF;AACF;AAEA,SAAS,gBACP,MACA,SAC+B;CAC/B,IAAI,CAAC,QAAQ,YAAY,KAAA,KAAa,KAAK,SAAS,SAAS,OAAO;CACpE,OAAO;EACL,GAAG;EACH,OAAO,aAAa,KAAK,MAAM,KAAK,OAAO;CAC7C;AACF;AAEA,SAAS,gBACP,QACkC;CAClC,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,MAAM,SAAS;EACb,YAAY,MAAM,KAAK,OAAO,OAAO,QAAQ,KAAK,KAAK,CAAC;EACxD,UAAU,MAAM,KAAK,OAAO,OAAO,UAAU,OAAQ,SAAS,KAAK,CAAC;EACpE,WAAW,aAAa,KAAK,OAAO,OAAO,SAAS,MAAM,GAAK,CAAC;EAChE,OAAO,aAAa,OAAO,SAAS,UAAU,OAAO,WAAW,GAAI;CACtE;CACA,OAAO,OAAO,SAAS,UACnB,EAAE,aAAa,OAAO,IACtB,EACE,aAAa;EACX,GAAG;EACH,WAAW;EACX,iBAAiB;CACnB,EACF;AACN;AAEA,SAAS,eACP,OACmC;CACnC,MAAM,WAAqD,CAAC;CAC5D,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,MAAM,QAAQ;EAChC,IAAI,WAAW,OAAO;GACpB,IAAI,OAAO,SAAS,KAAK,EAAE,MAAM,QAAQ,CAAC;GAC1C;EACF;EACA,SAAS,KAAK;GACZ,MAAM,QAAQ,WAAW;GACzB,GAAG,KAAK,MAAM,MAAM,IAAI,UAAU;GAClC,GAAG,KAAK,MAAM,MAAM,IAAI,UAAU;EACpC,CAAC;EACD,QAAQ;CACV;CACA,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,OAAO;EACL,MAAM;EACN,OAAO,CACL;GACE,OAAO,KAAK,MAAM,MAAM,QAAQ,UAAU;GAC1C,QAAQ,KAAK,MAAM,MAAM,SAAS,UAAU;GAC5C;EACF,CACF;CACF;AACF;AAEA,SAAS,iBACP,OACA,SACe;CACf,MAAM,SAAS,MAAM,SAAS,SAAS,WAAW,MAAM,SAAS,SAAS;CAC1E,MAAM,WAAW,SAAS,iBACrB,eAAe,QAAQ,cAAc,KAAK,MAAM,WACjD,WAAW,eAAe,SAAS,eAAe,KAAA,IAChD;EACE,GAAG,MAAM;EACT,cAAc,EACZ,KAAK,KAAK,MAAM,QAAQ,aAAa,GAAK,EAC5C;CACF,IACA,MAAM;CACZ,MAAM,eAAe,SAAS,qBAC1B,eAAe,QAAQ,oBAAoB,QAAQ,WAAW,IAC9D,KAAA;CACJ,MAAM,kBAAkB,SAAS,kBAC7B,eACE,QAAQ,iBACR,QAAQ,kBAAkB,QAAQ,WACpC,IACA,KAAA;CACJ,MAAM,UAA4C,MAAM,UACpD;EACE,GAAG,MAAM;EACT,MACE,mBACA,gBAAgB,MAAM,QAAQ,MAAM,SAAS,cAAc;EAC7D,MAAM,SAAS,YAAY,MAAM,QAAQ;CAC3C,IACA,kBACE,EAAE,MAAM,gBAAgB,IACxB,KAAA;CACN,MAAM,OAAO,SAAS,OAClB;EACE,QAAQ,MAAM,KAAK,MAAM,QAAQ,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC;EACzD,OAAO,aACL,QAAQ,KAAK,SAAS,UACtB,QAAQ,KAAK,WAAW,GAC1B;CACF,IACA,MAAM,SAAS;CACnB,MAAM,UAAgC;EACpC,GAAG,MAAM;EACT,GAAG,gBAAgB,SAAS,MAAM;EAClC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;CACzB;CACA,MAAM,QACJ,WAAW,UAAU,SAAS,YAAY,MAAM,CAAC,IAAI,MAAM;CAC7D,IAAI,SACF,WAAW,UAAU,SAAS,aAAa,MAAM,CAAC,IAAI,MAAM;CAC9D,IAAI,WAAW,UAAU,UAAU,KAAK,WAAW,GAAG,SAAS,MAAM,CAAC;CACtE,OAAO;EACL,GAAG;EACH;EACA;EACA;EACA,MAAM,gBAAgB,gBAAgB,MAAM,MAAM,SAAS,WAAW;EACtE;EACA,SAAS,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,IAAI,UAAU,KAAA;EACrD,gBAAgB,SAAS,SAAS,MAAM;EACxC,cAAc,SAAS,SAAS,MAAM;CACxC;AACF;AAEA,SAAS,gBACP,OACA,YACiB;CACjB,IAAI,CAAC,YAAY,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,OAAO;CAC5D,IAAI,QAAQ;CACZ,OAAO;EACL,GAAG;EACH,YAAY,MAAM,WAAW,KAAK,eAAe;GAC/C,GAAG;GACH,MAAM,UAAU,KAAK,KAAK,SAAS;IACjC,GAAG;IACH,WAAW,WAAW;GACxB,EAAE;EACJ,EAAE;CACJ;AACF;AAEA,IAAa,wBAAb,MAAmC;CACjC;CACA;CACA,uBAA+B;CAC/B,YAAoB;CACpB,aAAqB;CACrB,eAAuB;CACvB,aAAqB;CACrB,aAAqB;CAErB,cAAc;EACZ,MAAM,SAAS,WAAW;EAC1B,KAAK,gBAAgB;EACrB,KAAK,qBAAqB,OAAO,OAAO,EAAE,EAAE;CAC9C;CAEA,WAAW,QAAyB,uBAAuB,OAAa;EACtE,KAAK,gBAAgB;EACrB,KAAK,uBAAuB;CAC9B;CAEA,YAAY,QAA4B;EACtC,KAAK,qBAAqB;CAC5B;CAEA,IAAI,SAA0B;EAC5B,IAAI,CAAC,KAAK,eACR,MAAM,IAAI,MAAM,sCAAsC;EACxD,OAAO,KAAK;CACd;CAEA,IAAI,UAAqD;EACvD,QAAQ,KAAK,OAAO,SAAS,CAAC,EAAA,CAAG,SAAS,UACvC,KAAK,SAAS,gBAAgB,KAAK,SAAS,eAAe,SAAS,OACjE,CAAC;GAAE,MAAM;GAAkB,KAAK,KAAK;EAAI,CAAC,IAC1C,CAAC,CACP;CACF;CAEA,cAAc,QAA+B;EAC3C,KAAK,gBAAgB;CACvB;CAEA,IAAY,SAAuB;EACjC,IAAI,CAAC,KAAK,oBACR,MAAM,IAAI,MAAM,+BAA+B;EACjD,OAAO,KAAK;CACd;CAEA,SAAS,MAAgC;EACvC,MAAM,cAAc,kBAAkB,IAAI;EAC1C,KAAK,gBAAgB;GACnB,SAAS,MAAM,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC;GACrD,SAAS,MAAM,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC;GACrD,OAAO,MAAM,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC;GACnD,QAAQ,MAAM,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC;GACpD,UACE,KAAK,WAAW,KAAA,IACZ,KAAA,IACA,aAAa,KAAK,MAAM,KAAK,SAAS,GAAK,CAAC;GAClD,MAAM,KAAK,uBACP,KAAA,IACA;IACE,QAAQ;IACR,YAAY,MAAM,CAAC;IACnB,aAAa,MAAM,CAAC;IACpB,WAAW,MAAM,CAAC;IAClB,cAAc,MAAM,CAAC;GACvB;GACJ,YAAY,gBAAgB,IAAI;EAClC,CAAC;CACH;CAEA,gBACE,OACA,SAIM;EACN,MAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE,KAAK;EAC7C,KAAK,gBAAgB,WACnB,KAAK,QACL,KAAK,QACL,gBAAgB;GAAE,GAAG;GAAO;EAAK,GAAG,SAAS,UAAU,CACzD;CACF;CAEA,cACE,OACA,SACM;EACN,MAAM,OAAO,SAAS,QAAQ,SAAS,EAAE,KAAK;EAC9C,KAAK,gBAAgB,SACnB,KAAK,QACL,KAAK,QACL,iBAAiB;GAAE,GAAG;GAAO;EAAK,GAAG,OAAO,CAC9C;CACF;CAEA,gBACE,OACA,SACM;EACN,MAAM,OAAO,SAAS,QAAQ,WAAW,EAAE,KAAK;EAChD,KAAK,gBAAgB,WAAW,KAAK,QAAQ,KAAK,QAAQ;GACxD,GAAG;GACH;GACA,SAAS,gBAAgB,SAAS,MAAM;EAC1C,CAAC;CACH;CAEA,cAAc,OAAsB,SAAmC;EACrE,MAAM,OAAO,SAAS,QAAQ,SAAS,EAAE,KAAK;EAC9C,KAAK,gBAAgB,SAAS,KAAK,QAAQ,KAAK,QAAQ;GAAE,GAAG;GAAO;EAAK,CAAC;CAC5E;CAEA,cAAc,OAAsB,SAAmC;EACrE,MAAM,OAAO,SAAS,QAAQ,SAAS,EAAE,KAAK;EAC9C,KAAK,gBAAgB,SAAS,KAAK,QAAQ,KAAK,QAAQ;GAAE,GAAG;GAAO;EAAK,CAAC;CAC5E;CAEA,2BACE,oBACA,SACS;EACT,MAAM,OAAO,eAAe,oBAAoB,OAAO;EACvD,IAAI,CAAC,QAAQ,KAAK,SAAS,YAAY,OAAO;EAC9C,MAAM,aACJ,KAAK,iBAAiB,WAClB;GACE,MAAM;GACN,cAAc;GACd,OAAO,KAAK;GACZ,OAAO,KAAK,SAAS,aAAa,CAAC;EACrC,IACA;GACE,MAAM;GACN,cAAc;GACd,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EACN,KAAK,gBAAgB,mBACnB,KAAK,QACL,KAAK,QACL,UACF;EACA,OAAO;CACT;CAEA,wBAAwB,OAAqB;EAC3C,KAAK,gBAAgB,mBAAmB,KAAK,QAAQ,KAAK,QAAQ;GAChE,MAAM;GACN,OAAO,aAAa,KAAK;EAC3B,CAAC;CACH;CAEA,wBAAwB,OAAyB;EAC/C,KAAK,gBAAgB,mBAAmB,KAAK,QAAQ,KAAK,QAAQ;GAChE,MAAM;GACN;EACF,CAAC;CACH;AACF"}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { getImageData } from "../shared/measureImage.js";
|
|
2
2
|
import { resolveBoxSpacing } from "../shared/boxSpacing.js";
|
|
3
3
|
import { getNodeDef } from "../registry/nodeRegistry.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { pxToEmu, pxToPt } from "./units.js";
|
|
5
|
+
import { toColorInput } from "./pptxAuthoring.js";
|
|
6
|
+
import { createGlimpseRunProperties } from "./utils/glimpseTextBox.js";
|
|
7
|
+
import { imageBytesFromSource } from "./utils/glimpsePicture.js";
|
|
8
|
+
import { createShapeBoundsInput, noneShapeFill, shapeOutline, solidShapeFill } from "./utils/glimpseShape.js";
|
|
7
9
|
import "../registry/index.js";
|
|
8
10
|
import { renderBackgroundAndBorder, renderBorderOnly } from "./utils/backgroundBorder.js";
|
|
11
|
+
import { createWritablePptx } from "./writablePptx.js";
|
|
12
|
+
import { addEmptySlideFromLayout, addSlideNumber, asEmu, asPt, createPptx } from "@pptx-glimpse/document";
|
|
9
13
|
//#region src/renderPptx/renderPptx.ts
|
|
10
|
-
async function loadPptxGenJS() {
|
|
11
|
-
const mod = await import("pptxgenjs");
|
|
12
|
-
return mod.default?.default ?? mod.default ?? mod;
|
|
13
|
-
}
|
|
14
|
-
const DEFAULT_MASTER_NAME = "POM_MASTER";
|
|
15
14
|
function buildIdPositionMap(node, diagnostics) {
|
|
16
15
|
const map = /* @__PURE__ */ new Map();
|
|
17
16
|
function traverse(n) {
|
|
@@ -35,130 +34,163 @@ function sortByZIndex(children) {
|
|
|
35
34
|
if (children.every((c) => c.zIndex === void 0)) return children;
|
|
36
35
|
return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
italic: obj.italic,
|
|
55
|
-
underline: convertUnderline(obj.underline),
|
|
56
|
-
strike: convertStrike(obj.strike),
|
|
57
|
-
highlight: obj.highlight,
|
|
58
|
-
align: obj.textAlign
|
|
59
|
-
}
|
|
60
|
-
} };
|
|
61
|
-
case "image": {
|
|
62
|
-
const imageProps = {
|
|
63
|
-
x: pxToIn(obj.x),
|
|
64
|
-
y: pxToIn(obj.y),
|
|
65
|
-
w: pxToIn(obj.w),
|
|
66
|
-
h: pxToIn(obj.h)
|
|
67
|
-
};
|
|
68
|
-
if (obj.src.startsWith("data:")) imageProps.data = obj.src;
|
|
69
|
-
else imageProps.path = obj.src;
|
|
70
|
-
return { image: imageProps };
|
|
71
|
-
}
|
|
72
|
-
case "rect": return { rect: {
|
|
73
|
-
x: pxToIn(obj.x),
|
|
74
|
-
y: pxToIn(obj.y),
|
|
75
|
-
w: pxToIn(obj.w),
|
|
76
|
-
h: pxToIn(obj.h),
|
|
77
|
-
fill: obj.fill ? {
|
|
78
|
-
color: obj.fill.color,
|
|
79
|
-
transparency: obj.fill.transparency
|
|
80
|
-
} : void 0,
|
|
81
|
-
line: obj.border ? {
|
|
82
|
-
color: obj.border.color,
|
|
83
|
-
width: obj.border.width,
|
|
84
|
-
dashType: obj.border.dashType
|
|
85
|
-
} : void 0
|
|
86
|
-
} };
|
|
87
|
-
case "line": return { line: {
|
|
88
|
-
x: pxToIn(obj.x),
|
|
89
|
-
y: pxToIn(obj.y),
|
|
90
|
-
w: pxToIn(obj.w),
|
|
91
|
-
h: pxToIn(obj.h),
|
|
92
|
-
line: obj.line ? {
|
|
93
|
-
color: obj.line.color,
|
|
94
|
-
width: obj.line.width,
|
|
95
|
-
dashType: obj.line.dashType
|
|
96
|
-
} : {
|
|
97
|
-
color: "000000",
|
|
98
|
-
width: 1
|
|
99
|
-
}
|
|
100
|
-
} };
|
|
101
|
-
}
|
|
37
|
+
function masterBackground(master, buildContext) {
|
|
38
|
+
const background = master?.background;
|
|
39
|
+
if (!background) return void 0;
|
|
40
|
+
if ("color" in background) return {
|
|
41
|
+
kind: "solid",
|
|
42
|
+
color: toColorInput(background.color)
|
|
43
|
+
};
|
|
44
|
+
if ("data" in background) return {
|
|
45
|
+
kind: "image",
|
|
46
|
+
bytes: imageBytesFromSource("", background.data)
|
|
47
|
+
};
|
|
48
|
+
const src = "path" in background ? background.path : background.image;
|
|
49
|
+
return {
|
|
50
|
+
kind: "image",
|
|
51
|
+
bytes: imageBytesFromSource(src, getImageData(src, buildContext.imageDataCache))
|
|
52
|
+
};
|
|
102
53
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
function
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
54
|
+
function masterBounds(obj) {
|
|
55
|
+
return createShapeBoundsInput(obj);
|
|
56
|
+
}
|
|
57
|
+
function masterTextRunProperties(obj) {
|
|
58
|
+
const properties = createGlimpseRunProperties({
|
|
59
|
+
fontSize: obj.fontSize,
|
|
60
|
+
fontFace: obj.fontFamily,
|
|
61
|
+
color: obj.color,
|
|
62
|
+
bold: obj.bold,
|
|
63
|
+
italic: obj.italic,
|
|
64
|
+
underline: obj.underline,
|
|
65
|
+
strike: obj.strike,
|
|
66
|
+
highlight: obj.highlight
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
...properties,
|
|
70
|
+
fontFace: obj.fontFamily === void 0 ? void 0 : properties.fontFace,
|
|
71
|
+
fontSize: obj.fontSize === void 0 ? void 0 : properties.fontSize
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function addMasterContent(buildContext, master) {
|
|
75
|
+
const target = buildContext.pptxAuthoring.source.slideMasters[0]?.handle;
|
|
76
|
+
if (!target) throw new Error("createPptx did not create a slide master handle");
|
|
77
|
+
buildContext.pptxAuthoring.selectSlide(target);
|
|
78
|
+
for (const obj of master.objects ?? []) switch (obj.type) {
|
|
79
|
+
case "text":
|
|
80
|
+
buildContext.pptxAuthoring.registerTextBox({
|
|
81
|
+
...masterBounds(obj),
|
|
82
|
+
body: {
|
|
83
|
+
anchor: "middle",
|
|
84
|
+
marginLeft: asEmu(91440),
|
|
85
|
+
marginRight: asEmu(91440),
|
|
86
|
+
marginTop: asEmu(91440),
|
|
87
|
+
marginBottom: asEmu(91440)
|
|
88
|
+
},
|
|
89
|
+
paragraphs: [{
|
|
90
|
+
properties: {
|
|
91
|
+
align: obj.textAlign,
|
|
92
|
+
marginLeft: asEmu(0),
|
|
93
|
+
indent: asEmu(0),
|
|
94
|
+
bullet: { type: "none" }
|
|
95
|
+
},
|
|
96
|
+
runs: [{
|
|
97
|
+
text: obj.text,
|
|
98
|
+
properties: masterTextRunProperties(obj)
|
|
99
|
+
}]
|
|
100
|
+
}]
|
|
101
|
+
});
|
|
102
|
+
break;
|
|
103
|
+
case "image":
|
|
104
|
+
buildContext.pptxAuthoring.registerPicture({
|
|
105
|
+
...masterBounds(obj),
|
|
106
|
+
bytes: imageBytesFromSource(obj.src, getImageData(obj.src, buildContext.imageDataCache))
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
case "rect":
|
|
110
|
+
buildContext.pptxAuthoring.registerShape({
|
|
111
|
+
...masterBounds(obj),
|
|
112
|
+
geometry: {
|
|
113
|
+
kind: "preset",
|
|
114
|
+
preset: "rect"
|
|
115
|
+
},
|
|
116
|
+
fill: obj.fill ? solidShapeFill(obj.fill.color ?? "FFFFFF") : noneShapeFill(),
|
|
117
|
+
outline: obj.border ? shapeOutline(obj.border) : void 0
|
|
118
|
+
}, {
|
|
119
|
+
fillColor: obj.fill?.color,
|
|
120
|
+
fillOpacity: obj.fill?.transparency === void 0 ? void 0 : 1 - obj.fill.transparency / 100
|
|
121
|
+
});
|
|
122
|
+
break;
|
|
123
|
+
case "line":
|
|
124
|
+
buildContext.pptxAuthoring.registerShape({
|
|
125
|
+
...masterBounds(obj),
|
|
126
|
+
geometry: {
|
|
127
|
+
kind: "preset",
|
|
128
|
+
preset: "line"
|
|
129
|
+
},
|
|
130
|
+
fill: noneShapeFill(),
|
|
131
|
+
outline: shapeOutline(obj.line ?? {
|
|
132
|
+
color: "000000",
|
|
133
|
+
width: 1
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
114
137
|
}
|
|
115
|
-
if (master.
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
138
|
+
if (master.slideNumber) {
|
|
139
|
+
const value = master.slideNumber;
|
|
140
|
+
buildContext.pptxAuthoring.replaceSource(addSlideNumber(buildContext.pptxAuthoring.source, target, {
|
|
141
|
+
offsetX: asEmu(Math.round(pxToEmu(value.x))),
|
|
142
|
+
offsetY: asEmu(Math.round(pxToEmu(value.y))),
|
|
143
|
+
width: value.w === void 0 ? asEmu(8e5) : asEmu(Math.round(pxToEmu(value.w))),
|
|
144
|
+
height: value.h === void 0 ? asEmu(3e5) : asEmu(Math.round(pxToEmu(value.h))),
|
|
145
|
+
properties: {
|
|
146
|
+
fontFace: value.fontFamily,
|
|
147
|
+
fontSize: value.fontSize ? asPt(pxToPt(value.fontSize)) : void 0,
|
|
148
|
+
color: toColorInput(value.color)
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
123
151
|
}
|
|
124
|
-
if (master.objects && master.objects.length > 0) masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));
|
|
125
|
-
if (master.slideNumber) masterProps.slideNumber = {
|
|
126
|
-
x: pxToIn(master.slideNumber.x),
|
|
127
|
-
y: pxToIn(master.slideNumber.y),
|
|
128
|
-
w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : void 0,
|
|
129
|
-
h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : void 0,
|
|
130
|
-
fontSize: master.slideNumber.fontSize ? pxToPt(master.slideNumber.fontSize) : void 0,
|
|
131
|
-
fontFace: master.slideNumber.fontFamily,
|
|
132
|
-
color: master.slideNumber.color
|
|
133
|
-
};
|
|
134
|
-
pptx.defineSlideMaster(masterProps);
|
|
135
|
-
return masterName;
|
|
136
152
|
}
|
|
137
153
|
/**
|
|
138
|
-
* PositionedNode ツリーを
|
|
154
|
+
* PositionedNode ツリーを glimpse の PPTX source model に変換する
|
|
139
155
|
* @param pages PositionedNode ツリーの配列(各要素が1ページ)
|
|
140
156
|
* @param slidePx スライド全体のサイズ(px)
|
|
141
157
|
* @param master スライドマスターオプション(省略可能)
|
|
142
|
-
* @returns
|
|
158
|
+
* @returns PPTX の write / writeFile / stream 互換 facade
|
|
143
159
|
*/
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
function renderPptx(pages, slidePx, buildContext, master) {
|
|
161
|
+
const margin = master?.margin === void 0 ? void 0 : resolveBoxSpacing(master.margin);
|
|
162
|
+
let source = createPptx({
|
|
163
|
+
slideSize: {
|
|
164
|
+
width: asEmu(Math.round(pxToEmu(slidePx.w))),
|
|
165
|
+
height: asEmu(Math.round(pxToEmu(slidePx.h)))
|
|
166
|
+
},
|
|
167
|
+
slideMaster: {
|
|
168
|
+
name: master?.title ?? "POM_MASTER",
|
|
169
|
+
background: masterBackground(master, buildContext)
|
|
170
|
+
},
|
|
171
|
+
slideLayout: {
|
|
172
|
+
name: "POM_LAYOUT",
|
|
173
|
+
margin: margin ? {
|
|
174
|
+
top: asEmu(Math.round(pxToEmu(margin.top))),
|
|
175
|
+
right: asEmu(Math.round(pxToEmu(margin.right))),
|
|
176
|
+
bottom: asEmu(Math.round(pxToEmu(margin.bottom))),
|
|
177
|
+
left: asEmu(Math.round(pxToEmu(margin.left)))
|
|
178
|
+
} : void 0
|
|
179
|
+
}
|
|
154
180
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
for (const data of pages) {
|
|
158
|
-
|
|
181
|
+
buildContext.pptxAuthoring.initialize(source, margin !== void 0);
|
|
182
|
+
if (master) addMasterContent(buildContext, master);
|
|
183
|
+
for (const [pageIndex, data] of pages.entries()) {
|
|
184
|
+
if (pageIndex > 0) {
|
|
185
|
+
const layoutPartPath = buildContext.pptxAuthoring.source.slideLayouts[0]?.partPath;
|
|
186
|
+
if (!layoutPartPath) throw new Error("createPptx did not create a slide layout");
|
|
187
|
+
source = addEmptySlideFromLayout(buildContext.pptxAuthoring.source, { layoutPartPath });
|
|
188
|
+
buildContext.pptxAuthoring.replaceSource(source);
|
|
189
|
+
}
|
|
190
|
+
const slideHandle = buildContext.pptxAuthoring.source.slides[pageIndex]?.handle;
|
|
191
|
+
if (!slideHandle) throw new Error(`slide handle was not found: ${pageIndex + 1}`);
|
|
192
|
+
buildContext.pptxAuthoring.selectSlide(slideHandle);
|
|
159
193
|
const ctx = {
|
|
160
|
-
slide,
|
|
161
|
-
pptx,
|
|
162
194
|
buildContext,
|
|
163
195
|
idPositionMap: buildIdPositionMap(data, buildContext.diagnostics)
|
|
164
196
|
};
|
|
@@ -166,21 +198,19 @@ async function renderPptx(pages, slidePx, buildContext, master) {
|
|
|
166
198
|
const rootBackgroundColor = !isLinelike ? data.backgroundColor : void 0;
|
|
167
199
|
const rootBackgroundGradient = !isLinelike ? data.backgroundGradient : void 0;
|
|
168
200
|
const rootHasOpacity = !isLinelike && "opacity" in data && data.opacity !== void 0;
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
else if (rootBackgroundColor && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
|
|
201
|
+
const rootGradientApplied = rootBackgroundGradient && !rootHasOpacity ? buildContext.pptxAuthoring.setSlideBackgroundGradient(rootBackgroundGradient) : false;
|
|
202
|
+
if (!rootGradientApplied && rootBackgroundColor && !rootBackgroundGradient && !rootHasOpacity) buildContext.pptxAuthoring.setSlideBackgroundSolid(rootBackgroundColor);
|
|
172
203
|
const rootBackgroundImage = !isLinelike ? data.backgroundImage : void 0;
|
|
173
204
|
if (rootBackgroundImage) {
|
|
174
205
|
const cachedData = getImageData(rootBackgroundImage.src, buildContext.imageDataCache);
|
|
175
|
-
|
|
176
|
-
else slide.background = { path: rootBackgroundImage.src };
|
|
206
|
+
buildContext.pptxAuthoring.setSlideBackgroundImage(imageBytesFromSource(rootBackgroundImage.src, cachedData));
|
|
177
207
|
}
|
|
178
208
|
/**
|
|
179
209
|
* node をスライドにレンダリングする
|
|
180
210
|
* @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)
|
|
181
211
|
*/
|
|
182
212
|
function renderNode(node, isRoot = false) {
|
|
183
|
-
if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor ||
|
|
213
|
+
if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootGradientApplied) && !rootHasOpacity)) renderBorderOnly(node, ctx);
|
|
184
214
|
else renderBackgroundAndBorder(node, ctx);
|
|
185
215
|
const def = getNodeDef(node.type);
|
|
186
216
|
switch (def.category) {
|
|
@@ -198,7 +228,7 @@ async function renderPptx(pages, slidePx, buildContext, master) {
|
|
|
198
228
|
}
|
|
199
229
|
renderNode(data, true);
|
|
200
230
|
}
|
|
201
|
-
return
|
|
231
|
+
return createWritablePptx(() => buildContext.pptxAuthoring.source);
|
|
202
232
|
}
|
|
203
233
|
//#endregion
|
|
204
234
|
export { renderPptx };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["// pptxgenjs の型定義\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\n\n// pptxgenjs は CJS パッケージのため動的 import で読み込む\nasync function loadPptxGenJS(): Promise<new () => PptxGenJSInstance> {\n const pptxModule = await import(\"pptxgenjs\");\n // CJS default export の解決: module.default.default (ESM wrapper) または module.default\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n const mod = pptxModule as any;\n return mod.default?.default ?? mod.default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\ntype SlideMasterProps = Parameters<PptxGenJSInstance[\"defineSlideMaster\"]>[0];\ntype ImageProps = {\n x: number;\n y: number;\n w: number;\n h: number;\n path?: string;\n data?: string;\n};\nimport type {\n PositionedNode,\n SlideMasterOptions,\n MasterObject,\n} from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { convertUnderline, convertStrike } from \"./textOptions.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport {\n renderBackgroundAndBorder,\n renderBorderOnly,\n} from \"./utils/backgroundBorder.ts\";\nimport { registerBackgroundGradient } from \"./gradientFills.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nconst DEFAULT_MASTER_NAME = \"POM_MASTER\";\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\n/**\n * MasterObject を pptxgenjs の objects 形式に変換する\n */\nfunction convertMasterObject(\n obj: MasterObject,\n): SlideMasterProps[\"objects\"] extends (infer T)[] | undefined ? T : never {\n switch (obj.type) {\n case \"text\":\n return {\n text: {\n text: obj.text,\n options: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fontSize: obj.fontSize ? pxToPt(obj.fontSize) : undefined,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: convertUnderline(obj.underline),\n strike: convertStrike(obj.strike),\n highlight: obj.highlight,\n align: obj.textAlign,\n },\n },\n };\n case \"image\": {\n const imageProps: ImageProps = {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n };\n // src が data URI かパスかを判定\n if (obj.src.startsWith(\"data:\")) {\n imageProps.data = obj.src;\n } else {\n imageProps.path = obj.src;\n }\n return { image: imageProps };\n }\n case \"rect\":\n return {\n rect: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fill: obj.fill\n ? { color: obj.fill.color, transparency: obj.fill.transparency }\n : undefined,\n line: obj.border\n ? {\n color: obj.border.color,\n width: obj.border.width,\n dashType: obj.border.dashType,\n }\n : undefined,\n },\n };\n case \"line\":\n return {\n line: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n line: obj.line\n ? {\n color: obj.line.color,\n width: obj.line.width,\n dashType: obj.line.dashType,\n }\n : { color: \"000000\", width: 1 },\n },\n };\n }\n}\n\n/**\n * SlideMasterOptions から pptxgenjs の defineSlideMaster を呼び出す\n */\nfunction defineSlideMasterFromOptions(\n pptx: PptxGenJSInstance,\n master: SlideMasterOptions,\n): string {\n const masterName = master.title || DEFAULT_MASTER_NAME;\n\n const masterProps: SlideMasterProps = {\n title: masterName,\n };\n\n // background の変換\n if (master.background) {\n if (\"color\" in master.background) {\n masterProps.background = { color: master.background.color };\n } else if (\"path\" in master.background) {\n masterProps.background = { path: master.background.path };\n } else if (\"data\" in master.background) {\n masterProps.background = { data: master.background.data };\n } else if (\"image\" in master.background) {\n masterProps.background = { path: master.background.image };\n }\n }\n\n // margin の変換 (px -> inches)\n if (master.margin !== undefined) {\n const margin = resolveBoxSpacing(master.margin);\n masterProps.margin = [\n pxToIn(margin.top),\n pxToIn(margin.right),\n pxToIn(margin.bottom),\n pxToIn(margin.left),\n ];\n }\n\n // objects の変換\n if (master.objects && master.objects.length > 0) {\n masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));\n }\n\n // slideNumber の変換\n if (master.slideNumber) {\n masterProps.slideNumber = {\n x: pxToIn(master.slideNumber.x),\n y: pxToIn(master.slideNumber.y),\n w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : undefined,\n h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : undefined,\n fontSize: master.slideNumber.fontSize\n ? pxToPt(master.slideNumber.fontSize)\n : undefined,\n fontFace: master.slideNumber.fontFamily,\n color: master.slideNumber.color,\n };\n }\n\n pptx.defineSlideMaster(masterProps);\n return masterName;\n}\n\n/**\n * PositionedNode ツリーを PptxGenJS スライドに変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PptxGenJS インスタンス\n */\nexport async function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const slideIn = { w: pxToIn(slidePx.w), h: pxToIn(slidePx.h) }; // layout(=px) → PptxGenJS(=inch) への最終変換\n\n const PptxGenJS = await loadPptxGenJS();\n const pptx = new PptxGenJS();\n\n pptx.defineLayout({ name: \"custom\", width: slideIn.w, height: slideIn.h });\n pptx.layout = \"custom\";\n\n // マスターが指定されている場合、defineSlideMaster を呼び出す\n const masterName = master\n ? defineSlideMasterFromOptions(pptx, master)\n : undefined;\n\n for (const data of pages) {\n // マスターが指定されている場合は masterName を使用\n const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { slide, pptx, buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n // backgroundGradient はマーカー色で slide.background に適用し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)\n const rootGradientMarker =\n rootBackgroundGradient && !rootHasOpacity\n ? registerBackgroundGradient(\n rootBackgroundGradient,\n undefined,\n buildContext.gradientFills,\n )\n : undefined;\n if (rootGradientMarker) {\n slide.background = { color: rootGradientMarker };\n } else if (rootBackgroundColor && !rootHasOpacity) {\n slide.background = { color: rootBackgroundColor };\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n if (cachedData) {\n slide.background = { data: cachedData };\n } else {\n slide.background = { path: rootBackgroundImage.src };\n }\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootBackgroundGradient) &&\n !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n renderBorderOnly(node, ctx);\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return pptx;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["import {\n addEmptySlideFromLayout,\n addSlideNumber,\n asEmu,\n asPt,\n createPptx,\n type AddTextBoxRunPropertiesInput,\n type CreatePptxBackground,\n} from \"@pptx-glimpse/document\";\nimport type { PositionedNode, SlideMasterOptions } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToEmu, pxToPt } from \"./units.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport {\n renderBackgroundAndBorder,\n renderBorderOnly,\n} from \"./utils/backgroundBorder.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\nimport { toColorInput } from \"./pptxAuthoring.ts\";\nimport { createGlimpseRunProperties } from \"./utils/glimpseTextBox.ts\";\nimport {\n createShapeBoundsInput,\n noneShapeFill,\n shapeOutline,\n solidShapeFill,\n} from \"./utils/glimpseShape.ts\";\nimport { imageBytesFromSource } from \"./utils/glimpsePicture.ts\";\nimport { createWritablePptx } from \"./writablePptx.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\nfunction masterBackground(\n master: SlideMasterOptions | undefined,\n buildContext: BuildContext,\n): CreatePptxBackground | undefined {\n const background = master?.background;\n if (!background) return undefined;\n if (\"color\" in background) {\n return { kind: \"solid\", color: toColorInput(background.color)! };\n }\n if (\"data\" in background) {\n return { kind: \"image\", bytes: imageBytesFromSource(\"\", background.data) };\n }\n const src = \"path\" in background ? background.path : background.image;\n return {\n kind: \"image\",\n bytes: imageBytesFromSource(\n src,\n getImageData(src, buildContext.imageDataCache),\n ),\n };\n}\n\nfunction masterBounds(obj: { x: number; y: number; w: number; h: number }) {\n return createShapeBoundsInput(obj);\n}\n\nfunction masterTextRunProperties(\n obj: Extract<\n NonNullable<SlideMasterOptions[\"objects\"]>[number],\n { type: \"text\" }\n >,\n): AddTextBoxRunPropertiesInput {\n const properties = createGlimpseRunProperties({\n fontSize: obj.fontSize,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: obj.underline,\n strike: obj.strike,\n highlight: obj.highlight,\n });\n return {\n ...properties,\n fontFace: obj.fontFamily === undefined ? undefined : properties.fontFace,\n fontSize: obj.fontSize === undefined ? undefined : properties.fontSize,\n };\n}\n\nfunction addMasterContent(\n buildContext: BuildContext,\n master: SlideMasterOptions,\n): void {\n const target = buildContext.pptxAuthoring.source.slideMasters[0]?.handle;\n if (!target)\n throw new Error(\"createPptx did not create a slide master handle\");\n buildContext.pptxAuthoring.selectSlide(target);\n for (const obj of master.objects ?? []) {\n switch (obj.type) {\n case \"text\":\n buildContext.pptxAuthoring.registerTextBox({\n ...masterBounds(obj),\n body: {\n anchor: \"middle\",\n marginLeft: asEmu(91440),\n marginRight: asEmu(91440),\n marginTop: asEmu(91440),\n marginBottom: asEmu(91440),\n },\n paragraphs: [\n {\n properties: {\n align: obj.textAlign,\n marginLeft: asEmu(0),\n indent: asEmu(0),\n bullet: { type: \"none\" },\n },\n runs: [\n {\n text: obj.text,\n properties: masterTextRunProperties(obj),\n },\n ],\n },\n ],\n });\n break;\n case \"image\":\n buildContext.pptxAuthoring.registerPicture({\n ...masterBounds(obj),\n bytes: imageBytesFromSource(\n obj.src,\n getImageData(obj.src, buildContext.imageDataCache),\n ),\n });\n break;\n case \"rect\":\n buildContext.pptxAuthoring.registerShape(\n {\n ...masterBounds(obj),\n geometry: { kind: \"preset\", preset: \"rect\" },\n fill: obj.fill\n ? solidShapeFill(obj.fill.color ?? \"FFFFFF\")\n : noneShapeFill(),\n outline: obj.border ? shapeOutline(obj.border) : undefined,\n },\n {\n fillColor: obj.fill?.color,\n fillOpacity:\n obj.fill?.transparency === undefined\n ? undefined\n : 1 - obj.fill.transparency / 100,\n },\n );\n break;\n case \"line\":\n buildContext.pptxAuthoring.registerShape({\n ...masterBounds(obj),\n geometry: { kind: \"preset\", preset: \"line\" },\n fill: noneShapeFill(),\n outline: shapeOutline(obj.line ?? { color: \"000000\", width: 1 }),\n });\n break;\n }\n }\n if (master.slideNumber) {\n const value = master.slideNumber;\n buildContext.pptxAuthoring.replaceSource(\n addSlideNumber(buildContext.pptxAuthoring.source, target, {\n offsetX: asEmu(Math.round(pxToEmu(value.x))),\n offsetY: asEmu(Math.round(pxToEmu(value.y))),\n width:\n value.w === undefined\n ? asEmu(800000)\n : asEmu(Math.round(pxToEmu(value.w))),\n height:\n value.h === undefined\n ? asEmu(300000)\n : asEmu(Math.round(pxToEmu(value.h))),\n properties: {\n fontFace: value.fontFamily,\n fontSize: value.fontSize ? asPt(pxToPt(value.fontSize)) : undefined,\n color: toColorInput(value.color),\n },\n }),\n );\n }\n}\n\n/**\n * PositionedNode ツリーを glimpse の PPTX source model に変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PPTX の write / writeFile / stream 互換 facade\n */\nexport function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const margin =\n master?.margin === undefined ? undefined : resolveBoxSpacing(master.margin);\n let source = createPptx({\n slideSize: {\n width: asEmu(Math.round(pxToEmu(slidePx.w))),\n height: asEmu(Math.round(pxToEmu(slidePx.h))),\n },\n slideMaster: {\n name: master?.title ?? \"POM_MASTER\",\n background: masterBackground(master, buildContext),\n },\n slideLayout: {\n name: \"POM_LAYOUT\",\n margin: margin\n ? {\n top: asEmu(Math.round(pxToEmu(margin.top))),\n right: asEmu(Math.round(pxToEmu(margin.right))),\n bottom: asEmu(Math.round(pxToEmu(margin.bottom))),\n left: asEmu(Math.round(pxToEmu(margin.left))),\n }\n : undefined,\n },\n });\n buildContext.pptxAuthoring.initialize(source, margin !== undefined);\n if (master) addMasterContent(buildContext, master);\n\n for (const [pageIndex, data] of pages.entries()) {\n if (pageIndex > 0) {\n const layoutPartPath =\n buildContext.pptxAuthoring.source.slideLayouts[0]?.partPath;\n if (!layoutPartPath)\n throw new Error(\"createPptx did not create a slide layout\");\n source = addEmptySlideFromLayout(buildContext.pptxAuthoring.source, {\n layoutPartPath,\n });\n buildContext.pptxAuthoring.replaceSource(source);\n }\n const slideHandle =\n buildContext.pptxAuthoring.source.slides[pageIndex]?.handle;\n if (!slideHandle)\n throw new Error(`slide handle was not found: ${pageIndex + 1}`);\n buildContext.pptxAuthoring.selectSlide(slideHandle);\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n const rootGradientApplied =\n rootBackgroundGradient && !rootHasOpacity\n ? buildContext.pptxAuthoring.setSlideBackgroundGradient(\n rootBackgroundGradient,\n )\n : false;\n if (\n !rootGradientApplied &&\n rootBackgroundColor &&\n !rootBackgroundGradient &&\n !rootHasOpacity\n ) {\n buildContext.pptxAuthoring.setSlideBackgroundSolid(rootBackgroundColor);\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n buildContext.pptxAuthoring.setSlideBackgroundImage(\n imageBytesFromSource(rootBackgroundImage.src, cachedData),\n );\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootGradientApplied) && !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n renderBorderOnly(node, ctx);\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return createWritablePptx(() => buildContext.pptxAuthoring.source);\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { pxToIn, pxToPt } from "./units.js";
|
|
|
3
3
|
import { getContentAreaIn } from "./utils/contentArea.js";
|
|
4
4
|
//#region src/renderPptx/textOptions.ts
|
|
5
5
|
/**
|
|
6
|
-
* underline
|
|
6
|
+
* underline プロパティを描画用の形式に変換する
|
|
7
7
|
*/
|
|
8
8
|
function convertUnderline(underline) {
|
|
9
9
|
if (underline === void 0) return void 0;
|
|
@@ -15,7 +15,7 @@ function convertUnderline(underline) {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* strike
|
|
18
|
+
* strike プロパティを描画用の形式に変換する
|
|
19
19
|
*/
|
|
20
20
|
function convertStrike(strike) {
|
|
21
21
|
if (strike) return "sngStrike";
|
|
@@ -37,10 +37,9 @@ function resolveSubSup(run, parent) {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* glow
|
|
41
|
-
* size はユーザー入力 px
|
|
42
|
-
*
|
|
43
|
-
* 渡すとデフォルトが消える。ここで pom 側のデフォルトを確定させる。
|
|
40
|
+
* glow プロパティを描画用の形式に変換する。
|
|
41
|
+
* size はユーザー入力 px、描画用の glow.size は pt。
|
|
42
|
+
* ここで既存の出力互換性を保つデフォルトを確定させる。
|
|
44
43
|
*/
|
|
45
44
|
function convertGlow(glow) {
|
|
46
45
|
if (glow === void 0) return void 0;
|
|
@@ -51,8 +50,8 @@ function convertGlow(glow) {
|
|
|
51
50
|
};
|
|
52
51
|
}
|
|
53
52
|
/**
|
|
54
|
-
* outline
|
|
55
|
-
* size はユーザー入力 px
|
|
53
|
+
* outline プロパティを描画用の形式に変換する
|
|
54
|
+
* size はユーザー入力 px、描画用の outline.size は pt
|
|
56
55
|
*/
|
|
57
56
|
function convertOutline(outline) {
|
|
58
57
|
if (outline === void 0) return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type {\n PositionedNode,\n TextGlow,\n TextOutline,\n Underline,\n UnderlineStyle,\n} from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { getContentAreaIn } from \"./utils/contentArea.ts\";\nimport { measureFontVerticalMetricsRatio } from \"../calcYogaLayout/fontLoader.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline
|
|
1
|
+
{"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type {\n PositionedNode,\n TextGlow,\n TextOutline,\n Underline,\n UnderlineStyle,\n} from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { getContentAreaIn } from \"./utils/contentArea.ts\";\nimport { measureFontVerticalMetricsRatio } from \"../calcYogaLayout/fontLoader.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline プロパティを描画用の形式に変換する\n */\nexport function convertUnderline(\n underline: Underline | undefined,\n): { style?: UnderlineStyle; color?: string } | undefined {\n if (underline === undefined) return undefined;\n if (underline === false) return undefined;\n if (underline === true) return { style: \"sng\" };\n return {\n style: underline.style,\n color: underline.color,\n };\n}\n\n/**\n * strike プロパティを描画用の形式に変換する\n */\nexport function convertStrike(\n strike: boolean | undefined,\n): \"sngStrike\" | undefined {\n if (strike) return \"sngStrike\";\n return undefined;\n}\n\n/**\n * run と親 (node / cell / li) の subscript / superscript を相互排他に解決する。\n * OOXML の baseline 値で表現される性質上、両方 true は意味を持たないため、\n * run 側で sub/sup のいずれかが指定された場合は親の指定をまるごと無効化する\n * (片方だけ指定して反対側を親から漏らさない)。\n */\nexport function resolveSubSup(\n run: { subscript?: boolean; superscript?: boolean },\n parent: { subscript?: boolean; superscript?: boolean },\n): { subscript: boolean | undefined; superscript: boolean | undefined } {\n if (run.subscript !== undefined || run.superscript !== undefined) {\n return { subscript: run.subscript, superscript: run.superscript };\n }\n return { subscript: parent.subscript, superscript: parent.superscript };\n}\n\n/**\n * glow プロパティを描画用の形式に変換する。\n * size はユーザー入力 px、描画用の glow.size は pt。\n * ここで既存の出力互換性を保つデフォルトを確定させる。\n */\nexport function convertGlow(\n glow: TextGlow | undefined,\n): { size: number; opacity: number; color: string } | undefined {\n if (glow === undefined) return undefined;\n return {\n size: pxToPt(glow.size ?? 8),\n opacity: glow.opacity ?? 0.75,\n color: glow.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * outline プロパティを描画用の形式に変換する\n * size はユーザー入力 px、描画用の outline.size は pt\n */\nexport function convertOutline(\n outline: TextOutline | undefined,\n): { size: number; color: string } | undefined {\n if (outline === undefined) return undefined;\n return {\n size: pxToPt(outline.size ?? 1),\n color: outline.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * 行内でグリフ ink が上下中央に来るようにするための描画 y 補正量 (px)\n *\n * レンダラ (LibreOffice / PowerPoint) は固定行送り (spcPts) のとき\n * baseline を「行下端 − winDescent × fontSize」に置くため、グリフ ink\n * (typoAscender + typoDescender) は行内で下寄りになり、同じ gap でも\n * テキスト上側の視覚余白が広く下側が狭く見える (#846)。\n * baseline の実位置と「ink を行内中央に置いたときの baseline 位置」の\n * 差分を返し、呼び出し側でテキストフレームをその分だけ上へずらす。\n */\nexport function calcGlyphCenteringShiftPx(\n fontSizePx: number,\n lineHeight: number,\n fontWeight: \"normal\" | \"bold\",\n): number {\n const lineHeightPx = fontSizePx * lineHeight;\n const metrics = measureFontVerticalMetricsRatio(fontWeight);\n const baselineFromTopPx = lineHeightPx - metrics.winDescent * fontSizePx;\n const inkHeightPx =\n (metrics.typoAscender + metrics.typoDescender) * fontSizePx;\n const centeredBaselineFromTopPx =\n (lineHeightPx - inkHeightPx) / 2 + metrics.typoAscender * fontSizePx;\n return baselineFromTopPx - centeredBaselineFromTopPx;\n}\n\nexport function createTextOptions(node: TextNode) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n\n const area = getContentAreaIn(node);\n const glyphShiftPx = calcGlyphCenteringShiftPx(\n fontSizePx,\n lineHeight,\n node.bold ? \"bold\" : \"normal\",\n );\n\n return {\n ...area,\n y: area.y - pxToIn(glyphShiftPx),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n // 行送りを固定値 (spcPts) で指定する。倍率指定 (spcPct) はレンダラがフォント\n // メトリクスに対する倍率として解釈するため、計測高さ (行数 × fontSize ×\n // lineHeight) と実描画の行高さが一致せず、グリフがボックスからはみ出して\n // 上下余白が非対称になる (#846)\n lineSpacing: pxToPt(fontSizePx * lineHeight),\n rotate: node.rotate,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\n // letterSpacing はユーザー入力 px、描画用の charSpacing は pt\n charSpacing:\n node.letterSpacing !== undefined ? pxToPt(node.letterSpacing) : undefined,\n };\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,iBACd,WACwD;CACxD,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;CACpC,IAAI,cAAc,OAAO,OAAO,KAAA;CAChC,IAAI,cAAc,MAAM,OAAO,EAAE,OAAO,MAAM;CAC9C,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,UAAU;CACnB;AACF;;;;AAKA,SAAgB,cACd,QACyB;CACzB,IAAI,QAAQ,OAAO;AAErB;;;;;;;AAQA,SAAgB,cACd,KACA,QACsE;CACtE,IAAI,IAAI,cAAc,KAAA,KAAa,IAAI,gBAAgB,KAAA,GACrD,OAAO;EAAE,WAAW,IAAI;EAAW,aAAa,IAAI;CAAY;CAElE,OAAO;EAAE,WAAW,OAAO;EAAW,aAAa,OAAO;CAAY;AACxE;;;;;;AAOA,SAAgB,YACd,MAC8D;CAC9D,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO;EACL,MAAM,OAAO,KAAK,QAAQ,CAAC;EAC3B,SAAS,KAAK,WAAW;EACzB,OAAO,KAAK,SAAS;CACvB;AACF;;;;;AAMA,SAAgB,eACd,SAC6C;CAC7C,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,OAAO;EACL,MAAM,OAAO,QAAQ,QAAQ,CAAC;EAC9B,OAAO,QAAQ,SAAS;CAC1B;AACF;;;;;;;;;;;AAYA,SAAgB,0BACd,YACA,YACA,YACQ;CACR,MAAM,eAAe,aAAa;CAClC,MAAM,UAAU,gCAAgC,UAAU;CAM1D,OAL0B,eAAe,QAAQ,aAAa,eAI3D,gBAFA,QAAQ,eAAe,QAAQ,iBAAiB,cAElB,IAAI,QAAQ,eAAe;AAE9D;AAEA,SAAgB,kBAAkB,MAAgB;CAChD,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,cAAc;CAEtC,MAAM,OAAO,iBAAiB,IAAI;CAClC,MAAM,eAAe,0BACnB,YACA,YACA,KAAK,OAAO,SAAS,QACvB;CAEA,OAAO;EACL,GAAG;EACH,GAAG,KAAK,IAAI,OAAO,YAAY;EAC/B,UAAU,OAAO,UAAU;EAC3B,UAAU;EACV,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,QAAQ;EAKR,aAAa,OAAO,aAAa,UAAU;EAC3C,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,aAAa,KAAK;EAClB,WAAW,KAAK;EAChB,MAAM,YAAY,KAAK,IAAI;EAC3B,SAAS,eAAe,KAAK,OAAO;EAEpC,aACE,KAAK,kBAAkB,KAAA,IAAY,OAAO,KAAK,aAAa,IAAI,KAAA;CACpE;AACF"}
|
package/dist/renderPptx/units.js
CHANGED
|
@@ -4,12 +4,12 @@ const pxToPt = (px) => px * 72 / 96;
|
|
|
4
4
|
/**
|
|
5
5
|
* px を DrawingML の EMU (English Metric Unit) に変換する。
|
|
6
6
|
* 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。
|
|
7
|
-
*
|
|
7
|
+
* DrawingML の位置・サイズ指定で使う。
|
|
8
8
|
*/
|
|
9
9
|
const pxToEmu = (px) => px * EMU_PER_IN / 96;
|
|
10
10
|
/**
|
|
11
|
-
* px
|
|
12
|
-
* まとめて変換する。
|
|
11
|
+
* px 単位の矩形を互換用の位置オプション (inch 単位の x/y/w/h) に
|
|
12
|
+
* まとめて変換する。
|
|
13
13
|
*/
|
|
14
14
|
const rectPxToIn = (rect) => ({
|
|
15
15
|
x: pxToIn(rect.x),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\nexport const EMU_PER_IN = 914400;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px を DrawingML の EMU (English Metric Unit) に変換する。\n * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。\n *
|
|
1
|
+
{"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\nexport const EMU_PER_IN = 914400;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px を DrawingML の EMU (English Metric Unit) に変換する。\n * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。\n * DrawingML の位置・サイズ指定で使う。\n */\nexport const pxToEmu = (px: number) => (px * EMU_PER_IN) / PX_PER_IN;\n\n/**\n * px 単位の矩形を互換用の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"AACA,MAAa,aAAa;AAE1B,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA;;;;;;AAO5C,MAAa,WAAW,OAAgB,KAAK,aAAA;;;;;AAM7C,MAAa,cAAc,UAKpB;CACL,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;AAClB"}
|