@a3s-lab/office 0.33.0 → 0.34.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/README.md +5 -3
- package/dist/0~5809.js +123 -0
- package/dist/{0~7614.js → 0~7828.js} +376 -5
- package/dist/0~pdf-viewer.js +5 -1
- package/dist/0~presentation-editor.js +578 -66
- package/dist/0~work-pptx-export.js +50 -14
- package/dist/0~work-pptx-import.js +21 -9
- package/dist/4121.js +122 -1
- package/dist/4560.js +33 -0
- package/dist/internal/features/work/editors/presentation-animation-panel.d.ts +11 -0
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +16 -1
- package/dist/internal/features/work/editors/presentation-presenter-view.d.ts +2 -1
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +11 -3
- package/dist/internal/features/work/editors/use-presentation-animation-commands.d.ts +15 -0
- package/dist/internal/features/work/work-pptx-animation.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-groups.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation-constraints.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation.d.ts +20 -0
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +10 -3
- package/dist/internal/features/work/work-types.d.ts +17 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +123 -0
- package/package.json +4 -1
- package/dist/0~work-presentation-transition.js +0 -38
package/README.md
CHANGED
|
@@ -56,12 +56,13 @@ workflows, and a separate Rust automation plane.
|
|
|
56
56
|
|
|
57
57
|
## Latest on `main`
|
|
58
58
|
|
|
59
|
-
The `0.
|
|
59
|
+
The `0.34.0` release plus the current `main` branch expose these capabilities
|
|
60
60
|
as normal user-facing Playground templates, with matching implementation
|
|
61
61
|
detail in the documentation:
|
|
62
62
|
|
|
63
63
|
| Editor | Latest capability | Public entry |
|
|
64
64
|
| --- | --- | --- |
|
|
65
|
+
| Presentation | Author, reorder, preview, collaborate on, and play ordered appear, fade, fly-in, and zoom entrance animations with on-click, with-previous, and after-previous triggers; the bounded subset round-trips through native PPTX timing trees | Playground **Latest capabilities → 入场动画** · [Presentation reference](docs/latest/en/components/presentation.mdx#entrance-animations) |
|
|
65
66
|
| Writer | Compare the current document with an imported DOCX, HTML, or TXT version and generate deterministic insertion, deletion, character-formatting, and paragraph-formatting revisions; combine a reviewed copy only after exact baseline verification | Playground **Latest capabilities → 文档比较** · [Document reference](docs/latest/en/components/document.mdx#document-compare-and-combine) |
|
|
66
67
|
| Writer | Insert, customize, navigate, and explicitly refresh a typed Table of Contents with heading and native outline levels 1–9, hyperlinks, page numbers, alignment, four leader styles, and native DOCX `TOC` round trips | Playground **Latest capabilities → 可更新目录** · [Document reference](docs/latest/en/components/document.mdx#native-table-of-contents) |
|
|
67
68
|
| Writer | Mark primary and secondary index terms, cross-references, and bold/italic page numbers; insert, navigate, customize, and refresh a bounded native index with editable DOCX `XE` and `INDEX` round trips | Playground **Latest capabilities → 原生索引** · [Document reference](docs/latest/en/components/document.mdx#native-document-index) |
|
|
@@ -70,7 +71,7 @@ detail in the documentation:
|
|
|
70
71
|
| Spreadsheet | Complete common Data Validation input, blank/dropdown, and Stop/Warning/Information error settings with native XLSX round trips | Playground **Latest capabilities → 数据验证** · [Spreadsheet reference](docs/latest/en/components/spreadsheet.mdx#data-validation) |
|
|
71
72
|
| PDF | Insert, delete, rotate, reorder, extract, merge, and split pages through a dedicated Web Worker, with Blob-level Undo/Redo and independent binary reopen verification | Playground **Latest capabilities → 组织 PDF 页面** · [PDF reference](docs/latest/en/components/pdf.mdx#page-organization) |
|
|
72
73
|
|
|
73
|
-
All
|
|
74
|
+
All eight entries are reachable from the first Playground viewport. The seven
|
|
74
75
|
content templates also remain under **新建**; **组织 PDF 页面** opens the normal
|
|
75
76
|
PDF file workflow because page organization must operate on host-provided source
|
|
76
77
|
bytes. The highlighted strip keeps each capability discoverable without a deep
|
|
@@ -163,7 +164,7 @@ baseline rather than one specific release.
|
|
|
163
164
|
| Text, shapes, tables, charts, and images | **Partial** — typed editable objects with native import/export paths | Broader shapes, connectors, effects, SmartArt, and embedded chart editing |
|
|
164
165
|
| Masters and layouts | **Partial** — import/export inheritance with editable common paths | Full visual master, layout, and placeholder authoring |
|
|
165
166
|
| Transitions and timings | **Partial** — fade, push, wipe, split, cut, and click/automatic advance | Broader transition catalog and timing controls |
|
|
166
|
-
| Animations and media | **
|
|
167
|
+
| Animations and media | **Partial** — the Work scene model authors, validates, copies, collaborates on, previews, and plays ordered appear/fade/fly-in/zoom entrance cues with click/with-previous/after-previous triggers and bounded timing; this supported entrance subset round-trips through native PPTX timing trees | Exit/emphasis effects, motion paths, audio, video, recording, and broader PPTX preservation |
|
|
167
168
|
| Slideshow and presenter workflows | **Supported** — current/beginning start, keyboard playback, notes, timer, and responsive presenter view | Rehearsal, recording, ink/laser, and richer multi-display controls |
|
|
168
169
|
| Review and collaboration | **Partial** — comments, shared presence, remote object locations, and host transport | Threads, assignments, mentions, and integrated cloud review |
|
|
169
170
|
| PPTX, print, PDF, and video | **Partial** — PPTX round trip plus slide/notes/handout PDF models; no video export | Broader print controls, vector fidelity, media preservation, and video export |
|
|
@@ -1622,6 +1623,7 @@ without hard-coded return URLs.
|
|
|
1622
1623
|
|
|
1623
1624
|
- [Live Playground](https://a3s-lab.github.io/Office/)
|
|
1624
1625
|
- [Documentation center](https://a3s-lab.github.io/Office/docs/)
|
|
1626
|
+
- [A3S Office 0.34.0 documentation](https://a3s-lab.github.io/Office/docs/0.34.0/)
|
|
1625
1627
|
- [A3S Office 0.33.0 documentation](https://a3s-lab.github.io/Office/docs/0.33.0/)
|
|
1626
1628
|
- [A3S Office 0.32.0 documentation](https://a3s-lab.github.io/Office/docs/0.32.0/)
|
|
1627
1629
|
- [A3S Office 0.31.0 documentation](https://a3s-lab.github.io/Office/docs/0.31.0/)
|
package/dist/0~5809.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createOfficeId as createWorkId } from "./4121.js";
|
|
2
|
+
function createWorkSlideAnimation(elementId, effect, previous) {
|
|
3
|
+
return normalizeWorkSlideAnimation({
|
|
4
|
+
id: previous?.id ?? createWorkId('slide-animation'),
|
|
5
|
+
elementId,
|
|
6
|
+
effect,
|
|
7
|
+
trigger: previous?.trigger ?? 'on-click',
|
|
8
|
+
durationMs: previous?.durationMs ?? 500,
|
|
9
|
+
delayMs: previous?.delayMs ?? 0,
|
|
10
|
+
direction: 'fly-in' === effect ? previous?.direction ?? 'left' : void 0
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function normalizeWorkSlideAnimation(animation) {
|
|
14
|
+
return {
|
|
15
|
+
...animation,
|
|
16
|
+
durationMs: boundedInteger(animation.durationMs, 100, 60000),
|
|
17
|
+
delayMs: boundedInteger(animation.delayMs, 0, 60000),
|
|
18
|
+
direction: 'fly-in' === animation.effect ? normalizeWorkSlideAnimationDirection(animation.direction) : void 0
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function workSlideAnimationForElement(slide, elementId) {
|
|
22
|
+
if (!elementId) return;
|
|
23
|
+
return slide.animations?.find((animation)=>animation.elementId === elementId);
|
|
24
|
+
}
|
|
25
|
+
function workSlideAnimationIndex(slide, animationId) {
|
|
26
|
+
if (!animationId) return -1;
|
|
27
|
+
return slide.animations?.findIndex((animation)=>animation.id === animationId) ?? -1;
|
|
28
|
+
}
|
|
29
|
+
function workSlideAnimationCues(animations) {
|
|
30
|
+
const cues = [];
|
|
31
|
+
let current;
|
|
32
|
+
for (const raw of animations ?? []){
|
|
33
|
+
const animation = normalizeWorkSlideAnimation(raw);
|
|
34
|
+
if (!current || 'on-click' === animation.trigger) {
|
|
35
|
+
current = {
|
|
36
|
+
automatic: 'on-click' !== animation.trigger,
|
|
37
|
+
items: [],
|
|
38
|
+
totalDurationMs: 0
|
|
39
|
+
};
|
|
40
|
+
cues.push(current);
|
|
41
|
+
}
|
|
42
|
+
const previous = current.items.at(-1);
|
|
43
|
+
const baseStart = 'after-previous' === animation.trigger ? previous?.endOffsetMs ?? 0 : 'with-previous' === animation.trigger ? previous?.startOffsetMs ?? 0 : 0;
|
|
44
|
+
const startOffsetMs = baseStart + animation.delayMs;
|
|
45
|
+
const endOffsetMs = startOffsetMs + animation.durationMs;
|
|
46
|
+
current.items.push({
|
|
47
|
+
animation,
|
|
48
|
+
startOffsetMs,
|
|
49
|
+
endOffsetMs
|
|
50
|
+
});
|
|
51
|
+
current.totalDurationMs = Math.max(current.totalDurationMs, endOffsetMs);
|
|
52
|
+
}
|
|
53
|
+
return cues;
|
|
54
|
+
}
|
|
55
|
+
function initialWorkSlideAnimationCueIndex(cues) {
|
|
56
|
+
return cues[0]?.automatic ? 0 : -1;
|
|
57
|
+
}
|
|
58
|
+
function removeWorkSlideAnimationsForElements(slide, elementIds) {
|
|
59
|
+
const animations = slide.animations?.filter((animation)=>!elementIds.has(animation.elementId));
|
|
60
|
+
if (animations?.length === slide.animations?.length) return slide;
|
|
61
|
+
return {
|
|
62
|
+
...slide,
|
|
63
|
+
animations: animations?.length ? animations : void 0
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function remapWorkSlideAnimations(animations, elementIds) {
|
|
67
|
+
const remapped = (animations ?? []).flatMap((animation)=>{
|
|
68
|
+
const elementId = elementIds.get(animation.elementId);
|
|
69
|
+
return elementId ? [
|
|
70
|
+
{
|
|
71
|
+
...animation,
|
|
72
|
+
id: createWorkId('slide-animation'),
|
|
73
|
+
elementId
|
|
74
|
+
}
|
|
75
|
+
] : [];
|
|
76
|
+
});
|
|
77
|
+
return remapped.length ? remapped : void 0;
|
|
78
|
+
}
|
|
79
|
+
function normalizeWorkSlideAnimationDirection(direction) {
|
|
80
|
+
return 'right' === direction || 'up' === direction || 'down' === direction ? direction : 'left';
|
|
81
|
+
}
|
|
82
|
+
function boundedInteger(value, minimum, maximum) {
|
|
83
|
+
const finite = Number.isFinite(value) ? Math.round(value) : minimum;
|
|
84
|
+
return Math.min(maximum, Math.max(minimum, finite));
|
|
85
|
+
}
|
|
86
|
+
const CARDINAL_DIRECTIONS = new Set([
|
|
87
|
+
'left',
|
|
88
|
+
'right',
|
|
89
|
+
'up',
|
|
90
|
+
'down'
|
|
91
|
+
]);
|
|
92
|
+
const SPLIT_DIRECTIONS = new Set([
|
|
93
|
+
'in',
|
|
94
|
+
'out'
|
|
95
|
+
]);
|
|
96
|
+
function createWorkSlideTransition(type, previous) {
|
|
97
|
+
const common = {
|
|
98
|
+
type,
|
|
99
|
+
speed: previous?.speed ?? 'medium',
|
|
100
|
+
advanceOnClick: previous?.advanceOnClick ?? true,
|
|
101
|
+
advanceAfterMs: previous?.advanceAfterMs
|
|
102
|
+
};
|
|
103
|
+
if ('push' === type || 'wipe' === type) return {
|
|
104
|
+
...common,
|
|
105
|
+
direction: previous?.direction && CARDINAL_DIRECTIONS.has(previous.direction) ? previous.direction : 'left'
|
|
106
|
+
};
|
|
107
|
+
if ('split' === type) return {
|
|
108
|
+
...common,
|
|
109
|
+
direction: previous?.direction && SPLIT_DIRECTIONS.has(previous.direction) ? previous.direction : 'out',
|
|
110
|
+
orientation: previous?.orientation ?? 'horizontal'
|
|
111
|
+
};
|
|
112
|
+
return common;
|
|
113
|
+
}
|
|
114
|
+
function slideTransitionDurationMilliseconds(transition) {
|
|
115
|
+
if (transition?.speed === 'fast') return 500;
|
|
116
|
+
if (transition?.speed === 'slow') return 2000;
|
|
117
|
+
return 1000;
|
|
118
|
+
}
|
|
119
|
+
function workSlideTransitionsEqual(left, right) {
|
|
120
|
+
if (!left || !right) return left === right;
|
|
121
|
+
return left.type === right.type && left.speed === right.speed && left.direction === right.direction && left.orientation === right.orientation && left.advanceOnClick === right.advanceOnClick && left.advanceAfterMs === right.advanceAfterMs;
|
|
122
|
+
}
|
|
123
|
+
export { createWorkSlideAnimation, createWorkSlideTransition, initialWorkSlideAnimationCueIndex, normalizeWorkSlideAnimation, remapWorkSlideAnimations, removeWorkSlideAnimationsForElements, slideTransitionDurationMilliseconds, workSlideAnimationCues, workSlideAnimationForElement, workSlideAnimationIndex, workSlideTransitionsEqual };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import jszip from "jszip";
|
|
2
|
-
import {
|
|
2
|
+
import { directChildren, xmlNamespacePrefix, parseXml, firstDescendant, descendants, directChild, attribute, createOfficeId as createWorkId } from "./4121.js";
|
|
3
3
|
import { normalizePresentationChartDataLabelPosition, normalizePresentationChartDataLabels, normalizePresentationChartSeriesStyle } from "./0~work-presentation-charts.js";
|
|
4
4
|
import { parseXlsxChartSeriesStyle, xlsxChartSeriesMarkerXml, parseXlsxTrendline, isSupportedXlsxChartLegend, isSupportedXlsxChartPlotLayout, isSupportedXlsxChartSeriesFormatting, parseXlsxChartPlotLayout, parseXlsxChartLegend, xlsxChartSeriesShapePropertiesXml, parseXlsxErrorBars } from "./4166.js";
|
|
5
5
|
import { workSpreadsheetChartSupportsErrorBars, workSpreadsheetChartSupportsGrouping, normalizeWorkSpreadsheetChartSmoothLines, workSpreadsheetChartSupportsSeriesAnalysis, normalizeWorkSpreadsheetChartSeriesStyle, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetChartOverlap, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartLegendOverlay, normalizeWorkSpreadsheetChartGapWidth, normalizeWorkSpreadsheetChartGrouping, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartSupportsSmoothLines, normalizeWorkSpreadsheetTrendline } from "./8715.js";
|
|
6
|
-
import { createWorkSlideTransition } from "./0~
|
|
6
|
+
import { normalizeWorkSlideAnimation, createWorkSlideTransition } from "./0~5809.js";
|
|
7
7
|
const PRESENTATION_NAMESPACE = 'http://schemas.openxmlformats.org/presentationml/2006/main';
|
|
8
8
|
const DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
|
|
9
9
|
const OFFICE_RELATIONSHIPS_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
@@ -904,6 +904,377 @@ function finiteNumber(value) {
|
|
|
904
904
|
function work_pptx_chart_series_analysis_chartPartNumber(path) {
|
|
905
905
|
return Number(/chart(\d+)\.xml$/.exec(path)?.[1] ?? Number.MAX_SAFE_INTEGER);
|
|
906
906
|
}
|
|
907
|
+
const PRESENTATIONML_NAMESPACE = 'http://schemas.openxmlformats.org/presentationml/2006/main';
|
|
908
|
+
function readPptxAnimations(document, elementIdByPptxShapeId) {
|
|
909
|
+
const timing = directChild(document.documentElement, 'timing');
|
|
910
|
+
if (!timing) return {
|
|
911
|
+
animations: [],
|
|
912
|
+
diagnostics: []
|
|
913
|
+
};
|
|
914
|
+
const diagnostics = [];
|
|
915
|
+
if (elementNamespace(timing) !== PRESENTATIONML_NAMESPACE) return {
|
|
916
|
+
animations: [],
|
|
917
|
+
diagnostics: [
|
|
918
|
+
{
|
|
919
|
+
code: 'pptx.animation.namespace',
|
|
920
|
+
message: 'A namespace-spoofed animation timing tree was ignored instead of being treated as trusted PresentationML.'
|
|
921
|
+
}
|
|
922
|
+
]
|
|
923
|
+
};
|
|
924
|
+
const animations = [];
|
|
925
|
+
const animatedElements = new Set();
|
|
926
|
+
const effectNodes = pptxDescendants(timing, 'cTn').filter((node)=>[
|
|
927
|
+
'clickEffect',
|
|
928
|
+
'withEffect',
|
|
929
|
+
'afterEffect'
|
|
930
|
+
].includes(attribute(node, 'nodeType') ?? ''));
|
|
931
|
+
if (effectNodes.length > 256) diagnostics.push({
|
|
932
|
+
code: 'pptx.animation.limit',
|
|
933
|
+
message: "Only the first 256 object animation timing records on a slide are inspected and imported."
|
|
934
|
+
});
|
|
935
|
+
for (const node of effectNodes.slice(0, 256)){
|
|
936
|
+
if ('entr' !== attribute(node, 'presetClass')) {
|
|
937
|
+
diagnostics.push({
|
|
938
|
+
code: 'pptx.animation.class',
|
|
939
|
+
message: 'An emphasis, exit, motion-path, or malformed animation remains in the source PPTX only.'
|
|
940
|
+
});
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
const targets = Array.from(new Set(pptxDescendants(node, 'spTgt').map((target)=>attribute(target, 'spid')?.trim()).filter((target)=>Boolean(target))));
|
|
944
|
+
if (1 !== targets.length) {
|
|
945
|
+
diagnostics.push({
|
|
946
|
+
code: 'pptx.animation.target',
|
|
947
|
+
message: 'An entrance animation with a missing or ambiguous object target was ignored.'
|
|
948
|
+
});
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
const elementId = elementIdByPptxShapeId.get(targets[0]);
|
|
952
|
+
if (!elementId) {
|
|
953
|
+
diagnostics.push({
|
|
954
|
+
code: 'pptx.animation.target',
|
|
955
|
+
message: 'An entrance animation points to an unavailable slide object and was ignored.'
|
|
956
|
+
});
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
if (animatedElements.has(elementId)) {
|
|
960
|
+
diagnostics.push({
|
|
961
|
+
code: 'pptx.animation.duplicate-target',
|
|
962
|
+
message: 'Only the first supported entrance animation for an object is editable; additional effects remain in the source PPTX only.'
|
|
963
|
+
});
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
const effectResult = readPptxAnimationEffect(node);
|
|
967
|
+
if (!effectResult.effect) {
|
|
968
|
+
diagnostics.push({
|
|
969
|
+
code: 'pptx.animation.effect',
|
|
970
|
+
message: 'An entrance animation uses an unsupported or malformed effect and remains in the source PPTX only.'
|
|
971
|
+
});
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const timingResult = readPptxAnimationTiming(node);
|
|
975
|
+
if (timingResult.normalized) diagnostics.push({
|
|
976
|
+
code: 'pptx.animation.timing',
|
|
977
|
+
message: 'An invalid or out-of-range animation duration or delay was normalized to safe editable bounds.'
|
|
978
|
+
});
|
|
979
|
+
const animation = normalizeWorkSlideAnimation({
|
|
980
|
+
id: createWorkId('slide-animation'),
|
|
981
|
+
elementId,
|
|
982
|
+
effect: effectResult.effect,
|
|
983
|
+
trigger: pptxAnimationTrigger(attribute(node, 'nodeType')),
|
|
984
|
+
durationMs: timingResult.durationMs,
|
|
985
|
+
delayMs: timingResult.delayMs,
|
|
986
|
+
direction: effectResult.direction
|
|
987
|
+
});
|
|
988
|
+
animations.push(animation);
|
|
989
|
+
animatedElements.add(elementId);
|
|
990
|
+
}
|
|
991
|
+
if (!effectNodes.length) diagnostics.push({
|
|
992
|
+
code: 'pptx.animation.structure',
|
|
993
|
+
message: 'The slide timing tree does not contain a supported object entrance sequence and remains in the source PPTX only.'
|
|
994
|
+
});
|
|
995
|
+
return {
|
|
996
|
+
animations,
|
|
997
|
+
diagnostics
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
async function patchPptxAnimations(buffer, slides, registry) {
|
|
1001
|
+
if (!slides.some((slide)=>slide.animations?.length)) return buffer;
|
|
1002
|
+
const archive = await jszip.loadAsync(buffer);
|
|
1003
|
+
for (const [index, slide] of slides.entries()){
|
|
1004
|
+
if (!slide.animations?.length) continue;
|
|
1005
|
+
const path = `ppt/slides/slide${index + 1}.xml`;
|
|
1006
|
+
const entry = archive.file(path);
|
|
1007
|
+
if (!entry) throw new Error(`PPTX animation export is missing generated slide ${index + 1}.`);
|
|
1008
|
+
const document = parseXml(await entry.async('text'), path);
|
|
1009
|
+
normalizePptxShapeIds(document);
|
|
1010
|
+
const targetByMarker = pptxTargetIdsByMarker(document);
|
|
1011
|
+
const scope = `slide:${slide.id}`;
|
|
1012
|
+
const targets = slide.animations.map((animation)=>{
|
|
1013
|
+
const marker = registry.markerForElement(scope, animation.elementId);
|
|
1014
|
+
const ids = marker ? targetByMarker.get(marker) : void 0;
|
|
1015
|
+
if (!marker || ids?.length !== 1) throw new Error(`PPTX animation export expected one generated object for animation ${animation.id}, but found ${ids?.length ?? 0}.`);
|
|
1016
|
+
return {
|
|
1017
|
+
animation,
|
|
1018
|
+
spid: ids[0]
|
|
1019
|
+
};
|
|
1020
|
+
});
|
|
1021
|
+
writePptxAnimations(document, targets);
|
|
1022
|
+
archive.file(path, new XMLSerializer().serializeToString(document));
|
|
1023
|
+
}
|
|
1024
|
+
return archive.generateAsync({
|
|
1025
|
+
type: 'arraybuffer',
|
|
1026
|
+
compression: 'DEFLATE'
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
function writePptxAnimations(document, targets) {
|
|
1030
|
+
const root = document.documentElement;
|
|
1031
|
+
directChild(root, 'timing')?.remove();
|
|
1032
|
+
if (!targets.length) return;
|
|
1033
|
+
const source = parseXml(pptxAnimationTimingXml(targets), 'PPTX animations');
|
|
1034
|
+
const timing = clonePptxAnimationElement(document, source.documentElement);
|
|
1035
|
+
const extensionList = directChild(root, 'extLst') ?? null;
|
|
1036
|
+
root.insertBefore(timing, extensionList);
|
|
1037
|
+
}
|
|
1038
|
+
function clonePptxAnimationElement(document, source) {
|
|
1039
|
+
const element = document.createElementNS(PRESENTATIONML_NAMESPACE, `p:${source.localName}`);
|
|
1040
|
+
for (const item of Array.from(source.attributes))if (!('xmlns' === item.name || item.name.startsWith('xmlns:'))) element.setAttribute(item.name, item.value);
|
|
1041
|
+
for (const child of Array.from(source.childNodes))element.append(child instanceof Element ? clonePptxAnimationElement(document, child) : document.createTextNode(child.textContent ?? ''));
|
|
1042
|
+
return element;
|
|
1043
|
+
}
|
|
1044
|
+
function readPptxAnimationEffect(node) {
|
|
1045
|
+
const presetId = attribute(node, 'presetID');
|
|
1046
|
+
const animationEffects = pptxDescendants(node, 'animEffect');
|
|
1047
|
+
const filters = animationEffects.map((effect)=>attribute(effect, 'filter')?.trim()).filter((filter)=>Boolean(filter));
|
|
1048
|
+
const attributes = new Set(pptxDescendants(node, 'attrName').map((name)=>name.textContent?.trim()));
|
|
1049
|
+
if (filters.some((filter)=>'fade' === filter)) return {
|
|
1050
|
+
effect: 'fade'
|
|
1051
|
+
};
|
|
1052
|
+
for (const filter of filters){
|
|
1053
|
+
const direction = directionFromPptxSlideFilter(filter);
|
|
1054
|
+
if (direction) return {
|
|
1055
|
+
effect: 'fly-in',
|
|
1056
|
+
direction
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if ('23' === presetId || pptxDescendants(node, 'animScale').length > 0 || attributes.has('ppt_w') && attributes.has('ppt_h')) return {
|
|
1060
|
+
effect: 'zoom'
|
|
1061
|
+
};
|
|
1062
|
+
if ('2' === presetId && (attributes.has('ppt_x') || attributes.has('ppt_y'))) return {
|
|
1063
|
+
effect: 'fly-in',
|
|
1064
|
+
direction: directionFromPptxMotion(node)
|
|
1065
|
+
};
|
|
1066
|
+
if ('1' === presetId && pptxDescendants(node, 'set').length > 0 && 0 === animationEffects.length && 0 === pptxDescendants(node, 'anim').length && 0 === pptxDescendants(node, 'animScale').length) return {
|
|
1067
|
+
effect: 'appear'
|
|
1068
|
+
};
|
|
1069
|
+
return {};
|
|
1070
|
+
}
|
|
1071
|
+
function readPptxAnimationTiming(node) {
|
|
1072
|
+
const directDuration = nonnegativeInteger(attribute(node, 'dur'));
|
|
1073
|
+
const behaviorDurations = pptxDirectChildren(pptxDirectChild(node, 'childTnLst') ?? node).flatMap((behavior)=>pptxDescendants(behavior, 'cTn')).map((time)=>nonnegativeInteger(attribute(time, 'dur'))).filter((duration)=>void 0 !== duration);
|
|
1074
|
+
const behaviorDuration = Math.max(0, ...behaviorDurations);
|
|
1075
|
+
const rawDuration = directDuration ?? (behaviorDuration || 500);
|
|
1076
|
+
const delayValue = attribute(pptxChildPath(node, 'stCondLst', 'cond') ?? node, 'delay');
|
|
1077
|
+
const rawDelay = nonnegativeInteger(delayValue) ?? 0;
|
|
1078
|
+
const normalized = normalizeWorkSlideAnimation({
|
|
1079
|
+
id: 'timing',
|
|
1080
|
+
elementId: 'timing',
|
|
1081
|
+
effect: 'fade',
|
|
1082
|
+
trigger: 'on-click',
|
|
1083
|
+
durationMs: rawDuration,
|
|
1084
|
+
delayMs: rawDelay
|
|
1085
|
+
});
|
|
1086
|
+
return {
|
|
1087
|
+
durationMs: normalized.durationMs,
|
|
1088
|
+
delayMs: normalized.delayMs,
|
|
1089
|
+
normalized: normalized.durationMs !== rawDuration || normalized.delayMs !== rawDelay || null !== attribute(node, 'dur') && void 0 === directDuration || null !== delayValue && void 0 === nonnegativeInteger(delayValue)
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
function pptxAnimationTrigger(value) {
|
|
1093
|
+
if ('withEffect' === value) return 'with-previous';
|
|
1094
|
+
if ('afterEffect' === value) return 'after-previous';
|
|
1095
|
+
return 'on-click';
|
|
1096
|
+
}
|
|
1097
|
+
function directionFromPptxSlideFilter(filter) {
|
|
1098
|
+
if ('slide(fromLeft)' === filter) return 'left';
|
|
1099
|
+
if ('slide(fromRight)' === filter) return 'right';
|
|
1100
|
+
if ('slide(fromTop)' === filter) return 'up';
|
|
1101
|
+
if ('slide(fromBottom)' === filter) return 'down';
|
|
1102
|
+
}
|
|
1103
|
+
function directionFromPptxMotion(node) {
|
|
1104
|
+
const values = pptxDescendants(node, 'strVal').map((value)=>attribute(value, 'val') ?? '').join(' ');
|
|
1105
|
+
if (values.includes('-#ppt_w/2')) return 'right';
|
|
1106
|
+
if (values.includes('1+#ppt_h/2')) return 'down';
|
|
1107
|
+
if (values.includes('-#ppt_h/2')) return 'up';
|
|
1108
|
+
return 'left';
|
|
1109
|
+
}
|
|
1110
|
+
function pptxAnimationTimingXml(targets) {
|
|
1111
|
+
let timeNodeId = 1;
|
|
1112
|
+
const nextId = ()=>timeNodeId++;
|
|
1113
|
+
const rootId = nextId();
|
|
1114
|
+
const mainSequenceId = nextId();
|
|
1115
|
+
const targetByAnimationId = new Map(targets.map((target)=>[
|
|
1116
|
+
target.animation.id,
|
|
1117
|
+
target
|
|
1118
|
+
]));
|
|
1119
|
+
const groups = pptxAnimationGroups(targets.map((target)=>target.animation));
|
|
1120
|
+
const groupXml = groups.map((group)=>{
|
|
1121
|
+
const outerId = nextId();
|
|
1122
|
+
const wrappers = group.wrappers.map((animations)=>{
|
|
1123
|
+
const wrapperId = nextId();
|
|
1124
|
+
const effects = animations.map((animation)=>{
|
|
1125
|
+
const target = targetByAnimationId.get(animation.id);
|
|
1126
|
+
if (!target) return '';
|
|
1127
|
+
return pptxAnimationEffectXml(target, nextId);
|
|
1128
|
+
}).join('');
|
|
1129
|
+
return `<p:par><p:cTn id="${wrapperId}" fill="hold"><p:stCondLst><p:cond delay="0"/></p:stCondLst><p:childTnLst>${effects}</p:childTnLst></p:cTn></p:par>`;
|
|
1130
|
+
}).join('');
|
|
1131
|
+
const delay = group.automatic ? '0' : 'indefinite';
|
|
1132
|
+
return `<p:par><p:cTn id="${outerId}" fill="hold"><p:stCondLst><p:cond delay="${delay}"/></p:stCondLst><p:childTnLst>${wrappers}</p:childTnLst></p:cTn></p:par>`;
|
|
1133
|
+
}).join('');
|
|
1134
|
+
const builds = targets.map(({ spid })=>`<p:bldP spid="${spid}" grpId="0" animBg="1"/>`).join('');
|
|
1135
|
+
return `<p:timing xmlns:p="${PRESENTATIONML_NAMESPACE}"><p:tnLst><p:par><p:cTn id="${rootId}" dur="indefinite" restart="never" nodeType="tmRoot"><p:childTnLst><p:seq concurrent="1" nextAc="seek"><p:cTn id="${mainSequenceId}" dur="indefinite" nodeType="mainSeq"><p:childTnLst>${groupXml}</p:childTnLst></p:cTn><p:prevCondLst><p:cond evt="onPrev" delay="0"><p:tgtEl><p:sldTgt/></p:tgtEl></p:cond></p:prevCondLst><p:nextCondLst><p:cond evt="onNext" delay="0"><p:tgtEl><p:sldTgt/></p:tgtEl></p:cond></p:nextCondLst></p:seq></p:childTnLst></p:cTn></p:par></p:tnLst><p:bldLst>${builds}</p:bldLst></p:timing>`;
|
|
1136
|
+
}
|
|
1137
|
+
function pptxAnimationGroups(animations) {
|
|
1138
|
+
const groups = [];
|
|
1139
|
+
let current;
|
|
1140
|
+
for (const animation of animations)if (current && 'on-click' !== animation.trigger) if ('with-previous' === animation.trigger) current.wrappers.at(-1)?.push(animation);
|
|
1141
|
+
else current.wrappers.push([
|
|
1142
|
+
animation
|
|
1143
|
+
]);
|
|
1144
|
+
else {
|
|
1145
|
+
current = {
|
|
1146
|
+
automatic: 'on-click' !== animation.trigger,
|
|
1147
|
+
wrappers: [
|
|
1148
|
+
[
|
|
1149
|
+
animation
|
|
1150
|
+
]
|
|
1151
|
+
]
|
|
1152
|
+
};
|
|
1153
|
+
groups.push(current);
|
|
1154
|
+
}
|
|
1155
|
+
return groups;
|
|
1156
|
+
}
|
|
1157
|
+
function pptxAnimationEffectXml(target, nextId) {
|
|
1158
|
+
const animation = normalizeWorkSlideAnimation(target.animation);
|
|
1159
|
+
const preset = pptxAnimationPreset(animation);
|
|
1160
|
+
const leafId = nextId();
|
|
1161
|
+
const visibilityId = nextId();
|
|
1162
|
+
const visibility = `<p:set><p:cBhvr><p:cTn id="${visibilityId}" dur="1" fill="hold"><p:stCondLst><p:cond delay="0"/></p:stCondLst></p:cTn><p:tgtEl><p:spTgt spid="${target.spid}"/></p:tgtEl><p:attrNameLst><p:attrName>style.visibility</p:attrName></p:attrNameLst></p:cBhvr><p:to><p:strVal val="visible"/></p:to></p:set>`;
|
|
1163
|
+
const behavior = pptxAnimationBehaviorXml(animation, target.spid, nextId);
|
|
1164
|
+
return `<p:par><p:cTn id="${leafId}" presetID="${preset.id}" presetClass="entr" presetSubtype="${preset.subtype}" fill="hold" nodeType="${pptxAnimationNodeType(animation.trigger)}" dur="${animation.durationMs}"><p:stCondLst><p:cond delay="${animation.delayMs}"/></p:stCondLst><p:childTnLst>${visibility}${behavior}</p:childTnLst></p:cTn></p:par>`;
|
|
1165
|
+
}
|
|
1166
|
+
function pptxAnimationBehaviorXml(animation, spid, nextId) {
|
|
1167
|
+
if ('appear' === animation.effect) return '';
|
|
1168
|
+
if ('zoom' === animation.effect) return [
|
|
1169
|
+
'ppt_w',
|
|
1170
|
+
'ppt_h'
|
|
1171
|
+
].map((attributeName)=>{
|
|
1172
|
+
const id = nextId();
|
|
1173
|
+
return `<p:anim calcMode="lin" valueType="num"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}" fill="hold"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl><p:attrNameLst><p:attrName>${attributeName}</p:attrName></p:attrNameLst></p:cBhvr><p:tavLst><p:tav tm="0"><p:val><p:fltVal val="0"/></p:val></p:tav><p:tav tm="100000"><p:val><p:strVal val="#${attributeName}"/></p:val></p:tav></p:tavLst></p:anim>`;
|
|
1174
|
+
}).join('');
|
|
1175
|
+
const id = nextId();
|
|
1176
|
+
const filter = 'fade' === animation.effect ? 'fade' : pptxSlideFilter(animation.direction ?? 'left');
|
|
1177
|
+
return `<p:animEffect transition="in" filter="${filter}"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl></p:cBhvr></p:animEffect>`;
|
|
1178
|
+
}
|
|
1179
|
+
function pptxAnimationPreset(animation) {
|
|
1180
|
+
if ('appear' === animation.effect) return {
|
|
1181
|
+
id: 1,
|
|
1182
|
+
subtype: 0
|
|
1183
|
+
};
|
|
1184
|
+
if ('fade' === animation.effect) return {
|
|
1185
|
+
id: 10,
|
|
1186
|
+
subtype: 0
|
|
1187
|
+
};
|
|
1188
|
+
if ('zoom' === animation.effect) return {
|
|
1189
|
+
id: 23,
|
|
1190
|
+
subtype: 16
|
|
1191
|
+
};
|
|
1192
|
+
const subtypes = {
|
|
1193
|
+
up: 1,
|
|
1194
|
+
right: 2,
|
|
1195
|
+
down: 4,
|
|
1196
|
+
left: 8
|
|
1197
|
+
};
|
|
1198
|
+
return {
|
|
1199
|
+
id: 2,
|
|
1200
|
+
subtype: subtypes[animation.direction ?? 'left']
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
function pptxAnimationNodeType(trigger) {
|
|
1204
|
+
if ('with-previous' === trigger) return 'withEffect';
|
|
1205
|
+
if ('after-previous' === trigger) return 'afterEffect';
|
|
1206
|
+
return 'clickEffect';
|
|
1207
|
+
}
|
|
1208
|
+
function pptxSlideFilter(direction) {
|
|
1209
|
+
if ('right' === direction) return 'slide(fromRight)';
|
|
1210
|
+
if ('up' === direction) return 'slide(fromTop)';
|
|
1211
|
+
if ('down' === direction) return 'slide(fromBottom)';
|
|
1212
|
+
return 'slide(fromLeft)';
|
|
1213
|
+
}
|
|
1214
|
+
function normalizePptxShapeIds(document) {
|
|
1215
|
+
for (const shapeTree of descendants(document, 'spTree')){
|
|
1216
|
+
const used = new Set();
|
|
1217
|
+
let candidate = 1;
|
|
1218
|
+
for (const properties of descendants(shapeTree, 'cNvPr')){
|
|
1219
|
+
const current = Number(attribute(properties, 'id'));
|
|
1220
|
+
if (Number.isInteger(current) && current > 0 && !used.has(current)) {
|
|
1221
|
+
used.add(current);
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
while(used.has(candidate))candidate += 1;
|
|
1225
|
+
properties.setAttribute('id', String(candidate));
|
|
1226
|
+
used.add(candidate);
|
|
1227
|
+
candidate += 1;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
function pptxTargetIdsByMarker(document) {
|
|
1232
|
+
const targets = new Map();
|
|
1233
|
+
for (const properties of descendants(document, 'cNvPr')){
|
|
1234
|
+
const marker = attribute(properties, 'name');
|
|
1235
|
+
const id = Number(attribute(properties, 'id'));
|
|
1236
|
+
if (!marker || !Number.isInteger(id) || id <= 0) continue;
|
|
1237
|
+
const ids = targets.get(marker) ?? [];
|
|
1238
|
+
ids.push(id);
|
|
1239
|
+
targets.set(marker, ids);
|
|
1240
|
+
}
|
|
1241
|
+
return targets;
|
|
1242
|
+
}
|
|
1243
|
+
function nonnegativeInteger(value) {
|
|
1244
|
+
if (null === value || '' === value.trim()) return;
|
|
1245
|
+
const number = Number(value);
|
|
1246
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : void 0;
|
|
1247
|
+
}
|
|
1248
|
+
function elementNamespace(element) {
|
|
1249
|
+
if (element.namespaceURI) return element.namespaceURI;
|
|
1250
|
+
const separator = element.tagName.indexOf(':');
|
|
1251
|
+
const prefix = element.prefix ?? (separator >= 0 ? element.tagName.slice(0, separator) : void 0);
|
|
1252
|
+
const declaration = prefix ? `xmlns:${prefix}` : 'xmlns';
|
|
1253
|
+
let current = element;
|
|
1254
|
+
while(current){
|
|
1255
|
+
const namespace = current.getAttribute(declaration);
|
|
1256
|
+
if (namespace) return namespace;
|
|
1257
|
+
current = current.parentElement;
|
|
1258
|
+
}
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
function pptxDescendants(parent, localName) {
|
|
1262
|
+
return descendants(parent, localName).filter((element)=>elementNamespace(element) === PRESENTATIONML_NAMESPACE);
|
|
1263
|
+
}
|
|
1264
|
+
function pptxDirectChildren(parent, localName) {
|
|
1265
|
+
return directChildren(parent, localName).filter((element)=>elementNamespace(element) === PRESENTATIONML_NAMESPACE);
|
|
1266
|
+
}
|
|
1267
|
+
function pptxDirectChild(parent, localName) {
|
|
1268
|
+
return pptxDirectChildren(parent, localName)[0];
|
|
1269
|
+
}
|
|
1270
|
+
function pptxChildPath(parent, ...localNames) {
|
|
1271
|
+
let current = parent;
|
|
1272
|
+
for (const localName of localNames){
|
|
1273
|
+
if (!current) return;
|
|
1274
|
+
current = pptxDirectChild(current, localName);
|
|
1275
|
+
}
|
|
1276
|
+
return current instanceof Element ? current : void 0;
|
|
1277
|
+
}
|
|
907
1278
|
const EFFECT_TYPES = new Set([
|
|
908
1279
|
'fade',
|
|
909
1280
|
'push',
|
|
@@ -956,7 +1327,7 @@ function readPptxTransition(document) {
|
|
|
956
1327
|
speed: readSpeed(element, diagnostics),
|
|
957
1328
|
advanceOnClick: readBoolean(attribute(element, 'advClick'), true)
|
|
958
1329
|
};
|
|
959
|
-
const advanceAfterMs =
|
|
1330
|
+
const advanceAfterMs = work_pptx_transition_nonnegativeInteger(attribute(element, 'advTm'));
|
|
960
1331
|
if (void 0 !== advanceAfterMs) transition.advanceAfterMs = advanceAfterMs;
|
|
961
1332
|
else if (null !== attribute(element, 'advTm')) diagnostics.push({
|
|
962
1333
|
code: 'pptx.transition.timing',
|
|
@@ -1030,9 +1401,9 @@ function readBoolean(value, fallback) {
|
|
|
1030
1401
|
if (null === value) return fallback;
|
|
1031
1402
|
return '1' === value || 'true' === value.toLowerCase();
|
|
1032
1403
|
}
|
|
1033
|
-
function
|
|
1404
|
+
function work_pptx_transition_nonnegativeInteger(value) {
|
|
1034
1405
|
if (null === value) return;
|
|
1035
1406
|
const number = Number(value);
|
|
1036
1407
|
return Number.isSafeInteger(number) && number >= 0 ? number : void 0;
|
|
1037
1408
|
}
|
|
1038
|
-
export { loadPptxCommentAuthors, patchPptxChartDataLabels, patchPptxChartLayoutAndSeriesStyles, patchPptxChartSeriesAnalysis, patchPptxComments, patchPptxTransitions, readPptxChartDataLabels, readPptxChartLayoutAndSeriesStyles, readPptxChartSeriesAnalysis, readPptxSlideComments, readPptxTransition };
|
|
1409
|
+
export { loadPptxCommentAuthors, patchPptxAnimations, patchPptxChartDataLabels, patchPptxChartLayoutAndSeriesStyles, patchPptxChartSeriesAnalysis, patchPptxComments, patchPptxTransitions, readPptxAnimations, readPptxChartDataLabels, readPptxChartLayoutAndSeriesStyles, readPptxChartSeriesAnalysis, readPptxSlideComments, readPptxTransition };
|
package/dist/0~pdf-viewer.js
CHANGED
|
@@ -2195,13 +2195,17 @@ function PdfToolbar({ annotationState, can, commands, editable, pageOrganization
|
|
|
2195
2195
|
className: "work-pdf-page-organizer-trigger",
|
|
2196
2196
|
"data-pdf-page-organizer-trigger": true,
|
|
2197
2197
|
"aria-label": "组织 PDF 页面",
|
|
2198
|
+
title: "组织 PDF 页面",
|
|
2198
2199
|
disabled: !can.openPageOrganizer(),
|
|
2199
2200
|
onClick: commands.openPageOrganizer,
|
|
2200
2201
|
children: [
|
|
2201
2202
|
/*#__PURE__*/ jsx(LayoutGrid, {
|
|
2202
2203
|
size: 15
|
|
2203
2204
|
}),
|
|
2204
|
-
"
|
|
2205
|
+
/*#__PURE__*/ jsx("span", {
|
|
2206
|
+
className: "work-pdf-page-organizer-label",
|
|
2207
|
+
children: "组织页面"
|
|
2208
|
+
})
|
|
2205
2209
|
]
|
|
2206
2210
|
})
|
|
2207
2211
|
}),
|