@opengeni/contracts 0.40.0 → 0.44.1
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/atlassian.d.ts +278 -0
- package/dist/atlassian.js +55 -0
- package/dist/atlassian.js.map +1 -0
- package/dist/chunk-52SGB2QO.js +688 -0
- package/dist/chunk-52SGB2QO.js.map +1 -0
- package/dist/{chunk-7JZMI22I.js → chunk-5UDNP7AO.js} +34 -2
- package/dist/chunk-5UDNP7AO.js.map +1 -0
- package/dist/chunk-BVGIYW7D.js +11 -0
- package/dist/chunk-BVGIYW7D.js.map +1 -0
- package/dist/chunk-CADSX6UN.js +1145 -0
- package/dist/chunk-CADSX6UN.js.map +1 -0
- package/dist/chunk-D2HKF2EF.js +1460 -0
- package/dist/chunk-D2HKF2EF.js.map +1 -0
- package/dist/chunk-FJBG5E4O.js +194 -0
- package/dist/chunk-FJBG5E4O.js.map +1 -0
- package/dist/chunk-H5BVCZKF.js +382 -0
- package/dist/chunk-H5BVCZKF.js.map +1 -0
- package/dist/chunk-LSB7GRLG.js +779 -0
- package/dist/chunk-LSB7GRLG.js.map +1 -0
- package/dist/chunk-MYBZRDTY.js +340 -0
- package/dist/chunk-MYBZRDTY.js.map +1 -0
- package/dist/chunk-RIY7GYQS.js +1204 -0
- package/dist/chunk-RIY7GYQS.js.map +1 -0
- package/dist/chunk-TZ6YEYN4.js +386 -0
- package/dist/chunk-TZ6YEYN4.js.map +1 -0
- package/dist/chunk-VA22KRHS.js +15177 -0
- package/dist/chunk-VA22KRHS.js.map +1 -0
- package/dist/chunk-Y3MLSLB4.js +392 -0
- package/dist/chunk-Y3MLSLB4.js.map +1 -0
- package/dist/chunk-YRKRZ6OF.js +134 -0
- package/dist/chunk-YRKRZ6OF.js.map +1 -0
- package/dist/company-profile.d.ts +629 -0
- package/dist/document-artifact-commands.d.ts +282 -0
- package/dist/document-artifact-commands.js +54 -0
- package/dist/document-artifact-commands.js.map +1 -0
- package/dist/document-artifact-query.d.ts +2 -0
- package/dist/document-artifact-query.js +54 -0
- package/dist/document-artifact-query.js.map +1 -0
- package/dist/editable-artifact-binary.d.ts +56 -0
- package/dist/editable-artifact-causal-frontier.d.ts +12 -0
- package/dist/editable-artifact-causal-frontier.js +18 -0
- package/dist/editable-artifact-causal-frontier.js.map +1 -0
- package/dist/editable-artifact-codec-registry.d.ts +48 -0
- package/dist/editable-artifact-codec-registry.js +20 -0
- package/dist/editable-artifact-codec-registry.js.map +1 -0
- package/dist/editable-artifact-committed-transaction.d.ts +35 -0
- package/dist/editable-artifact-committed-transaction.js +19 -0
- package/dist/editable-artifact-committed-transaction.js.map +1 -0
- package/dist/editable-artifact-live.d.ts +276 -0
- package/dist/editable-artifact-live.js +907 -0
- package/dist/editable-artifact-live.js.map +1 -0
- package/dist/editable-artifact-serialized-commit.d.ts +53 -0
- package/dist/editable-artifact-serialized-commit.js +26 -0
- package/dist/editable-artifact-serialized-commit.js.map +1 -0
- package/dist/editable-artifact-versions.d.ts +6 -0
- package/dist/editable-artifact-versions.js +11 -0
- package/dist/editable-artifact-versions.js.map +1 -0
- package/dist/editable-artifacts.d.ts +46 -0
- package/dist/editable-artifacts.js +236 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/event-preview.d.ts +1 -1
- package/dist/google-drive.d.ts +114 -2
- package/dist/google-drive.js +70 -14
- package/dist/google-drive.js.map +1 -1
- package/dist/image-generation.d.ts +70 -0
- package/dist/index.d.ts +4460 -155
- package/dist/index.js +1127 -30
- package/dist/interaction.d.ts +6895 -0
- package/dist/memory-slack-delivery.d.ts +397 -0
- package/dist/presentation-artifact-commands.d.ts +349 -0
- package/dist/presentation-artifact-commands.js +66 -0
- package/dist/presentation-artifact-commands.js.map +1 -0
- package/dist/presentation-artifact-query.d.ts +2 -0
- package/dist/presentation-artifact-query.js +66 -0
- package/dist/presentation-artifact-query.js.map +1 -0
- package/dist/retained-output.d.ts +24 -0
- package/dist/sandbox-snapshots.d.ts +9 -0
- package/dist/scoped-knowledge.d.ts +14 -13
- package/dist/slack-bot-scopes.d.ts +4 -0
- package/dist/slack-bot-scopes.js +3 -1
- package/dist/spreadsheet-artifact-commands.d.ts +101 -0
- package/dist/spreadsheet-artifact-commands.js +27 -0
- package/dist/spreadsheet-artifact-commands.js.map +1 -0
- package/dist/spreadsheet-artifact-date.d.ts +4 -0
- package/dist/spreadsheet-artifact-query.d.ts +120 -0
- package/dist/spreadsheet-artifact-query.js +48 -0
- package/dist/spreadsheet-artifact-query.js.map +1 -0
- package/dist/tool-catalog.d.ts +576 -0
- package/dist/video-generation.d.ts +532 -0
- package/dist/video-generation.js +51 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-learning-policy.d.ts +208 -0
- package/package.json +61 -1
- package/src/atlassian.ts +160 -0
- package/src/company-profile.ts +306 -0
- package/src/document-artifact-commands.ts +1709 -0
- package/src/document-artifact-query.ts +2 -0
- package/src/editable-artifact-binary.ts +396 -0
- package/src/editable-artifact-causal-frontier.ts +154 -0
- package/src/editable-artifact-codec-registry.ts +261 -0
- package/src/editable-artifact-committed-transaction.ts +478 -0
- package/src/editable-artifact-live.ts +1379 -0
- package/src/editable-artifact-serialized-commit.ts +494 -0
- package/src/editable-artifact-versions.ts +8 -0
- package/src/editable-artifacts.ts +473 -0
- package/src/google-drive.ts +68 -15
- package/src/image-generation.ts +106 -0
- package/src/index.ts +2326 -325
- package/src/interaction.ts +2292 -0
- package/src/memory-slack-delivery.ts +183 -0
- package/src/presentation-artifact-commands.ts +2035 -0
- package/src/presentation-artifact-query.ts +2 -0
- package/src/retained-output.ts +149 -2
- package/src/sandbox-snapshots.ts +28 -1
- package/src/scoped-knowledge.ts +18 -4
- package/src/slack-bot-scopes.ts +34 -1
- package/src/spreadsheet-artifact-commands.ts +999 -0
- package/src/spreadsheet-artifact-date.ts +21 -0
- package/src/spreadsheet-artifact-query.ts +1411 -0
- package/src/tool-catalog.ts +334 -0
- package/src/video-generation.ts +394 -0
- package/src/workspace-learning-policy.ts +233 -0
- package/dist/chunk-5HR2GPSC.js +0 -11031
- package/dist/chunk-5HR2GPSC.js.map +0 -1
- package/dist/chunk-7JZMI22I.js.map +0 -1
|
@@ -0,0 +1,2035 @@
|
|
|
1
|
+
/** Canonical identity-free OGAPC001/OGAPQ001/OGAPV001 presentation ABI. */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ArtifactBinaryReader,
|
|
5
|
+
ArtifactBinaryWriter,
|
|
6
|
+
decodeCountedEnvelope,
|
|
7
|
+
encodeCountedEnvelope,
|
|
8
|
+
equalBytes,
|
|
9
|
+
fnv1a64,
|
|
10
|
+
strictUtf8,
|
|
11
|
+
} from "./editable-artifact-binary";
|
|
12
|
+
|
|
13
|
+
export const PRESENTATION_ARTIFACT_COMMAND_VERSION = 1 as const;
|
|
14
|
+
export const PRESENTATION_ARTIFACT_QUERY_VERSION = 1 as const;
|
|
15
|
+
export const PRESENTATION_ARTIFACT_QUERY_RESPONSE_VERSION = 1 as const;
|
|
16
|
+
export const PRESENTATION_ARTIFACT_COMMAND_MAX_BYTES = 4 * 1024 * 1024;
|
|
17
|
+
export const PRESENTATION_ARTIFACT_COMMAND_MAX_COMMANDS = 10_000;
|
|
18
|
+
export const PRESENTATION_ARTIFACT_QUERY_MAX_BYTES = 96;
|
|
19
|
+
export const PRESENTATION_ARTIFACT_QUERY_RESPONSE_MAX_BYTES = 8 * 1024 * 1024;
|
|
20
|
+
export const PRESENTATION_ARTIFACT_QUERY_MAX_NODES = 16_384;
|
|
21
|
+
export const PRESENTATION_ARTIFACT_QUERY_MAX_SLIDES = 10_000;
|
|
22
|
+
export const PRESENTATION_ARTIFACT_QUERY_MAX_TEXT_BYTES = 4 * 1024 * 1024;
|
|
23
|
+
export const PRESENTATION_ARTIFACT_MAX_COORDINATE = 9_525_000_000_000;
|
|
24
|
+
export const PRESENTATION_ARTIFACT_MAX_NAME_BYTES = 1_024;
|
|
25
|
+
export const PRESENTATION_ARTIFACT_MAX_TEXT_BYTES = 16 * 1024 * 1024;
|
|
26
|
+
export const PRESENTATION_ARTIFACT_MAX_TEXT_PARAGRAPHS = 100_000;
|
|
27
|
+
export const PRESENTATION_ARTIFACT_MAX_TEXT_RUNS = 250_000;
|
|
28
|
+
export const PRESENTATION_ARTIFACT_MAX_TABLE_ROWS = 10_000;
|
|
29
|
+
export const PRESENTATION_ARTIFACT_MAX_TABLE_COLUMNS = 1_024;
|
|
30
|
+
export const PRESENTATION_ARTIFACT_MAX_TABLE_CELLS = 1_000_000;
|
|
31
|
+
export const PRESENTATION_ARTIFACT_MAX_CHART_SERIES = 16_384;
|
|
32
|
+
export const PRESENTATION_ARTIFACT_MAX_CHART_POINTS = 1_000_000;
|
|
33
|
+
export const PRESENTATION_ARTIFACT_MAX_GROUP_CHILDREN = 100_000;
|
|
34
|
+
|
|
35
|
+
const COMMAND_MAGIC = "OGAPC001";
|
|
36
|
+
const QUERY_MAGIC = strictUtf8("OGAPQ001", "presentation query magic");
|
|
37
|
+
const RESPONSE_MAGIC = strictUtf8("OGAPV001", "presentation response magic");
|
|
38
|
+
const QUERY_HEADER_BYTES = 28;
|
|
39
|
+
const RESPONSE_HEADER_BYTES = 32;
|
|
40
|
+
const CHECKSUM_BYTES = 8;
|
|
41
|
+
|
|
42
|
+
export type PresentationArtifactStableId = string;
|
|
43
|
+
export type PresentationArtifactFill =
|
|
44
|
+
| Readonly<{ kind: "none" }>
|
|
45
|
+
| Readonly<{ kind: "solid"; color: number }>;
|
|
46
|
+
export type PresentationArtifactLine = Readonly<{
|
|
47
|
+
fill: PresentationArtifactFill;
|
|
48
|
+
width: number;
|
|
49
|
+
dash: "solid" | "dash" | "dot";
|
|
50
|
+
}>;
|
|
51
|
+
export type PresentationArtifactRect = Readonly<{
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
}>;
|
|
57
|
+
export type PresentationArtifactTransform = Readonly<{
|
|
58
|
+
rotation: number;
|
|
59
|
+
flipHorizontal: boolean;
|
|
60
|
+
flipVertical: boolean;
|
|
61
|
+
}>;
|
|
62
|
+
export type PresentationArtifactOwner = Readonly<{
|
|
63
|
+
kind: "master" | "layout" | "slide";
|
|
64
|
+
id: PresentationArtifactStableId;
|
|
65
|
+
}>;
|
|
66
|
+
export type PresentationArtifactTextStyle = Readonly<{
|
|
67
|
+
fontFamily: string;
|
|
68
|
+
fontSizeCentipoints: number;
|
|
69
|
+
color: number;
|
|
70
|
+
bold: boolean;
|
|
71
|
+
italic: boolean;
|
|
72
|
+
underline: boolean;
|
|
73
|
+
language: string | null;
|
|
74
|
+
}>;
|
|
75
|
+
export type PresentationArtifactTextRun = Readonly<{
|
|
76
|
+
text: string;
|
|
77
|
+
style: PresentationArtifactTextStyle;
|
|
78
|
+
}>;
|
|
79
|
+
export type PresentationArtifactTextParagraph = Readonly<{
|
|
80
|
+
runs: readonly PresentationArtifactTextRun[];
|
|
81
|
+
alignment: "left" | "center" | "right" | "justify";
|
|
82
|
+
}>;
|
|
83
|
+
export type PresentationArtifactRichText = Readonly<{
|
|
84
|
+
paragraphs: readonly PresentationArtifactTextParagraph[];
|
|
85
|
+
verticalAlignment: "top" | "middle" | "bottom";
|
|
86
|
+
}>;
|
|
87
|
+
export type PresentationArtifactConnectorEndpoint = Readonly<{
|
|
88
|
+
nodeId: PresentationArtifactStableId | null;
|
|
89
|
+
x: number;
|
|
90
|
+
y: number;
|
|
91
|
+
}>;
|
|
92
|
+
export type PresentationArtifactChartSeries = Readonly<{
|
|
93
|
+
name: string;
|
|
94
|
+
categories: readonly string[];
|
|
95
|
+
values: readonly number[];
|
|
96
|
+
xValues: readonly number[];
|
|
97
|
+
bubbleSizes: readonly number[];
|
|
98
|
+
}>;
|
|
99
|
+
export type PresentationArtifactTableCell = Readonly<{
|
|
100
|
+
text: PresentationArtifactRichText;
|
|
101
|
+
fill: PresentationArtifactFill;
|
|
102
|
+
rowSpan: number;
|
|
103
|
+
columnSpan: number;
|
|
104
|
+
}>;
|
|
105
|
+
export type PresentationArtifactNodeKind =
|
|
106
|
+
| Readonly<{
|
|
107
|
+
kind: "shape";
|
|
108
|
+
geometry:
|
|
109
|
+
| "text-box"
|
|
110
|
+
| "rectangle"
|
|
111
|
+
| "rounded-rectangle"
|
|
112
|
+
| "ellipse"
|
|
113
|
+
| "triangle"
|
|
114
|
+
| "right-arrow"
|
|
115
|
+
| "line";
|
|
116
|
+
fill: PresentationArtifactFill;
|
|
117
|
+
line: PresentationArtifactLine;
|
|
118
|
+
text: PresentationArtifactRichText | null;
|
|
119
|
+
placeholder: Readonly<{ kind: string; index: number | null }> | null;
|
|
120
|
+
}>
|
|
121
|
+
| Readonly<{
|
|
122
|
+
kind: "group";
|
|
123
|
+
childOffsetX: number;
|
|
124
|
+
childOffsetY: number;
|
|
125
|
+
childExtentWidth: number;
|
|
126
|
+
childExtentHeight: number;
|
|
127
|
+
children: readonly PresentationArtifactStableId[];
|
|
128
|
+
}>
|
|
129
|
+
| Readonly<{
|
|
130
|
+
kind: "connector";
|
|
131
|
+
connectorKind: "straight" | "elbow" | "curved";
|
|
132
|
+
start: PresentationArtifactConnectorEndpoint;
|
|
133
|
+
end: PresentationArtifactConnectorEndpoint;
|
|
134
|
+
line: PresentationArtifactLine;
|
|
135
|
+
}>
|
|
136
|
+
| Readonly<{
|
|
137
|
+
kind: "chart";
|
|
138
|
+
chartType: "bar" | "line" | "area" | "pie" | "doughnut" | "scatter" | "bubble" | "radar";
|
|
139
|
+
title: PresentationArtifactRichText;
|
|
140
|
+
series: readonly PresentationArtifactChartSeries[];
|
|
141
|
+
hasLegend: boolean;
|
|
142
|
+
}>
|
|
143
|
+
| Readonly<{
|
|
144
|
+
kind: "table";
|
|
145
|
+
rows: readonly (readonly (PresentationArtifactTableCell | null)[])[];
|
|
146
|
+
columnWidths: readonly number[];
|
|
147
|
+
rowHeights: readonly number[];
|
|
148
|
+
line: PresentationArtifactLine;
|
|
149
|
+
}>
|
|
150
|
+
| Readonly<{
|
|
151
|
+
kind: "media";
|
|
152
|
+
digest: Uint8Array;
|
|
153
|
+
contentType: string;
|
|
154
|
+
altText: string;
|
|
155
|
+
fit: "contain" | "cover";
|
|
156
|
+
intrinsicWidth: number;
|
|
157
|
+
intrinsicHeight: number;
|
|
158
|
+
}>;
|
|
159
|
+
|
|
160
|
+
export type PresentationArtifactNewNode = Readonly<{
|
|
161
|
+
id: PresentationArtifactStableId;
|
|
162
|
+
name: string;
|
|
163
|
+
bounds: PresentationArtifactRect;
|
|
164
|
+
transform: PresentationArtifactTransform;
|
|
165
|
+
content: PresentationArtifactNodeKind;
|
|
166
|
+
}>;
|
|
167
|
+
|
|
168
|
+
export type PresentationArtifactCommand =
|
|
169
|
+
| Readonly<{
|
|
170
|
+
kind: "master.create";
|
|
171
|
+
id: PresentationArtifactStableId;
|
|
172
|
+
name: string;
|
|
173
|
+
background: PresentationArtifactFill;
|
|
174
|
+
}>
|
|
175
|
+
| Readonly<{
|
|
176
|
+
kind: "layout.create";
|
|
177
|
+
id: PresentationArtifactStableId;
|
|
178
|
+
name: string;
|
|
179
|
+
masterId: PresentationArtifactStableId | null;
|
|
180
|
+
background: PresentationArtifactFill;
|
|
181
|
+
}>
|
|
182
|
+
| Readonly<{
|
|
183
|
+
kind: "slide.create";
|
|
184
|
+
id: PresentationArtifactStableId;
|
|
185
|
+
index: number;
|
|
186
|
+
title: string;
|
|
187
|
+
layoutId: PresentationArtifactStableId | null;
|
|
188
|
+
background: PresentationArtifactFill;
|
|
189
|
+
}>
|
|
190
|
+
| Readonly<{ kind: "master.delete"; id: PresentationArtifactStableId }>
|
|
191
|
+
| Readonly<{ kind: "layout.delete"; id: PresentationArtifactStableId }>
|
|
192
|
+
| Readonly<{ kind: "slide.delete"; id: PresentationArtifactStableId }>
|
|
193
|
+
| Readonly<{ kind: "slide.title.set"; id: PresentationArtifactStableId; title: string }>
|
|
194
|
+
| Readonly<{
|
|
195
|
+
kind: "slide.layout.set";
|
|
196
|
+
id: PresentationArtifactStableId;
|
|
197
|
+
layoutId: PresentationArtifactStableId | null;
|
|
198
|
+
}>
|
|
199
|
+
| Readonly<{
|
|
200
|
+
kind: "slide.notes.set";
|
|
201
|
+
id: PresentationArtifactStableId;
|
|
202
|
+
notes: PresentationArtifactRichText;
|
|
203
|
+
}>
|
|
204
|
+
| Readonly<{
|
|
205
|
+
kind: "node.insert";
|
|
206
|
+
owner: PresentationArtifactOwner;
|
|
207
|
+
parentId: PresentationArtifactStableId | null;
|
|
208
|
+
index: number;
|
|
209
|
+
node: PresentationArtifactNewNode;
|
|
210
|
+
}>
|
|
211
|
+
| Readonly<{ kind: "node.delete"; id: PresentationArtifactStableId }>
|
|
212
|
+
| Readonly<{
|
|
213
|
+
kind: "node.move";
|
|
214
|
+
id: PresentationArtifactStableId;
|
|
215
|
+
newParentId: PresentationArtifactStableId | null;
|
|
216
|
+
index: number;
|
|
217
|
+
}>
|
|
218
|
+
| Readonly<{
|
|
219
|
+
kind: "node.bounds.set";
|
|
220
|
+
id: PresentationArtifactStableId;
|
|
221
|
+
bounds: PresentationArtifactRect;
|
|
222
|
+
}>
|
|
223
|
+
| Readonly<{
|
|
224
|
+
kind: "node.transform.set";
|
|
225
|
+
id: PresentationArtifactStableId;
|
|
226
|
+
transform: PresentationArtifactTransform;
|
|
227
|
+
}>
|
|
228
|
+
| Readonly<{
|
|
229
|
+
kind: "node.content.set";
|
|
230
|
+
id: PresentationArtifactStableId;
|
|
231
|
+
content: PresentationArtifactNodeKind;
|
|
232
|
+
}>
|
|
233
|
+
| Readonly<{
|
|
234
|
+
kind: "presentation.size.set";
|
|
235
|
+
size: Readonly<{ width: number; height: number }>;
|
|
236
|
+
}>;
|
|
237
|
+
|
|
238
|
+
export type PresentationArtifactCommandBatch = Readonly<{
|
|
239
|
+
version: typeof PRESENTATION_ARTIFACT_COMMAND_VERSION;
|
|
240
|
+
commands: readonly PresentationArtifactCommand[];
|
|
241
|
+
}>;
|
|
242
|
+
|
|
243
|
+
export function encodePresentationArtifactCommandBatch(
|
|
244
|
+
input: PresentationArtifactCommandBatch,
|
|
245
|
+
): Uint8Array {
|
|
246
|
+
if (input?.version !== 1 || !Array.isArray(input.commands))
|
|
247
|
+
throw new TypeError("presentation command batch is invalid");
|
|
248
|
+
if (input.commands.length > PRESENTATION_ARTIFACT_COMMAND_MAX_COMMANDS)
|
|
249
|
+
throw new RangeError("presentation command count exceeds its limit");
|
|
250
|
+
const payload = new ArtifactBinaryWriter(PRESENTATION_ARTIFACT_COMMAND_MAX_BYTES - 32);
|
|
251
|
+
for (const command of input.commands) writeCommand(payload, command);
|
|
252
|
+
return encodeCountedEnvelope(
|
|
253
|
+
COMMAND_MAGIC,
|
|
254
|
+
1,
|
|
255
|
+
input.commands.length,
|
|
256
|
+
payload.finish(),
|
|
257
|
+
PRESENTATION_ARTIFACT_COMMAND_MAX_BYTES,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function decodePresentationArtifactCommandBatch(
|
|
262
|
+
bytes: Uint8Array,
|
|
263
|
+
): PresentationArtifactCommandBatch {
|
|
264
|
+
const envelope = decodeCountedEnvelope(
|
|
265
|
+
bytes,
|
|
266
|
+
COMMAND_MAGIC,
|
|
267
|
+
1,
|
|
268
|
+
PRESENTATION_ARTIFACT_COMMAND_MAX_BYTES,
|
|
269
|
+
PRESENTATION_ARTIFACT_COMMAND_MAX_COMMANDS,
|
|
270
|
+
);
|
|
271
|
+
const reader = new ArtifactBinaryReader(envelope.payload);
|
|
272
|
+
const commands: PresentationArtifactCommand[] = [];
|
|
273
|
+
for (let index = 0; index < envelope.count; index += 1) commands.push(readCommand(reader));
|
|
274
|
+
reader.done("presentation command payload contains trailing bytes");
|
|
275
|
+
return Object.freeze({ version: 1, commands: Object.freeze(commands) });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function assertCanonicalPresentationArtifactCommandBytes(bytes: Uint8Array): void {
|
|
279
|
+
if (
|
|
280
|
+
!equalBytes(
|
|
281
|
+
bytes,
|
|
282
|
+
encodePresentationArtifactCommandBatch(decodePresentationArtifactCommandBatch(bytes)),
|
|
283
|
+
)
|
|
284
|
+
) {
|
|
285
|
+
throw new TypeError("presentation command batch is not canonically encoded");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function writeCommand(writer: ArtifactBinaryWriter, command: PresentationArtifactCommand): void {
|
|
290
|
+
switch (command.kind) {
|
|
291
|
+
case "master.create":
|
|
292
|
+
writer.u8(0);
|
|
293
|
+
writeId(writer, command.id);
|
|
294
|
+
writeString(writer, command.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
295
|
+
writeFill(writer, command.background);
|
|
296
|
+
return;
|
|
297
|
+
case "layout.create":
|
|
298
|
+
writer.u8(1);
|
|
299
|
+
writeId(writer, command.id);
|
|
300
|
+
writeString(writer, command.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
301
|
+
writeOptionalId(writer, command.masterId);
|
|
302
|
+
writeFill(writer, command.background);
|
|
303
|
+
return;
|
|
304
|
+
case "slide.create":
|
|
305
|
+
writer.u8(2);
|
|
306
|
+
writeId(writer, command.id);
|
|
307
|
+
writer.u32(command.index);
|
|
308
|
+
writeString(writer, command.title, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
309
|
+
writeOptionalId(writer, command.layoutId);
|
|
310
|
+
writeFill(writer, command.background);
|
|
311
|
+
return;
|
|
312
|
+
case "master.delete":
|
|
313
|
+
writer.u8(3);
|
|
314
|
+
writeId(writer, command.id);
|
|
315
|
+
return;
|
|
316
|
+
case "layout.delete":
|
|
317
|
+
writer.u8(4);
|
|
318
|
+
writeId(writer, command.id);
|
|
319
|
+
return;
|
|
320
|
+
case "slide.delete":
|
|
321
|
+
writer.u8(5);
|
|
322
|
+
writeId(writer, command.id);
|
|
323
|
+
return;
|
|
324
|
+
case "slide.title.set":
|
|
325
|
+
writer.u8(6);
|
|
326
|
+
writeId(writer, command.id);
|
|
327
|
+
writeString(writer, command.title, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
328
|
+
return;
|
|
329
|
+
case "slide.layout.set":
|
|
330
|
+
writer.u8(7);
|
|
331
|
+
writeId(writer, command.id);
|
|
332
|
+
writeOptionalId(writer, command.layoutId);
|
|
333
|
+
return;
|
|
334
|
+
case "slide.notes.set":
|
|
335
|
+
writer.u8(8);
|
|
336
|
+
writeId(writer, command.id);
|
|
337
|
+
writeRichText(writer, command.notes);
|
|
338
|
+
return;
|
|
339
|
+
case "node.insert":
|
|
340
|
+
writer.u8(9);
|
|
341
|
+
writeOwner(writer, command.owner);
|
|
342
|
+
writeOptionalId(writer, command.parentId);
|
|
343
|
+
writer.u32(command.index);
|
|
344
|
+
writeNewNode(writer, command.node);
|
|
345
|
+
return;
|
|
346
|
+
case "node.delete":
|
|
347
|
+
writer.u8(10);
|
|
348
|
+
writeId(writer, command.id);
|
|
349
|
+
return;
|
|
350
|
+
case "node.move":
|
|
351
|
+
writer.u8(11);
|
|
352
|
+
writeId(writer, command.id);
|
|
353
|
+
writeOptionalId(writer, command.newParentId);
|
|
354
|
+
writer.u32(command.index);
|
|
355
|
+
return;
|
|
356
|
+
case "node.bounds.set":
|
|
357
|
+
writer.u8(12);
|
|
358
|
+
writeId(writer, command.id);
|
|
359
|
+
writeRect(writer, command.bounds);
|
|
360
|
+
return;
|
|
361
|
+
case "node.transform.set":
|
|
362
|
+
writer.u8(13);
|
|
363
|
+
writeId(writer, command.id);
|
|
364
|
+
writeTransform(writer, command.transform);
|
|
365
|
+
return;
|
|
366
|
+
case "node.content.set":
|
|
367
|
+
writer.u8(14);
|
|
368
|
+
writeId(writer, command.id);
|
|
369
|
+
writeNodeKind(writer, command.content);
|
|
370
|
+
return;
|
|
371
|
+
case "presentation.size.set":
|
|
372
|
+
writer.u8(15);
|
|
373
|
+
writeSlideSize(writer, command.size);
|
|
374
|
+
return;
|
|
375
|
+
default:
|
|
376
|
+
throw new TypeError("invalid presentation command kind");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function readCommand(reader: ArtifactBinaryReader): PresentationArtifactCommand {
|
|
381
|
+
switch (reader.u8()) {
|
|
382
|
+
case 0:
|
|
383
|
+
return Object.freeze({
|
|
384
|
+
kind: "master.create",
|
|
385
|
+
id: readId(reader),
|
|
386
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
387
|
+
background: readFill(reader),
|
|
388
|
+
});
|
|
389
|
+
case 1:
|
|
390
|
+
return Object.freeze({
|
|
391
|
+
kind: "layout.create",
|
|
392
|
+
id: readId(reader),
|
|
393
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
394
|
+
masterId: readOptionalId(reader),
|
|
395
|
+
background: readFill(reader),
|
|
396
|
+
});
|
|
397
|
+
case 2:
|
|
398
|
+
return Object.freeze({
|
|
399
|
+
kind: "slide.create",
|
|
400
|
+
id: readId(reader),
|
|
401
|
+
index: reader.u32(),
|
|
402
|
+
title: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
403
|
+
layoutId: readOptionalId(reader),
|
|
404
|
+
background: readFill(reader),
|
|
405
|
+
});
|
|
406
|
+
case 3:
|
|
407
|
+
return Object.freeze({ kind: "master.delete", id: readId(reader) });
|
|
408
|
+
case 4:
|
|
409
|
+
return Object.freeze({ kind: "layout.delete", id: readId(reader) });
|
|
410
|
+
case 5:
|
|
411
|
+
return Object.freeze({ kind: "slide.delete", id: readId(reader) });
|
|
412
|
+
case 6:
|
|
413
|
+
return Object.freeze({
|
|
414
|
+
kind: "slide.title.set",
|
|
415
|
+
id: readId(reader),
|
|
416
|
+
title: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
417
|
+
});
|
|
418
|
+
case 7:
|
|
419
|
+
return Object.freeze({
|
|
420
|
+
kind: "slide.layout.set",
|
|
421
|
+
id: readId(reader),
|
|
422
|
+
layoutId: readOptionalId(reader),
|
|
423
|
+
});
|
|
424
|
+
case 8:
|
|
425
|
+
return Object.freeze({
|
|
426
|
+
kind: "slide.notes.set",
|
|
427
|
+
id: readId(reader),
|
|
428
|
+
notes: readRichText(reader),
|
|
429
|
+
});
|
|
430
|
+
case 9:
|
|
431
|
+
return Object.freeze({
|
|
432
|
+
kind: "node.insert",
|
|
433
|
+
owner: readOwner(reader),
|
|
434
|
+
parentId: readOptionalId(reader),
|
|
435
|
+
index: reader.u32(),
|
|
436
|
+
node: readNewNode(reader),
|
|
437
|
+
});
|
|
438
|
+
case 10:
|
|
439
|
+
return Object.freeze({ kind: "node.delete", id: readId(reader) });
|
|
440
|
+
case 11:
|
|
441
|
+
return Object.freeze({
|
|
442
|
+
kind: "node.move",
|
|
443
|
+
id: readId(reader),
|
|
444
|
+
newParentId: readOptionalId(reader),
|
|
445
|
+
index: reader.u32(),
|
|
446
|
+
});
|
|
447
|
+
case 12:
|
|
448
|
+
return Object.freeze({
|
|
449
|
+
kind: "node.bounds.set",
|
|
450
|
+
id: readId(reader),
|
|
451
|
+
bounds: readRect(reader),
|
|
452
|
+
});
|
|
453
|
+
case 13:
|
|
454
|
+
return Object.freeze({
|
|
455
|
+
kind: "node.transform.set",
|
|
456
|
+
id: readId(reader),
|
|
457
|
+
transform: readTransform(reader),
|
|
458
|
+
});
|
|
459
|
+
case 14:
|
|
460
|
+
return Object.freeze({
|
|
461
|
+
kind: "node.content.set",
|
|
462
|
+
id: readId(reader),
|
|
463
|
+
content: readNodeKind(reader),
|
|
464
|
+
});
|
|
465
|
+
case 15:
|
|
466
|
+
return Object.freeze({
|
|
467
|
+
kind: "presentation.size.set",
|
|
468
|
+
size: readSlideSize(reader),
|
|
469
|
+
});
|
|
470
|
+
default:
|
|
471
|
+
throw new TypeError("invalid presentation command tag");
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function writeId(writer: ArtifactBinaryWriter, id: string): void {
|
|
476
|
+
writer.stableId(id, "presentation id");
|
|
477
|
+
}
|
|
478
|
+
function readId(reader: ArtifactBinaryReader): string {
|
|
479
|
+
return reader.stableId("presentation id");
|
|
480
|
+
}
|
|
481
|
+
function writeOptionalId(writer: ArtifactBinaryWriter, id: string | null): void {
|
|
482
|
+
writer.bool(id !== null);
|
|
483
|
+
if (id !== null) writeId(writer, id);
|
|
484
|
+
}
|
|
485
|
+
function readOptionalId(reader: ArtifactBinaryReader): string | null {
|
|
486
|
+
return reader.bool("optional presentation id") ? readId(reader) : null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function writeString(writer: ArtifactBinaryWriter, value: string, maximum: number): void {
|
|
490
|
+
writer.string(value, maximum, "presentation string");
|
|
491
|
+
}
|
|
492
|
+
function readString(reader: ArtifactBinaryReader, maximum: number): string {
|
|
493
|
+
return reader.string(maximum, "presentation string");
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function writeOwner(writer: ArtifactBinaryWriter, owner: PresentationArtifactOwner): void {
|
|
497
|
+
writer.u8(
|
|
498
|
+
owner.kind === "master"
|
|
499
|
+
? 0
|
|
500
|
+
: owner.kind === "layout"
|
|
501
|
+
? 1
|
|
502
|
+
: owner.kind === "slide"
|
|
503
|
+
? 2
|
|
504
|
+
: bad("presentation owner"),
|
|
505
|
+
);
|
|
506
|
+
writeId(writer, owner.id);
|
|
507
|
+
}
|
|
508
|
+
function readOwner(reader: ArtifactBinaryReader): PresentationArtifactOwner {
|
|
509
|
+
const tag = reader.u8();
|
|
510
|
+
const id = readId(reader);
|
|
511
|
+
return Object.freeze({
|
|
512
|
+
kind:
|
|
513
|
+
tag === 0 ? "master" : tag === 1 ? "layout" : tag === 2 ? "slide" : bad("presentation owner"),
|
|
514
|
+
id,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function writeFill(writer: ArtifactBinaryWriter, fill: PresentationArtifactFill): void {
|
|
519
|
+
if (fill.kind === "none") writer.u8(0);
|
|
520
|
+
else if (fill.kind === "solid") {
|
|
521
|
+
writer.u8(1);
|
|
522
|
+
writer.u32(fill.color);
|
|
523
|
+
} else bad("presentation fill");
|
|
524
|
+
}
|
|
525
|
+
function readFill(reader: ArtifactBinaryReader): PresentationArtifactFill {
|
|
526
|
+
const tag = reader.u8();
|
|
527
|
+
if (tag === 0) return Object.freeze({ kind: "none" });
|
|
528
|
+
if (tag === 1) return Object.freeze({ kind: "solid", color: reader.u32() });
|
|
529
|
+
return bad("presentation fill");
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function writeLine(writer: ArtifactBinaryWriter, line: PresentationArtifactLine): void {
|
|
533
|
+
writeFill(writer, line.fill);
|
|
534
|
+
writer.i64(coordinate(line.width, "presentation line width"));
|
|
535
|
+
writer.u8(
|
|
536
|
+
line.dash === "solid"
|
|
537
|
+
? 0
|
|
538
|
+
: line.dash === "dash"
|
|
539
|
+
? 1
|
|
540
|
+
: line.dash === "dot"
|
|
541
|
+
? 2
|
|
542
|
+
: bad("presentation line dash"),
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
function readLine(reader: ArtifactBinaryReader): PresentationArtifactLine {
|
|
546
|
+
const fill = readFill(reader);
|
|
547
|
+
const width = readCoordinate(reader, "presentation line width");
|
|
548
|
+
const tag = reader.u8();
|
|
549
|
+
return Object.freeze({
|
|
550
|
+
fill,
|
|
551
|
+
width,
|
|
552
|
+
dash:
|
|
553
|
+
tag === 0 ? "solid" : tag === 1 ? "dash" : tag === 2 ? "dot" : bad("presentation line dash"),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function writeRect(writer: ArtifactBinaryWriter, rect: PresentationArtifactRect): void {
|
|
558
|
+
const x = coordinate(rect.x, "presentation x");
|
|
559
|
+
const y = coordinate(rect.y, "presentation y");
|
|
560
|
+
const width = coordinate(rect.width, "presentation width");
|
|
561
|
+
const height = coordinate(rect.height, "presentation height");
|
|
562
|
+
if (
|
|
563
|
+
width <= 0 ||
|
|
564
|
+
height <= 0 ||
|
|
565
|
+
Math.abs(x + width) > PRESENTATION_ARTIFACT_MAX_COORDINATE ||
|
|
566
|
+
Math.abs(y + height) > PRESENTATION_ARTIFACT_MAX_COORDINATE
|
|
567
|
+
)
|
|
568
|
+
bad("presentation rectangle");
|
|
569
|
+
writer.i64(x);
|
|
570
|
+
writer.i64(y);
|
|
571
|
+
writer.i64(width);
|
|
572
|
+
writer.i64(height);
|
|
573
|
+
}
|
|
574
|
+
function writeSlideSize(
|
|
575
|
+
writer: ArtifactBinaryWriter,
|
|
576
|
+
size: Readonly<{ width: number; height: number }>,
|
|
577
|
+
): void {
|
|
578
|
+
const width = coordinate(size.width, "presentation slide width");
|
|
579
|
+
const height = coordinate(size.height, "presentation slide height");
|
|
580
|
+
if (width <= 0 || height <= 0) throw new TypeError("invalid presentation slide size");
|
|
581
|
+
writer.i64(width);
|
|
582
|
+
writer.i64(height);
|
|
583
|
+
}
|
|
584
|
+
function readSlideSize(reader: ArtifactBinaryReader): Readonly<{ width: number; height: number }> {
|
|
585
|
+
const size = Object.freeze({
|
|
586
|
+
width: readCoordinate(reader, "presentation slide width"),
|
|
587
|
+
height: readCoordinate(reader, "presentation slide height"),
|
|
588
|
+
});
|
|
589
|
+
if (size.width <= 0 || size.height <= 0) throw new TypeError("invalid presentation slide size");
|
|
590
|
+
return size;
|
|
591
|
+
}
|
|
592
|
+
function readRect(reader: ArtifactBinaryReader): PresentationArtifactRect {
|
|
593
|
+
const rect = Object.freeze({
|
|
594
|
+
x: readCoordinate(reader, "presentation x"),
|
|
595
|
+
y: readCoordinate(reader, "presentation y"),
|
|
596
|
+
width: readCoordinate(reader, "presentation width"),
|
|
597
|
+
height: readCoordinate(reader, "presentation height"),
|
|
598
|
+
});
|
|
599
|
+
if (
|
|
600
|
+
rect.width <= 0 ||
|
|
601
|
+
rect.height <= 0 ||
|
|
602
|
+
Math.abs(rect.x + rect.width) > PRESENTATION_ARTIFACT_MAX_COORDINATE ||
|
|
603
|
+
Math.abs(rect.y + rect.height) > PRESENTATION_ARTIFACT_MAX_COORDINATE
|
|
604
|
+
)
|
|
605
|
+
bad("presentation rectangle");
|
|
606
|
+
return rect;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function writeTransform(
|
|
610
|
+
writer: ArtifactBinaryWriter,
|
|
611
|
+
transform: PresentationArtifactTransform,
|
|
612
|
+
): void {
|
|
613
|
+
if (
|
|
614
|
+
!Number.isInteger(transform.rotation) ||
|
|
615
|
+
transform.rotation < -21_600_000 ||
|
|
616
|
+
transform.rotation > 21_600_000
|
|
617
|
+
)
|
|
618
|
+
bad("presentation rotation");
|
|
619
|
+
writer.i32(transform.rotation);
|
|
620
|
+
writer.bool(transform.flipHorizontal);
|
|
621
|
+
writer.bool(transform.flipVertical);
|
|
622
|
+
}
|
|
623
|
+
function readTransform(reader: ArtifactBinaryReader): PresentationArtifactTransform {
|
|
624
|
+
const rotation = reader.i32();
|
|
625
|
+
if (rotation < -21_600_000 || rotation > 21_600_000) bad("presentation rotation");
|
|
626
|
+
return Object.freeze({
|
|
627
|
+
rotation,
|
|
628
|
+
flipHorizontal: reader.bool("presentation boolean"),
|
|
629
|
+
flipVertical: reader.bool("presentation boolean"),
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function writeRichText(writer: ArtifactBinaryWriter, text: PresentationArtifactRichText): void {
|
|
634
|
+
writer.u8(
|
|
635
|
+
text.verticalAlignment === "top"
|
|
636
|
+
? 0
|
|
637
|
+
: text.verticalAlignment === "middle"
|
|
638
|
+
? 1
|
|
639
|
+
: text.verticalAlignment === "bottom"
|
|
640
|
+
? 2
|
|
641
|
+
: bad("vertical alignment"),
|
|
642
|
+
);
|
|
643
|
+
writer.count(
|
|
644
|
+
text.paragraphs.length,
|
|
645
|
+
PRESENTATION_ARTIFACT_MAX_TEXT_PARAGRAPHS,
|
|
646
|
+
"presentation paragraphs",
|
|
647
|
+
);
|
|
648
|
+
let totalRuns = 0;
|
|
649
|
+
let totalBytes = 0;
|
|
650
|
+
for (const paragraph of text.paragraphs) {
|
|
651
|
+
writer.u8(
|
|
652
|
+
paragraph.alignment === "left"
|
|
653
|
+
? 0
|
|
654
|
+
: paragraph.alignment === "center"
|
|
655
|
+
? 1
|
|
656
|
+
: paragraph.alignment === "right"
|
|
657
|
+
? 2
|
|
658
|
+
: paragraph.alignment === "justify"
|
|
659
|
+
? 3
|
|
660
|
+
: bad("horizontal alignment"),
|
|
661
|
+
);
|
|
662
|
+
totalRuns += paragraph.runs.length;
|
|
663
|
+
if (totalRuns > PRESENTATION_ARTIFACT_MAX_TEXT_RUNS)
|
|
664
|
+
throw new RangeError("presentation text runs exceed their limit");
|
|
665
|
+
writer.count(
|
|
666
|
+
paragraph.runs.length,
|
|
667
|
+
PRESENTATION_ARTIFACT_MAX_TEXT_RUNS,
|
|
668
|
+
"presentation text runs",
|
|
669
|
+
);
|
|
670
|
+
for (const run of paragraph.runs) {
|
|
671
|
+
const bytes = strictUtf8(run.text, "presentation text");
|
|
672
|
+
totalBytes += bytes.byteLength;
|
|
673
|
+
if (totalBytes > PRESENTATION_ARTIFACT_MAX_TEXT_BYTES)
|
|
674
|
+
throw new RangeError("presentation text exceeds its byte limit");
|
|
675
|
+
writer.u32(bytes.byteLength);
|
|
676
|
+
writer.bytes(bytes);
|
|
677
|
+
writeTextStyle(writer, run.style);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function readRichText(reader: ArtifactBinaryReader): PresentationArtifactRichText {
|
|
683
|
+
const verticalTag = reader.u8();
|
|
684
|
+
const verticalAlignment =
|
|
685
|
+
verticalTag === 0
|
|
686
|
+
? "top"
|
|
687
|
+
: verticalTag === 1
|
|
688
|
+
? "middle"
|
|
689
|
+
: verticalTag === 2
|
|
690
|
+
? "bottom"
|
|
691
|
+
: bad("vertical alignment");
|
|
692
|
+
const count = reader.count(PRESENTATION_ARTIFACT_MAX_TEXT_PARAGRAPHS, "presentation paragraphs");
|
|
693
|
+
const paragraphs: PresentationArtifactTextParagraph[] = [];
|
|
694
|
+
let totalRuns = 0;
|
|
695
|
+
let totalBytes = 0;
|
|
696
|
+
for (let index = 0; index < count; index += 1) {
|
|
697
|
+
const alignmentTag = reader.u8();
|
|
698
|
+
const alignment =
|
|
699
|
+
alignmentTag === 0
|
|
700
|
+
? "left"
|
|
701
|
+
: alignmentTag === 1
|
|
702
|
+
? "center"
|
|
703
|
+
: alignmentTag === 2
|
|
704
|
+
? "right"
|
|
705
|
+
: alignmentTag === 3
|
|
706
|
+
? "justify"
|
|
707
|
+
: bad("horizontal alignment");
|
|
708
|
+
const runCount = reader.count(PRESENTATION_ARTIFACT_MAX_TEXT_RUNS, "presentation text runs");
|
|
709
|
+
totalRuns += runCount;
|
|
710
|
+
if (totalRuns > PRESENTATION_ARTIFACT_MAX_TEXT_RUNS)
|
|
711
|
+
throw new RangeError("presentation text runs exceed their limit");
|
|
712
|
+
const runs: PresentationArtifactTextRun[] = [];
|
|
713
|
+
for (let runIndex = 0; runIndex < runCount; runIndex += 1) {
|
|
714
|
+
const text = readString(reader, PRESENTATION_ARTIFACT_MAX_TEXT_BYTES);
|
|
715
|
+
totalBytes += strictUtf8(text, "presentation text").byteLength;
|
|
716
|
+
if (totalBytes > PRESENTATION_ARTIFACT_MAX_TEXT_BYTES)
|
|
717
|
+
throw new RangeError("presentation text exceeds its byte limit");
|
|
718
|
+
runs.push(Object.freeze({ text, style: readTextStyle(reader) }));
|
|
719
|
+
}
|
|
720
|
+
paragraphs.push(Object.freeze({ runs: Object.freeze(runs), alignment }));
|
|
721
|
+
}
|
|
722
|
+
return Object.freeze({ paragraphs: Object.freeze(paragraphs), verticalAlignment });
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function writeTextStyle(writer: ArtifactBinaryWriter, style: PresentationArtifactTextStyle): void {
|
|
726
|
+
writeString(writer, style.fontFamily, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
727
|
+
writer.u32(style.fontSizeCentipoints);
|
|
728
|
+
writer.u32(style.color);
|
|
729
|
+
writer.bool(style.bold);
|
|
730
|
+
writer.bool(style.italic);
|
|
731
|
+
writer.bool(style.underline);
|
|
732
|
+
writer.bool(style.language !== null);
|
|
733
|
+
if (style.language !== null) writeString(writer, style.language, 128);
|
|
734
|
+
}
|
|
735
|
+
function readTextStyle(reader: ArtifactBinaryReader): PresentationArtifactTextStyle {
|
|
736
|
+
return Object.freeze({
|
|
737
|
+
fontFamily: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
738
|
+
fontSizeCentipoints: reader.u32(),
|
|
739
|
+
color: reader.u32(),
|
|
740
|
+
bold: reader.bool("presentation boolean"),
|
|
741
|
+
italic: reader.bool("presentation boolean"),
|
|
742
|
+
underline: reader.bool("presentation boolean"),
|
|
743
|
+
language: reader.bool("optional presentation language") ? readString(reader, 128) : null,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function writeNewNode(writer: ArtifactBinaryWriter, node: PresentationArtifactNewNode): void {
|
|
748
|
+
writeId(writer, node.id);
|
|
749
|
+
writeString(writer, node.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
750
|
+
writeRect(writer, node.bounds);
|
|
751
|
+
writeTransform(writer, node.transform);
|
|
752
|
+
writeNodeKind(writer, node.content);
|
|
753
|
+
}
|
|
754
|
+
function readNewNode(reader: ArtifactBinaryReader): PresentationArtifactNewNode {
|
|
755
|
+
return Object.freeze({
|
|
756
|
+
id: readId(reader),
|
|
757
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
758
|
+
bounds: readRect(reader),
|
|
759
|
+
transform: readTransform(reader),
|
|
760
|
+
content: readNodeKind(reader),
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function writeNodeKind(writer: ArtifactBinaryWriter, node: PresentationArtifactNodeKind): void {
|
|
765
|
+
if (node.kind === "shape") {
|
|
766
|
+
writer.u8(0);
|
|
767
|
+
writer.u8(
|
|
768
|
+
(
|
|
769
|
+
{
|
|
770
|
+
"text-box": 0,
|
|
771
|
+
rectangle: 1,
|
|
772
|
+
"rounded-rectangle": 2,
|
|
773
|
+
ellipse: 3,
|
|
774
|
+
triangle: 4,
|
|
775
|
+
"right-arrow": 5,
|
|
776
|
+
line: 6,
|
|
777
|
+
} as const
|
|
778
|
+
)[node.geometry] ?? bad("shape geometry"),
|
|
779
|
+
);
|
|
780
|
+
writeFill(writer, node.fill);
|
|
781
|
+
writeLine(writer, node.line);
|
|
782
|
+
writer.bool(node.text !== null);
|
|
783
|
+
if (node.text !== null) writeRichText(writer, node.text);
|
|
784
|
+
writer.bool(node.placeholder !== null);
|
|
785
|
+
if (node.placeholder !== null) {
|
|
786
|
+
writeString(writer, node.placeholder.kind, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
787
|
+
writer.bool(node.placeholder.index !== null);
|
|
788
|
+
if (node.placeholder.index !== null) writer.u32(node.placeholder.index);
|
|
789
|
+
}
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (node.kind === "group") {
|
|
793
|
+
writer.u8(1);
|
|
794
|
+
for (const value of [
|
|
795
|
+
node.childOffsetX,
|
|
796
|
+
node.childOffsetY,
|
|
797
|
+
node.childExtentWidth,
|
|
798
|
+
node.childExtentHeight,
|
|
799
|
+
])
|
|
800
|
+
writer.i64(coordinate(value, "presentation group coordinate"));
|
|
801
|
+
writer.count(
|
|
802
|
+
node.children.length,
|
|
803
|
+
PRESENTATION_ARTIFACT_MAX_GROUP_CHILDREN,
|
|
804
|
+
"presentation group children",
|
|
805
|
+
);
|
|
806
|
+
for (const id of node.children) writeId(writer, id);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (node.kind === "connector") {
|
|
810
|
+
writer.u8(2);
|
|
811
|
+
writer.u8(
|
|
812
|
+
node.connectorKind === "straight"
|
|
813
|
+
? 0
|
|
814
|
+
: node.connectorKind === "elbow"
|
|
815
|
+
? 1
|
|
816
|
+
: node.connectorKind === "curved"
|
|
817
|
+
? 2
|
|
818
|
+
: bad("connector kind"),
|
|
819
|
+
);
|
|
820
|
+
writeEndpoint(writer, node.start);
|
|
821
|
+
writeEndpoint(writer, node.end);
|
|
822
|
+
writeLine(writer, node.line);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (node.kind === "chart") {
|
|
826
|
+
writer.u8(3);
|
|
827
|
+
writer.u8(
|
|
828
|
+
({ bar: 0, line: 1, area: 2, pie: 3, doughnut: 4, scatter: 5, bubble: 6, radar: 7 } as const)[
|
|
829
|
+
node.chartType
|
|
830
|
+
] ?? bad("chart type"),
|
|
831
|
+
);
|
|
832
|
+
writeRichText(writer, node.title);
|
|
833
|
+
writer.count(
|
|
834
|
+
node.series.length,
|
|
835
|
+
PRESENTATION_ARTIFACT_MAX_CHART_SERIES,
|
|
836
|
+
"presentation chart series",
|
|
837
|
+
);
|
|
838
|
+
let points = 0;
|
|
839
|
+
for (const series of node.series) {
|
|
840
|
+
writeString(writer, series.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
841
|
+
points +=
|
|
842
|
+
series.categories.length +
|
|
843
|
+
series.values.length +
|
|
844
|
+
series.xValues.length +
|
|
845
|
+
series.bubbleSizes.length;
|
|
846
|
+
if (points > PRESENTATION_ARTIFACT_MAX_CHART_POINTS)
|
|
847
|
+
throw new RangeError("presentation chart points exceed their limit");
|
|
848
|
+
writeStringVector(writer, series.categories);
|
|
849
|
+
writeNumberVector(writer, series.values);
|
|
850
|
+
writeNumberVector(writer, series.xValues);
|
|
851
|
+
writeNumberVector(writer, series.bubbleSizes);
|
|
852
|
+
}
|
|
853
|
+
writer.bool(node.hasLegend);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (node.kind === "table") {
|
|
857
|
+
writer.u8(4);
|
|
858
|
+
const columns = node.rows[0]?.length ?? 0;
|
|
859
|
+
const cells = node.rows.length * columns;
|
|
860
|
+
if (
|
|
861
|
+
node.rows.length === 0 ||
|
|
862
|
+
columns === 0 ||
|
|
863
|
+
node.rows.length > PRESENTATION_ARTIFACT_MAX_TABLE_ROWS ||
|
|
864
|
+
columns > PRESENTATION_ARTIFACT_MAX_TABLE_COLUMNS ||
|
|
865
|
+
cells > PRESENTATION_ARTIFACT_MAX_TABLE_CELLS ||
|
|
866
|
+
node.rows.some((row) => row.length !== columns)
|
|
867
|
+
)
|
|
868
|
+
throw new RangeError("presentation table dimensions are invalid");
|
|
869
|
+
writer.u32(node.rows.length);
|
|
870
|
+
writer.u32(columns);
|
|
871
|
+
for (const row of node.rows)
|
|
872
|
+
for (const cell of row) {
|
|
873
|
+
writer.bool(cell !== null);
|
|
874
|
+
if (cell !== null) {
|
|
875
|
+
writeRichText(writer, cell.text);
|
|
876
|
+
writeFill(writer, cell.fill);
|
|
877
|
+
writer.u16(cell.rowSpan);
|
|
878
|
+
writer.u16(cell.columnSpan);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
writer.count(
|
|
882
|
+
node.columnWidths.length,
|
|
883
|
+
PRESENTATION_ARTIFACT_MAX_TABLE_COLUMNS,
|
|
884
|
+
"presentation table widths",
|
|
885
|
+
);
|
|
886
|
+
for (const width of node.columnWidths)
|
|
887
|
+
writer.i64(coordinate(width, "presentation table width"));
|
|
888
|
+
writer.count(
|
|
889
|
+
node.rowHeights.length,
|
|
890
|
+
PRESENTATION_ARTIFACT_MAX_TABLE_ROWS,
|
|
891
|
+
"presentation table heights",
|
|
892
|
+
);
|
|
893
|
+
for (const height of node.rowHeights)
|
|
894
|
+
writer.i64(coordinate(height, "presentation table height"));
|
|
895
|
+
writeLine(writer, node.line);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (node.kind === "media") {
|
|
899
|
+
if (!(node.digest instanceof Uint8Array) || node.digest.byteLength !== 32)
|
|
900
|
+
throw new TypeError("presentation media digest must be 32 bytes");
|
|
901
|
+
writer.u8(5);
|
|
902
|
+
writer.bytes(node.digest);
|
|
903
|
+
writeString(writer, node.contentType, 255);
|
|
904
|
+
writeString(writer, node.altText, PRESENTATION_ARTIFACT_MAX_TEXT_BYTES);
|
|
905
|
+
writer.u8(node.fit === "contain" ? 0 : node.fit === "cover" ? 1 : bad("media fit"));
|
|
906
|
+
writer.u32(node.intrinsicWidth);
|
|
907
|
+
writer.u32(node.intrinsicHeight);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
return bad("presentation node kind");
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function readNodeKind(reader: ArtifactBinaryReader): PresentationArtifactNodeKind {
|
|
914
|
+
const tag = reader.u8();
|
|
915
|
+
if (tag === 0) {
|
|
916
|
+
const geometryTag = reader.u8();
|
|
917
|
+
const geometries = [
|
|
918
|
+
"text-box",
|
|
919
|
+
"rectangle",
|
|
920
|
+
"rounded-rectangle",
|
|
921
|
+
"ellipse",
|
|
922
|
+
"triangle",
|
|
923
|
+
"right-arrow",
|
|
924
|
+
"line",
|
|
925
|
+
] as const;
|
|
926
|
+
const geometry = geometries[geometryTag];
|
|
927
|
+
if (!geometry) return bad("shape geometry");
|
|
928
|
+
const fill = readFill(reader);
|
|
929
|
+
const line = readLine(reader);
|
|
930
|
+
const text = reader.bool("optional presentation text") ? readRichText(reader) : null;
|
|
931
|
+
const placeholder = reader.bool("optional presentation placeholder")
|
|
932
|
+
? Object.freeze({
|
|
933
|
+
kind: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
934
|
+
index: reader.bool("optional placeholder index") ? reader.u32() : null,
|
|
935
|
+
})
|
|
936
|
+
: null;
|
|
937
|
+
return Object.freeze({ kind: "shape", geometry, fill, line, text, placeholder });
|
|
938
|
+
}
|
|
939
|
+
if (tag === 1) {
|
|
940
|
+
const childOffsetX = readCoordinate(reader, "presentation group coordinate");
|
|
941
|
+
const childOffsetY = readCoordinate(reader, "presentation group coordinate");
|
|
942
|
+
const childExtentWidth = readCoordinate(reader, "presentation group coordinate");
|
|
943
|
+
const childExtentHeight = readCoordinate(reader, "presentation group coordinate");
|
|
944
|
+
const count = reader.count(
|
|
945
|
+
PRESENTATION_ARTIFACT_MAX_GROUP_CHILDREN,
|
|
946
|
+
"presentation group children",
|
|
947
|
+
16,
|
|
948
|
+
);
|
|
949
|
+
const children: string[] = [];
|
|
950
|
+
for (let index = 0; index < count; index += 1) children.push(readId(reader));
|
|
951
|
+
return Object.freeze({
|
|
952
|
+
kind: "group",
|
|
953
|
+
childOffsetX,
|
|
954
|
+
childOffsetY,
|
|
955
|
+
childExtentWidth,
|
|
956
|
+
childExtentHeight,
|
|
957
|
+
children: Object.freeze(children),
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
if (tag === 2) {
|
|
961
|
+
const kindTag = reader.u8();
|
|
962
|
+
return Object.freeze({
|
|
963
|
+
kind: "connector",
|
|
964
|
+
connectorKind:
|
|
965
|
+
kindTag === 0
|
|
966
|
+
? "straight"
|
|
967
|
+
: kindTag === 1
|
|
968
|
+
? "elbow"
|
|
969
|
+
: kindTag === 2
|
|
970
|
+
? "curved"
|
|
971
|
+
: bad("connector kind"),
|
|
972
|
+
start: readEndpoint(reader),
|
|
973
|
+
end: readEndpoint(reader),
|
|
974
|
+
line: readLine(reader),
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
if (tag === 3) {
|
|
978
|
+
const chartTag = reader.u8();
|
|
979
|
+
const types = ["bar", "line", "area", "pie", "doughnut", "scatter", "bubble", "radar"] as const;
|
|
980
|
+
const chartType = types[chartTag];
|
|
981
|
+
if (!chartType) return bad("chart type");
|
|
982
|
+
const title = readRichText(reader);
|
|
983
|
+
const count = reader.count(PRESENTATION_ARTIFACT_MAX_CHART_SERIES, "presentation chart series");
|
|
984
|
+
const series: PresentationArtifactChartSeries[] = [];
|
|
985
|
+
let points = 0;
|
|
986
|
+
for (let index = 0; index < count; index += 1) {
|
|
987
|
+
const item = Object.freeze({
|
|
988
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
989
|
+
categories: readStringVector(reader),
|
|
990
|
+
values: readNumberVector(reader),
|
|
991
|
+
xValues: readNumberVector(reader),
|
|
992
|
+
bubbleSizes: readNumberVector(reader),
|
|
993
|
+
});
|
|
994
|
+
points +=
|
|
995
|
+
item.categories.length + item.values.length + item.xValues.length + item.bubbleSizes.length;
|
|
996
|
+
if (points > PRESENTATION_ARTIFACT_MAX_CHART_POINTS)
|
|
997
|
+
throw new RangeError("presentation chart points exceed their limit");
|
|
998
|
+
series.push(item);
|
|
999
|
+
}
|
|
1000
|
+
return Object.freeze({
|
|
1001
|
+
kind: "chart",
|
|
1002
|
+
chartType,
|
|
1003
|
+
title,
|
|
1004
|
+
series: Object.freeze(series),
|
|
1005
|
+
hasLegend: reader.bool("presentation boolean"),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (tag === 4) {
|
|
1009
|
+
const rowCount = reader.count(PRESENTATION_ARTIFACT_MAX_TABLE_ROWS, "presentation table rows");
|
|
1010
|
+
const columnCount = reader.count(
|
|
1011
|
+
PRESENTATION_ARTIFACT_MAX_TABLE_COLUMNS,
|
|
1012
|
+
"presentation table columns",
|
|
1013
|
+
);
|
|
1014
|
+
const cells = rowCount * columnCount;
|
|
1015
|
+
if (rowCount === 0 || columnCount === 0 || cells > PRESENTATION_ARTIFACT_MAX_TABLE_CELLS)
|
|
1016
|
+
throw new RangeError("presentation table dimensions are invalid");
|
|
1017
|
+
const rows: (readonly (PresentationArtifactTableCell | null)[])[] = [];
|
|
1018
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
1019
|
+
const row: (PresentationArtifactTableCell | null)[] = [];
|
|
1020
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1)
|
|
1021
|
+
row.push(
|
|
1022
|
+
reader.bool("optional table cell")
|
|
1023
|
+
? Object.freeze({
|
|
1024
|
+
text: readRichText(reader),
|
|
1025
|
+
fill: readFill(reader),
|
|
1026
|
+
rowSpan: reader.u16(),
|
|
1027
|
+
columnSpan: reader.u16(),
|
|
1028
|
+
})
|
|
1029
|
+
: null,
|
|
1030
|
+
);
|
|
1031
|
+
rows.push(Object.freeze(row));
|
|
1032
|
+
}
|
|
1033
|
+
const widthCount = reader.count(
|
|
1034
|
+
PRESENTATION_ARTIFACT_MAX_TABLE_COLUMNS,
|
|
1035
|
+
"presentation table widths",
|
|
1036
|
+
8,
|
|
1037
|
+
);
|
|
1038
|
+
const columnWidths: number[] = [];
|
|
1039
|
+
for (let index = 0; index < widthCount; index += 1)
|
|
1040
|
+
columnWidths.push(readCoordinate(reader, "presentation table width"));
|
|
1041
|
+
const heightCount = reader.count(
|
|
1042
|
+
PRESENTATION_ARTIFACT_MAX_TABLE_ROWS,
|
|
1043
|
+
"presentation table heights",
|
|
1044
|
+
8,
|
|
1045
|
+
);
|
|
1046
|
+
const rowHeights: number[] = [];
|
|
1047
|
+
for (let index = 0; index < heightCount; index += 1)
|
|
1048
|
+
rowHeights.push(readCoordinate(reader, "presentation table height"));
|
|
1049
|
+
return Object.freeze({
|
|
1050
|
+
kind: "table",
|
|
1051
|
+
rows: Object.freeze(rows),
|
|
1052
|
+
columnWidths: Object.freeze(columnWidths),
|
|
1053
|
+
rowHeights: Object.freeze(rowHeights),
|
|
1054
|
+
line: readLine(reader),
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
if (tag === 5) {
|
|
1058
|
+
const digest = reader.bytes(32).slice();
|
|
1059
|
+
const contentType = readString(reader, 255);
|
|
1060
|
+
const altText = readString(reader, PRESENTATION_ARTIFACT_MAX_TEXT_BYTES);
|
|
1061
|
+
const fitTag = reader.u8();
|
|
1062
|
+
return Object.freeze({
|
|
1063
|
+
kind: "media",
|
|
1064
|
+
digest,
|
|
1065
|
+
contentType,
|
|
1066
|
+
altText,
|
|
1067
|
+
fit: fitTag === 0 ? "contain" : fitTag === 1 ? "cover" : bad("media fit"),
|
|
1068
|
+
intrinsicWidth: reader.u32(),
|
|
1069
|
+
intrinsicHeight: reader.u32(),
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
return bad("presentation node kind");
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
function writeEndpoint(
|
|
1076
|
+
writer: ArtifactBinaryWriter,
|
|
1077
|
+
endpoint: PresentationArtifactConnectorEndpoint,
|
|
1078
|
+
): void {
|
|
1079
|
+
writeOptionalId(writer, endpoint.nodeId);
|
|
1080
|
+
writer.i64(coordinate(endpoint.x, "connector x"));
|
|
1081
|
+
writer.i64(coordinate(endpoint.y, "connector y"));
|
|
1082
|
+
}
|
|
1083
|
+
function readEndpoint(reader: ArtifactBinaryReader): PresentationArtifactConnectorEndpoint {
|
|
1084
|
+
return Object.freeze({
|
|
1085
|
+
nodeId: readOptionalId(reader),
|
|
1086
|
+
x: readCoordinate(reader, "connector x"),
|
|
1087
|
+
y: readCoordinate(reader, "connector y"),
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function writeStringVector(writer: ArtifactBinaryWriter, values: readonly string[]): void {
|
|
1091
|
+
writer.count(values.length, PRESENTATION_ARTIFACT_MAX_CHART_POINTS, "presentation chart points");
|
|
1092
|
+
for (const value of values) writeString(writer, value, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
1093
|
+
}
|
|
1094
|
+
function readStringVector(reader: ArtifactBinaryReader): readonly string[] {
|
|
1095
|
+
const count = reader.count(PRESENTATION_ARTIFACT_MAX_CHART_POINTS, "presentation chart points");
|
|
1096
|
+
const values: string[] = [];
|
|
1097
|
+
for (let index = 0; index < count; index += 1)
|
|
1098
|
+
values.push(readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES));
|
|
1099
|
+
return Object.freeze(values);
|
|
1100
|
+
}
|
|
1101
|
+
function writeNumberVector(writer: ArtifactBinaryWriter, values: readonly number[]): void {
|
|
1102
|
+
writer.count(values.length, PRESENTATION_ARTIFACT_MAX_CHART_POINTS, "presentation chart points");
|
|
1103
|
+
for (const value of values) writer.f64(value, "presentation chart value");
|
|
1104
|
+
}
|
|
1105
|
+
function readNumberVector(reader: ArtifactBinaryReader): readonly number[] {
|
|
1106
|
+
const count = reader.count(
|
|
1107
|
+
PRESENTATION_ARTIFACT_MAX_CHART_POINTS,
|
|
1108
|
+
"presentation chart points",
|
|
1109
|
+
8,
|
|
1110
|
+
);
|
|
1111
|
+
const values: number[] = [];
|
|
1112
|
+
for (let index = 0; index < count; index += 1)
|
|
1113
|
+
values.push(reader.f64("presentation chart value"));
|
|
1114
|
+
return Object.freeze(values);
|
|
1115
|
+
}
|
|
1116
|
+
function coordinate(value: number, label: string): number {
|
|
1117
|
+
if (!Number.isSafeInteger(value) || Math.abs(value) > PRESENTATION_ARTIFACT_MAX_COORDINATE)
|
|
1118
|
+
throw new RangeError(`${label} exceeds its bound`);
|
|
1119
|
+
return value;
|
|
1120
|
+
}
|
|
1121
|
+
function readCoordinate(reader: ArtifactBinaryReader, label: string): number {
|
|
1122
|
+
return coordinate(reader.i64Safe(label), label);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
export type PresentationArtifactQuery =
|
|
1126
|
+
| Readonly<{
|
|
1127
|
+
kind: "viewport";
|
|
1128
|
+
owner: PresentationArtifactOwner;
|
|
1129
|
+
viewport: PresentationArtifactRect;
|
|
1130
|
+
maxNodes: number;
|
|
1131
|
+
maxBytes: number;
|
|
1132
|
+
}>
|
|
1133
|
+
| Readonly<{
|
|
1134
|
+
kind: "hit-test";
|
|
1135
|
+
owner: PresentationArtifactOwner;
|
|
1136
|
+
x: number;
|
|
1137
|
+
y: number;
|
|
1138
|
+
maxNodes: number;
|
|
1139
|
+
maxBytes: number;
|
|
1140
|
+
}>
|
|
1141
|
+
| Readonly<{
|
|
1142
|
+
kind: "resolved-slide";
|
|
1143
|
+
slideId: PresentationArtifactStableId;
|
|
1144
|
+
maxNodes: number;
|
|
1145
|
+
maxBytes: number;
|
|
1146
|
+
}>
|
|
1147
|
+
| Readonly<{ kind: "metadata"; maxBytes: number }>
|
|
1148
|
+
| Readonly<{
|
|
1149
|
+
kind: "slide-catalog";
|
|
1150
|
+
startSlide: number;
|
|
1151
|
+
maxSlides: number;
|
|
1152
|
+
maxTextBytes: number;
|
|
1153
|
+
maxBytes: number;
|
|
1154
|
+
}>
|
|
1155
|
+
| Readonly<{
|
|
1156
|
+
kind: "editor-slide";
|
|
1157
|
+
slideId: PresentationArtifactStableId;
|
|
1158
|
+
maxNodes: number;
|
|
1159
|
+
maxTextBytes: number;
|
|
1160
|
+
maxBytes: number;
|
|
1161
|
+
}>;
|
|
1162
|
+
|
|
1163
|
+
export function encodePresentationArtifactQuery(query: PresentationArtifactQuery): Uint8Array {
|
|
1164
|
+
const payload = new ArtifactBinaryWriter(PRESENTATION_ARTIFACT_QUERY_MAX_BYTES);
|
|
1165
|
+
let tag: number;
|
|
1166
|
+
let maxItems = 0;
|
|
1167
|
+
if (query.kind === "viewport") {
|
|
1168
|
+
tag = 0;
|
|
1169
|
+
maxItems = query.maxNodes;
|
|
1170
|
+
validateQueryLimits(maxItems, query.maxBytes, 49);
|
|
1171
|
+
writeOwner(payload, query.owner);
|
|
1172
|
+
writeRect(payload, query.viewport);
|
|
1173
|
+
} else if (query.kind === "hit-test") {
|
|
1174
|
+
tag = 1;
|
|
1175
|
+
maxItems = query.maxNodes;
|
|
1176
|
+
validateQueryLimits(maxItems, query.maxBytes, 49);
|
|
1177
|
+
writeOwner(payload, query.owner);
|
|
1178
|
+
payload.i64(coordinate(query.x, "presentation hit-test x"));
|
|
1179
|
+
payload.i64(coordinate(query.y, "presentation hit-test y"));
|
|
1180
|
+
} else if (query.kind === "resolved-slide") {
|
|
1181
|
+
tag = 2;
|
|
1182
|
+
maxItems = query.maxNodes;
|
|
1183
|
+
validateQueryLimits(maxItems, query.maxBytes, 16);
|
|
1184
|
+
writeId(payload, query.slideId);
|
|
1185
|
+
} else if (query.kind === "metadata") {
|
|
1186
|
+
tag = 3;
|
|
1187
|
+
validateResponseBytes(query.maxBytes, 44);
|
|
1188
|
+
} else if (query.kind === "slide-catalog") {
|
|
1189
|
+
tag = 4;
|
|
1190
|
+
maxItems = query.maxSlides;
|
|
1191
|
+
validateCatalogQueryLimits(maxItems, query.maxTextBytes, query.maxBytes);
|
|
1192
|
+
validateResponseBytes(query.maxBytes, 13);
|
|
1193
|
+
payload.u32(query.startSlide);
|
|
1194
|
+
payload.u32(query.maxTextBytes);
|
|
1195
|
+
} else if (query.kind === "editor-slide") {
|
|
1196
|
+
tag = 5;
|
|
1197
|
+
maxItems = query.maxNodes;
|
|
1198
|
+
validateQueryLimits(maxItems, query.maxBytes, 35);
|
|
1199
|
+
validateQueryTextBytes(query.maxTextBytes);
|
|
1200
|
+
writeId(payload, query.slideId);
|
|
1201
|
+
payload.u32(query.maxTextBytes);
|
|
1202
|
+
} else return bad("presentation query kind");
|
|
1203
|
+
const maxBytes = query.maxBytes;
|
|
1204
|
+
const writer = new ArtifactBinaryWriter(PRESENTATION_ARTIFACT_QUERY_MAX_BYTES);
|
|
1205
|
+
writer.bytes(QUERY_MAGIC);
|
|
1206
|
+
writer.u16(1);
|
|
1207
|
+
writer.u16(0);
|
|
1208
|
+
writer.u8(tag);
|
|
1209
|
+
writer.bytes(new Uint8Array(3));
|
|
1210
|
+
writer.u32(maxItems);
|
|
1211
|
+
writer.u32(maxBytes);
|
|
1212
|
+
writer.u32(payload.length);
|
|
1213
|
+
writer.bytes(payload.finish());
|
|
1214
|
+
writer.u64(fnv1a64(writer.view()));
|
|
1215
|
+
return writer.finish();
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
export function decodePresentationArtifactQuery(bytes: Uint8Array): PresentationArtifactQuery {
|
|
1219
|
+
const envelope = decodePresentationQueryEnvelope(bytes);
|
|
1220
|
+
const reader = new ArtifactBinaryReader(envelope.payload);
|
|
1221
|
+
let query: PresentationArtifactQuery;
|
|
1222
|
+
if (envelope.tag === 0) {
|
|
1223
|
+
validateQueryLimits(envelope.maxItems, envelope.maxBytes, 49);
|
|
1224
|
+
query = Object.freeze({
|
|
1225
|
+
kind: "viewport",
|
|
1226
|
+
owner: readOwner(reader),
|
|
1227
|
+
viewport: readRect(reader),
|
|
1228
|
+
maxNodes: envelope.maxItems,
|
|
1229
|
+
maxBytes: envelope.maxBytes,
|
|
1230
|
+
});
|
|
1231
|
+
} else if (envelope.tag === 1) {
|
|
1232
|
+
validateQueryLimits(envelope.maxItems, envelope.maxBytes, 49);
|
|
1233
|
+
query = Object.freeze({
|
|
1234
|
+
kind: "hit-test",
|
|
1235
|
+
owner: readOwner(reader),
|
|
1236
|
+
x: readCoordinate(reader, "presentation hit-test x"),
|
|
1237
|
+
y: readCoordinate(reader, "presentation hit-test y"),
|
|
1238
|
+
maxNodes: envelope.maxItems,
|
|
1239
|
+
maxBytes: envelope.maxBytes,
|
|
1240
|
+
});
|
|
1241
|
+
} else if (envelope.tag === 2) {
|
|
1242
|
+
validateQueryLimits(envelope.maxItems, envelope.maxBytes, 16);
|
|
1243
|
+
query = Object.freeze({
|
|
1244
|
+
kind: "resolved-slide",
|
|
1245
|
+
slideId: readId(reader),
|
|
1246
|
+
maxNodes: envelope.maxItems,
|
|
1247
|
+
maxBytes: envelope.maxBytes,
|
|
1248
|
+
});
|
|
1249
|
+
} else if (envelope.tag === 3) {
|
|
1250
|
+
if (envelope.maxItems !== 0)
|
|
1251
|
+
throw new TypeError("presentation metadata max items must be zero");
|
|
1252
|
+
validateResponseBytes(envelope.maxBytes, 44);
|
|
1253
|
+
query = Object.freeze({ kind: "metadata", maxBytes: envelope.maxBytes });
|
|
1254
|
+
} else if (envelope.tag === 4) {
|
|
1255
|
+
const startSlide = reader.u32();
|
|
1256
|
+
const maxTextBytes = reader.u32();
|
|
1257
|
+
validateCatalogQueryLimits(envelope.maxItems, maxTextBytes, envelope.maxBytes);
|
|
1258
|
+
validateResponseBytes(envelope.maxBytes, 13);
|
|
1259
|
+
query = Object.freeze({
|
|
1260
|
+
kind: "slide-catalog",
|
|
1261
|
+
startSlide,
|
|
1262
|
+
maxSlides: envelope.maxItems,
|
|
1263
|
+
maxTextBytes,
|
|
1264
|
+
maxBytes: envelope.maxBytes,
|
|
1265
|
+
});
|
|
1266
|
+
} else {
|
|
1267
|
+
validateQueryLimits(envelope.maxItems, envelope.maxBytes, 35);
|
|
1268
|
+
const slideId = readId(reader);
|
|
1269
|
+
const maxTextBytes = reader.u32();
|
|
1270
|
+
validateQueryTextBytes(maxTextBytes);
|
|
1271
|
+
query = Object.freeze({
|
|
1272
|
+
kind: "editor-slide",
|
|
1273
|
+
slideId,
|
|
1274
|
+
maxNodes: envelope.maxItems,
|
|
1275
|
+
maxTextBytes,
|
|
1276
|
+
maxBytes: envelope.maxBytes,
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
reader.done("presentation query contains trailing bytes");
|
|
1280
|
+
return query;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
export function assertCanonicalPresentationArtifactQueryBytes(bytes: Uint8Array): void {
|
|
1284
|
+
if (!equalBytes(bytes, encodePresentationArtifactQuery(decodePresentationArtifactQuery(bytes))))
|
|
1285
|
+
throw new TypeError("presentation query is not canonically encoded");
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function decodePresentationQueryEnvelope(bytes: Uint8Array): {
|
|
1289
|
+
tag: number;
|
|
1290
|
+
maxItems: number;
|
|
1291
|
+
maxBytes: number;
|
|
1292
|
+
payload: Uint8Array;
|
|
1293
|
+
} {
|
|
1294
|
+
if (
|
|
1295
|
+
!(bytes instanceof Uint8Array) ||
|
|
1296
|
+
bytes.length > PRESENTATION_ARTIFACT_QUERY_MAX_BYTES ||
|
|
1297
|
+
bytes.length < QUERY_HEADER_BYTES + CHECKSUM_BYTES
|
|
1298
|
+
)
|
|
1299
|
+
throw new TypeError("invalid presentation query envelope");
|
|
1300
|
+
const reader = new ArtifactBinaryReader(bytes);
|
|
1301
|
+
if (!equalBytes(reader.bytes(8), QUERY_MAGIC))
|
|
1302
|
+
throw new TypeError("invalid presentation query magic");
|
|
1303
|
+
if (reader.u16() !== 1) throw new TypeError("unsupported presentation query version");
|
|
1304
|
+
if (reader.u16() !== 0) throw new TypeError("reserved presentation query flags must be zero");
|
|
1305
|
+
const tag = reader.u8();
|
|
1306
|
+
if (tag > 5) throw new TypeError("invalid presentation query tag");
|
|
1307
|
+
if (!reader.bytes(3).every((value) => value === 0))
|
|
1308
|
+
throw new TypeError("reserved presentation query bytes must be zero");
|
|
1309
|
+
const maxItems = reader.u32();
|
|
1310
|
+
const maxBytes = reader.u32();
|
|
1311
|
+
const payloadLength = reader.u32();
|
|
1312
|
+
const expected = QUERY_HEADER_BYTES + payloadLength + CHECKSUM_BYTES;
|
|
1313
|
+
if (bytes.length !== expected)
|
|
1314
|
+
throw new TypeError(
|
|
1315
|
+
bytes.length < expected
|
|
1316
|
+
? "truncated presentation query"
|
|
1317
|
+
: "presentation query contains trailing bytes",
|
|
1318
|
+
);
|
|
1319
|
+
const payload = reader.bytes(payloadLength);
|
|
1320
|
+
const checksum = reader.u64BigInt();
|
|
1321
|
+
if (checksum !== fnv1a64(bytes.subarray(0, -8)))
|
|
1322
|
+
throw new TypeError("presentation query checksum does not match");
|
|
1323
|
+
return { tag, maxItems, maxBytes, payload };
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
export type PresentationArtifactProjectedNode = Readonly<{
|
|
1327
|
+
id: string;
|
|
1328
|
+
owner: PresentationArtifactOwner;
|
|
1329
|
+
parentId: string | null;
|
|
1330
|
+
nodeKind: PresentationArtifactNodeKind["kind"];
|
|
1331
|
+
bounds: PresentationArtifactRect;
|
|
1332
|
+
paintOrder: number;
|
|
1333
|
+
}>;
|
|
1334
|
+
export type PresentationArtifactResolvedNode = Readonly<{
|
|
1335
|
+
id: string;
|
|
1336
|
+
source: PresentationArtifactOwner;
|
|
1337
|
+
inherited: boolean;
|
|
1338
|
+
}>;
|
|
1339
|
+
export type PresentationArtifactSlideLayoutFacts = Readonly<{
|
|
1340
|
+
id: PresentationArtifactStableId;
|
|
1341
|
+
name: string;
|
|
1342
|
+
masterId: PresentationArtifactStableId | null;
|
|
1343
|
+
background: PresentationArtifactFill;
|
|
1344
|
+
}>;
|
|
1345
|
+
export type PresentationArtifactSlideCatalogItem = Readonly<{
|
|
1346
|
+
index: number;
|
|
1347
|
+
id: PresentationArtifactStableId;
|
|
1348
|
+
title: string;
|
|
1349
|
+
background: PresentationArtifactFill;
|
|
1350
|
+
layout: PresentationArtifactSlideLayoutFacts | null;
|
|
1351
|
+
}>;
|
|
1352
|
+
export type PresentationArtifactEditorSceneNode = Readonly<{
|
|
1353
|
+
id: PresentationArtifactStableId;
|
|
1354
|
+
source: PresentationArtifactOwner;
|
|
1355
|
+
inherited: boolean;
|
|
1356
|
+
parentId: PresentationArtifactStableId | null;
|
|
1357
|
+
order: number;
|
|
1358
|
+
name: string;
|
|
1359
|
+
bounds: PresentationArtifactRect;
|
|
1360
|
+
transform: PresentationArtifactTransform;
|
|
1361
|
+
content: PresentationArtifactNodeKind;
|
|
1362
|
+
}>;
|
|
1363
|
+
export type PresentationArtifactQueryResponse =
|
|
1364
|
+
| Readonly<{
|
|
1365
|
+
kind: "viewport";
|
|
1366
|
+
revision: bigint;
|
|
1367
|
+
owner: PresentationArtifactOwner;
|
|
1368
|
+
viewport: PresentationArtifactRect;
|
|
1369
|
+
nodes: readonly PresentationArtifactProjectedNode[];
|
|
1370
|
+
truncated: boolean;
|
|
1371
|
+
}>
|
|
1372
|
+
| Readonly<{
|
|
1373
|
+
kind: "hit-test";
|
|
1374
|
+
revision: bigint;
|
|
1375
|
+
owner: PresentationArtifactOwner;
|
|
1376
|
+
viewport: PresentationArtifactRect;
|
|
1377
|
+
nodes: readonly PresentationArtifactProjectedNode[];
|
|
1378
|
+
truncated: boolean;
|
|
1379
|
+
}>
|
|
1380
|
+
| Readonly<{
|
|
1381
|
+
kind: "resolved-slide";
|
|
1382
|
+
revision: bigint;
|
|
1383
|
+
slideId: string;
|
|
1384
|
+
nodes: readonly PresentationArtifactResolvedNode[];
|
|
1385
|
+
truncated: boolean;
|
|
1386
|
+
}>
|
|
1387
|
+
| Readonly<{
|
|
1388
|
+
kind: "metadata";
|
|
1389
|
+
revision: bigint;
|
|
1390
|
+
presentationId: string;
|
|
1391
|
+
slideSize: Readonly<{ width: number; height: number }>;
|
|
1392
|
+
masters: number;
|
|
1393
|
+
layouts: number;
|
|
1394
|
+
slides: number;
|
|
1395
|
+
}>
|
|
1396
|
+
| Readonly<{
|
|
1397
|
+
kind: "slide-catalog";
|
|
1398
|
+
revision: bigint;
|
|
1399
|
+
startSlide: number;
|
|
1400
|
+
nextSlide: number | null;
|
|
1401
|
+
projectedTextBytes: number;
|
|
1402
|
+
slides: readonly PresentationArtifactSlideCatalogItem[];
|
|
1403
|
+
truncated: boolean;
|
|
1404
|
+
}>
|
|
1405
|
+
| Readonly<{
|
|
1406
|
+
kind: "editor-slide";
|
|
1407
|
+
revision: bigint;
|
|
1408
|
+
slide: PresentationArtifactSlideCatalogItem;
|
|
1409
|
+
notes: PresentationArtifactRichText | null;
|
|
1410
|
+
projectedTextBytes: number;
|
|
1411
|
+
nodes: readonly PresentationArtifactEditorSceneNode[];
|
|
1412
|
+
truncated: boolean;
|
|
1413
|
+
}>;
|
|
1414
|
+
export type PresentationArtifactSlideCatalogResponse = Extract<
|
|
1415
|
+
PresentationArtifactQueryResponse,
|
|
1416
|
+
{ kind: "slide-catalog" }
|
|
1417
|
+
>;
|
|
1418
|
+
export type PresentationArtifactEditorSlideResponse = Extract<
|
|
1419
|
+
PresentationArtifactQueryResponse,
|
|
1420
|
+
{ kind: "editor-slide" }
|
|
1421
|
+
>;
|
|
1422
|
+
|
|
1423
|
+
export function encodePresentationArtifactQueryResponse(
|
|
1424
|
+
response: PresentationArtifactQueryResponse,
|
|
1425
|
+
maximumBytes = PRESENTATION_ARTIFACT_QUERY_RESPONSE_MAX_BYTES,
|
|
1426
|
+
): Uint8Array {
|
|
1427
|
+
validateResponseBytes(maximumBytes, 0);
|
|
1428
|
+
const payload = new ArtifactBinaryWriter(maximumBytes - RESPONSE_HEADER_BYTES - CHECKSUM_BYTES);
|
|
1429
|
+
let tag: number;
|
|
1430
|
+
let itemCount: number;
|
|
1431
|
+
let truncated = false;
|
|
1432
|
+
if (response.kind === "viewport" || response.kind === "hit-test") {
|
|
1433
|
+
tag = response.kind === "viewport" ? 0 : 1;
|
|
1434
|
+
itemCount = response.nodes.length;
|
|
1435
|
+
truncated = response.truncated;
|
|
1436
|
+
validateProjectedResponse(response);
|
|
1437
|
+
writeOwner(payload, response.owner);
|
|
1438
|
+
writeRect(payload, response.viewport);
|
|
1439
|
+
for (const node of response.nodes) {
|
|
1440
|
+
writeId(payload, node.id);
|
|
1441
|
+
writeOwner(payload, node.owner);
|
|
1442
|
+
writeOptionalId(payload, node.parentId);
|
|
1443
|
+
payload.u8(nodeKindTag(node.nodeKind));
|
|
1444
|
+
writeRect(payload, node.bounds);
|
|
1445
|
+
payload.u32(node.paintOrder);
|
|
1446
|
+
}
|
|
1447
|
+
} else if (response.kind === "resolved-slide") {
|
|
1448
|
+
tag = 2;
|
|
1449
|
+
itemCount = response.nodes.length;
|
|
1450
|
+
truncated = response.truncated;
|
|
1451
|
+
validateResolvedResponse(response);
|
|
1452
|
+
writeId(payload, response.slideId);
|
|
1453
|
+
for (const node of response.nodes) {
|
|
1454
|
+
writeId(payload, node.id);
|
|
1455
|
+
writeOwner(payload, node.source);
|
|
1456
|
+
payload.bool(node.inherited);
|
|
1457
|
+
}
|
|
1458
|
+
} else if (response.kind === "metadata") {
|
|
1459
|
+
tag = 3;
|
|
1460
|
+
itemCount = 1;
|
|
1461
|
+
assertAllocatedId(response.presentationId, "presentation metadata id");
|
|
1462
|
+
if (response.slideSize.width <= 0 || response.slideSize.height <= 0)
|
|
1463
|
+
throw new TypeError("invalid presentation slide size");
|
|
1464
|
+
writeId(payload, response.presentationId);
|
|
1465
|
+
payload.i64(coordinate(response.slideSize.width, "presentation slide width"));
|
|
1466
|
+
payload.i64(coordinate(response.slideSize.height, "presentation slide height"));
|
|
1467
|
+
payload.u32(response.masters);
|
|
1468
|
+
payload.u32(response.layouts);
|
|
1469
|
+
payload.u32(response.slides);
|
|
1470
|
+
} else if (response.kind === "slide-catalog") {
|
|
1471
|
+
tag = 4;
|
|
1472
|
+
itemCount = response.slides.length;
|
|
1473
|
+
truncated = response.truncated;
|
|
1474
|
+
validateSlideCatalogResponse(response);
|
|
1475
|
+
payload.u32(response.startSlide);
|
|
1476
|
+
payload.bool(response.nextSlide !== null);
|
|
1477
|
+
if (response.nextSlide !== null) payload.u32(response.nextSlide);
|
|
1478
|
+
payload.u32(response.projectedTextBytes);
|
|
1479
|
+
for (const slide of response.slides) writeSlideCatalogItem(payload, slide);
|
|
1480
|
+
} else if (response.kind === "editor-slide") {
|
|
1481
|
+
tag = 5;
|
|
1482
|
+
itemCount = response.nodes.length;
|
|
1483
|
+
truncated = response.truncated;
|
|
1484
|
+
validateEditorSlideResponse(response);
|
|
1485
|
+
writeSlideCatalogItem(payload, response.slide);
|
|
1486
|
+
payload.bool(response.notes !== null);
|
|
1487
|
+
if (response.notes !== null) writeRichText(payload, response.notes);
|
|
1488
|
+
payload.u32(response.projectedTextBytes);
|
|
1489
|
+
for (const node of response.nodes) writeEditorSceneNode(payload, node);
|
|
1490
|
+
} else return bad("presentation response kind");
|
|
1491
|
+
const writer = new ArtifactBinaryWriter(maximumBytes);
|
|
1492
|
+
writer.bytes(RESPONSE_MAGIC);
|
|
1493
|
+
writer.u16(1);
|
|
1494
|
+
writer.u16(truncated ? 1 : 0);
|
|
1495
|
+
writer.u8(tag);
|
|
1496
|
+
writer.bytes(new Uint8Array(3));
|
|
1497
|
+
writer.u64(response.revision);
|
|
1498
|
+
writer.u32(itemCount);
|
|
1499
|
+
writer.u32(payload.length);
|
|
1500
|
+
writer.bytes(payload.finish());
|
|
1501
|
+
writer.u64(fnv1a64(writer.view()));
|
|
1502
|
+
return writer.finish();
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
export function decodePresentationArtifactQueryResponse(
|
|
1506
|
+
bytes: Uint8Array,
|
|
1507
|
+
): PresentationArtifactQueryResponse {
|
|
1508
|
+
if (
|
|
1509
|
+
!(bytes instanceof Uint8Array) ||
|
|
1510
|
+
bytes.length > PRESENTATION_ARTIFACT_QUERY_RESPONSE_MAX_BYTES ||
|
|
1511
|
+
bytes.length < RESPONSE_HEADER_BYTES + CHECKSUM_BYTES
|
|
1512
|
+
)
|
|
1513
|
+
throw new TypeError("invalid presentation response envelope");
|
|
1514
|
+
const header = new ArtifactBinaryReader(bytes);
|
|
1515
|
+
if (!equalBytes(header.bytes(8), RESPONSE_MAGIC))
|
|
1516
|
+
throw new TypeError("invalid presentation response magic");
|
|
1517
|
+
if (header.u16() !== 1) throw new TypeError("unsupported presentation response version");
|
|
1518
|
+
const flags = header.u16();
|
|
1519
|
+
if ((flags & ~1) !== 0) throw new TypeError("reserved presentation response flags are set");
|
|
1520
|
+
const tag = header.u8();
|
|
1521
|
+
if (tag > 5) throw new TypeError("invalid presentation response tag");
|
|
1522
|
+
if (!header.bytes(3).every((value) => value === 0))
|
|
1523
|
+
throw new TypeError("reserved presentation response bytes are set");
|
|
1524
|
+
const revision = header.u64BigInt();
|
|
1525
|
+
const itemCount = header.u32();
|
|
1526
|
+
const payloadLength = header.u32();
|
|
1527
|
+
const expected = RESPONSE_HEADER_BYTES + payloadLength + CHECKSUM_BYTES;
|
|
1528
|
+
if (bytes.length !== expected)
|
|
1529
|
+
throw new TypeError(
|
|
1530
|
+
bytes.length < expected
|
|
1531
|
+
? "truncated presentation response"
|
|
1532
|
+
: "presentation response contains trailing bytes",
|
|
1533
|
+
);
|
|
1534
|
+
const reader = new ArtifactBinaryReader(header.bytes(payloadLength));
|
|
1535
|
+
const checksum = header.u64BigInt();
|
|
1536
|
+
if (checksum !== fnv1a64(bytes.subarray(0, -8)))
|
|
1537
|
+
throw new TypeError("presentation response checksum does not match");
|
|
1538
|
+
const truncated = (flags & 1) !== 0;
|
|
1539
|
+
let response: PresentationArtifactQueryResponse;
|
|
1540
|
+
if (tag === 0 || tag === 1) {
|
|
1541
|
+
if (itemCount > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
1542
|
+
throw new RangeError("presentation response has too many nodes");
|
|
1543
|
+
const owner = readOwner(reader);
|
|
1544
|
+
assertAllocatedId(owner.id, "presentation response owner");
|
|
1545
|
+
const viewport = readRect(reader);
|
|
1546
|
+
const nodes: PresentationArtifactProjectedNode[] = [];
|
|
1547
|
+
const ids = new Set<string>();
|
|
1548
|
+
let previous: number | null = null;
|
|
1549
|
+
for (let index = 0; index < itemCount; index += 1) {
|
|
1550
|
+
const id = readId(reader);
|
|
1551
|
+
const nodeOwner = readOwner(reader);
|
|
1552
|
+
const parentId = readOptionalId(reader);
|
|
1553
|
+
const kindTag = reader.u8();
|
|
1554
|
+
const nodeKind = readNodeKindTag(kindTag);
|
|
1555
|
+
const bounds = readRect(reader);
|
|
1556
|
+
const paintOrder = reader.u32();
|
|
1557
|
+
assertAllocatedId(id, "presentation response node");
|
|
1558
|
+
if (
|
|
1559
|
+
nodeOwner.kind !== owner.kind ||
|
|
1560
|
+
nodeOwner.id !== owner.id ||
|
|
1561
|
+
ids.has(id) ||
|
|
1562
|
+
parentId === id ||
|
|
1563
|
+
(parentId !== null && !isAllocatedId(parentId)) ||
|
|
1564
|
+
(previous !== null && (tag === 0 ? paintOrder <= previous : paintOrder >= previous))
|
|
1565
|
+
)
|
|
1566
|
+
throw new TypeError("invalid presentation projection node");
|
|
1567
|
+
ids.add(id);
|
|
1568
|
+
previous = paintOrder;
|
|
1569
|
+
nodes.push(Object.freeze({ id, owner: nodeOwner, parentId, nodeKind, bounds, paintOrder }));
|
|
1570
|
+
}
|
|
1571
|
+
response = Object.freeze({
|
|
1572
|
+
kind: tag === 0 ? "viewport" : "hit-test",
|
|
1573
|
+
revision,
|
|
1574
|
+
owner,
|
|
1575
|
+
viewport,
|
|
1576
|
+
nodes: Object.freeze(nodes),
|
|
1577
|
+
truncated,
|
|
1578
|
+
});
|
|
1579
|
+
} else if (tag === 2) {
|
|
1580
|
+
if (itemCount > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
1581
|
+
throw new RangeError("presentation response has too many nodes");
|
|
1582
|
+
const slideId = readId(reader);
|
|
1583
|
+
assertAllocatedId(slideId, "resolved presentation slide");
|
|
1584
|
+
const ids = new Set<string>();
|
|
1585
|
+
const nodes: PresentationArtifactResolvedNode[] = [];
|
|
1586
|
+
for (let index = 0; index < itemCount; index += 1) {
|
|
1587
|
+
const id = readId(reader);
|
|
1588
|
+
const source = readOwner(reader);
|
|
1589
|
+
const inherited = reader.bool("presentation inherited flag");
|
|
1590
|
+
assertAllocatedId(id, "resolved presentation node");
|
|
1591
|
+
assertAllocatedId(source.id, "resolved presentation owner");
|
|
1592
|
+
if (
|
|
1593
|
+
ids.has(id) ||
|
|
1594
|
+
(source.kind === "slide" && source.id !== slideId) ||
|
|
1595
|
+
inherited !== !(source.kind === "slide" && source.id === slideId)
|
|
1596
|
+
)
|
|
1597
|
+
throw new TypeError("invalid resolved presentation node");
|
|
1598
|
+
ids.add(id);
|
|
1599
|
+
nodes.push(Object.freeze({ id, source, inherited }));
|
|
1600
|
+
}
|
|
1601
|
+
response = Object.freeze({
|
|
1602
|
+
kind: "resolved-slide",
|
|
1603
|
+
revision,
|
|
1604
|
+
slideId,
|
|
1605
|
+
nodes: Object.freeze(nodes),
|
|
1606
|
+
truncated,
|
|
1607
|
+
});
|
|
1608
|
+
} else if (tag === 3) {
|
|
1609
|
+
if (itemCount !== 1 || truncated) throw new TypeError("invalid presentation metadata response");
|
|
1610
|
+
const presentationId = readId(reader);
|
|
1611
|
+
assertAllocatedId(presentationId, "presentation metadata id");
|
|
1612
|
+
const width = readCoordinate(reader, "presentation slide width");
|
|
1613
|
+
const height = readCoordinate(reader, "presentation slide height");
|
|
1614
|
+
if (width <= 0 || height <= 0) throw new TypeError("invalid presentation slide size");
|
|
1615
|
+
response = Object.freeze({
|
|
1616
|
+
kind: "metadata",
|
|
1617
|
+
revision,
|
|
1618
|
+
presentationId,
|
|
1619
|
+
slideSize: Object.freeze({ width, height }),
|
|
1620
|
+
masters: reader.u32(),
|
|
1621
|
+
layouts: reader.u32(),
|
|
1622
|
+
slides: reader.u32(),
|
|
1623
|
+
});
|
|
1624
|
+
} else if (tag === 4) {
|
|
1625
|
+
if (itemCount > PRESENTATION_ARTIFACT_QUERY_MAX_SLIDES)
|
|
1626
|
+
throw new RangeError("presentation slide catalog has too many slides");
|
|
1627
|
+
const startSlide = reader.u32();
|
|
1628
|
+
const nextSlide = reader.bool("optional next presentation slide") ? reader.u32() : null;
|
|
1629
|
+
const projectedTextBytes = reader.u32();
|
|
1630
|
+
validateProjectedTextBytes(projectedTextBytes);
|
|
1631
|
+
const slides: PresentationArtifactSlideCatalogItem[] = [];
|
|
1632
|
+
const ids = new Set<string>();
|
|
1633
|
+
for (let offset = 0; offset < itemCount; offset += 1) {
|
|
1634
|
+
const slide = readSlideCatalogItem(reader);
|
|
1635
|
+
if (slide.index !== startSlide + offset || ids.has(slide.id))
|
|
1636
|
+
throw new TypeError("invalid presentation slide catalog order");
|
|
1637
|
+
ids.add(slide.id);
|
|
1638
|
+
slides.push(slide);
|
|
1639
|
+
}
|
|
1640
|
+
const expectedNext = startSlide + itemCount;
|
|
1641
|
+
if (
|
|
1642
|
+
expectedNext > 0xffff_ffff ||
|
|
1643
|
+
truncated !== (nextSlide !== null) ||
|
|
1644
|
+
(nextSlide !== null && nextSlide !== expectedNext)
|
|
1645
|
+
)
|
|
1646
|
+
throw new TypeError("invalid presentation slide catalog boundary");
|
|
1647
|
+
response = Object.freeze({
|
|
1648
|
+
kind: "slide-catalog",
|
|
1649
|
+
revision,
|
|
1650
|
+
startSlide,
|
|
1651
|
+
nextSlide,
|
|
1652
|
+
projectedTextBytes,
|
|
1653
|
+
slides: Object.freeze(slides),
|
|
1654
|
+
truncated,
|
|
1655
|
+
});
|
|
1656
|
+
validateSlideCatalogResponse(response);
|
|
1657
|
+
} else {
|
|
1658
|
+
if (itemCount > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
1659
|
+
throw new RangeError("presentation editor scene has too many nodes");
|
|
1660
|
+
const slide = readSlideCatalogItem(reader);
|
|
1661
|
+
const notes = reader.bool("optional presentation notes") ? readRichText(reader) : null;
|
|
1662
|
+
if (notes === null && !truncated)
|
|
1663
|
+
throw new TypeError("omitted presentation notes require truncation");
|
|
1664
|
+
const projectedTextBytes = reader.u32();
|
|
1665
|
+
validateProjectedTextBytes(projectedTextBytes);
|
|
1666
|
+
const nodes: PresentationArtifactEditorSceneNode[] = [];
|
|
1667
|
+
for (let index = 0; index < itemCount; index += 1) nodes.push(readEditorSceneNode(reader));
|
|
1668
|
+
response = Object.freeze({
|
|
1669
|
+
kind: "editor-slide",
|
|
1670
|
+
revision,
|
|
1671
|
+
slide,
|
|
1672
|
+
notes,
|
|
1673
|
+
projectedTextBytes,
|
|
1674
|
+
nodes: Object.freeze(nodes),
|
|
1675
|
+
truncated,
|
|
1676
|
+
});
|
|
1677
|
+
validateEditorSlideResponse(response);
|
|
1678
|
+
}
|
|
1679
|
+
reader.done("presentation response contains trailing bytes");
|
|
1680
|
+
return response;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
export function assertCanonicalPresentationArtifactQueryResponseBytes(bytes: Uint8Array): void {
|
|
1684
|
+
const response = decodePresentationArtifactQueryResponse(bytes);
|
|
1685
|
+
if (!equalBytes(bytes, encodePresentationArtifactQueryResponse(response, bytes.length)))
|
|
1686
|
+
throw new TypeError("presentation response is not canonically encoded");
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
function validateQueryLimits(maxNodes: number, maxBytes: number, mandatory: number): void {
|
|
1690
|
+
if (
|
|
1691
|
+
!Number.isInteger(maxNodes) ||
|
|
1692
|
+
maxNodes < 1 ||
|
|
1693
|
+
maxNodes > PRESENTATION_ARTIFACT_QUERY_MAX_NODES
|
|
1694
|
+
)
|
|
1695
|
+
throw new RangeError("presentation query node limit is invalid");
|
|
1696
|
+
validateResponseBytes(maxBytes, mandatory);
|
|
1697
|
+
}
|
|
1698
|
+
function validateResponseBytes(maxBytes: number, mandatory: number): void {
|
|
1699
|
+
const minimum = RESPONSE_HEADER_BYTES + CHECKSUM_BYTES + mandatory;
|
|
1700
|
+
if (
|
|
1701
|
+
!Number.isInteger(maxBytes) ||
|
|
1702
|
+
maxBytes < minimum ||
|
|
1703
|
+
maxBytes > PRESENTATION_ARTIFACT_QUERY_RESPONSE_MAX_BYTES
|
|
1704
|
+
)
|
|
1705
|
+
throw new RangeError("presentation response byte limit is invalid");
|
|
1706
|
+
}
|
|
1707
|
+
function validateCatalogQueryLimits(
|
|
1708
|
+
maxSlides: number,
|
|
1709
|
+
maxTextBytes: number,
|
|
1710
|
+
maxBytes: number,
|
|
1711
|
+
): void {
|
|
1712
|
+
if (
|
|
1713
|
+
!Number.isInteger(maxSlides) ||
|
|
1714
|
+
maxSlides < 1 ||
|
|
1715
|
+
maxSlides > PRESENTATION_ARTIFACT_QUERY_MAX_SLIDES
|
|
1716
|
+
)
|
|
1717
|
+
throw new RangeError("presentation slide catalog limit is invalid");
|
|
1718
|
+
validateQueryTextBytes(maxTextBytes);
|
|
1719
|
+
validateResponseBytes(maxBytes, 13);
|
|
1720
|
+
}
|
|
1721
|
+
function validateQueryTextBytes(maxTextBytes: number): void {
|
|
1722
|
+
if (
|
|
1723
|
+
!Number.isInteger(maxTextBytes) ||
|
|
1724
|
+
maxTextBytes < 1 ||
|
|
1725
|
+
maxTextBytes > PRESENTATION_ARTIFACT_QUERY_MAX_TEXT_BYTES
|
|
1726
|
+
)
|
|
1727
|
+
throw new RangeError("presentation query text limit is invalid");
|
|
1728
|
+
}
|
|
1729
|
+
function validateProjectedTextBytes(projectedTextBytes: number): void {
|
|
1730
|
+
if (
|
|
1731
|
+
!Number.isInteger(projectedTextBytes) ||
|
|
1732
|
+
projectedTextBytes < 0 ||
|
|
1733
|
+
projectedTextBytes > PRESENTATION_ARTIFACT_QUERY_MAX_TEXT_BYTES
|
|
1734
|
+
)
|
|
1735
|
+
throw new RangeError("presentation projected text limit is invalid");
|
|
1736
|
+
}
|
|
1737
|
+
function writeSlideCatalogItem(
|
|
1738
|
+
writer: ArtifactBinaryWriter,
|
|
1739
|
+
item: PresentationArtifactSlideCatalogItem,
|
|
1740
|
+
): void {
|
|
1741
|
+
validateSlideCatalogItem(item);
|
|
1742
|
+
writer.u32(item.index);
|
|
1743
|
+
writeId(writer, item.id);
|
|
1744
|
+
writeString(writer, item.title, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
1745
|
+
writeFill(writer, item.background);
|
|
1746
|
+
writer.bool(item.layout !== null);
|
|
1747
|
+
if (item.layout !== null) {
|
|
1748
|
+
writeId(writer, item.layout.id);
|
|
1749
|
+
writeString(writer, item.layout.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
1750
|
+
writeOptionalId(writer, item.layout.masterId);
|
|
1751
|
+
writeFill(writer, item.layout.background);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
function readSlideCatalogItem(reader: ArtifactBinaryReader): PresentationArtifactSlideCatalogItem {
|
|
1755
|
+
const index = reader.u32();
|
|
1756
|
+
const id = readId(reader);
|
|
1757
|
+
const title = readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
1758
|
+
const background = readFill(reader);
|
|
1759
|
+
const layout = reader.bool("optional presentation layout")
|
|
1760
|
+
? Object.freeze({
|
|
1761
|
+
id: readId(reader),
|
|
1762
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
1763
|
+
masterId: readOptionalId(reader),
|
|
1764
|
+
background: readFill(reader),
|
|
1765
|
+
})
|
|
1766
|
+
: null;
|
|
1767
|
+
const item = Object.freeze({ index, id, title, background, layout });
|
|
1768
|
+
validateSlideCatalogItem(item);
|
|
1769
|
+
return item;
|
|
1770
|
+
}
|
|
1771
|
+
function validateSlideCatalogItem(item: PresentationArtifactSlideCatalogItem): void {
|
|
1772
|
+
assertAllocatedId(item.id, "presentation slide catalog id");
|
|
1773
|
+
if (item.layout !== null) {
|
|
1774
|
+
assertAllocatedId(item.layout.id, "presentation slide layout id");
|
|
1775
|
+
if (item.layout.masterId !== null)
|
|
1776
|
+
assertAllocatedId(item.layout.masterId, "presentation slide master id");
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
function writeEditorSceneNode(
|
|
1780
|
+
writer: ArtifactBinaryWriter,
|
|
1781
|
+
node: PresentationArtifactEditorSceneNode,
|
|
1782
|
+
): void {
|
|
1783
|
+
writeId(writer, node.id);
|
|
1784
|
+
writeOwner(writer, node.source);
|
|
1785
|
+
writer.bool(node.inherited);
|
|
1786
|
+
writeOptionalId(writer, node.parentId);
|
|
1787
|
+
writer.u32(node.order);
|
|
1788
|
+
writeString(writer, node.name, PRESENTATION_ARTIFACT_MAX_NAME_BYTES);
|
|
1789
|
+
writeRect(writer, node.bounds);
|
|
1790
|
+
writeTransform(writer, node.transform);
|
|
1791
|
+
writeNodeKind(writer, node.content);
|
|
1792
|
+
}
|
|
1793
|
+
function readEditorSceneNode(reader: ArtifactBinaryReader): PresentationArtifactEditorSceneNode {
|
|
1794
|
+
return Object.freeze({
|
|
1795
|
+
id: readId(reader),
|
|
1796
|
+
source: readOwner(reader),
|
|
1797
|
+
inherited: reader.bool("presentation inherited flag"),
|
|
1798
|
+
parentId: readOptionalId(reader),
|
|
1799
|
+
order: reader.u32(),
|
|
1800
|
+
name: readString(reader, PRESENTATION_ARTIFACT_MAX_NAME_BYTES),
|
|
1801
|
+
bounds: readRect(reader),
|
|
1802
|
+
transform: readTransform(reader),
|
|
1803
|
+
content: readNodeKind(reader),
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
function validateSlideCatalogResponse(
|
|
1807
|
+
response: Extract<PresentationArtifactQueryResponse, { kind: "slide-catalog" }>,
|
|
1808
|
+
): void {
|
|
1809
|
+
if (response.slides.length > PRESENTATION_ARTIFACT_QUERY_MAX_SLIDES)
|
|
1810
|
+
throw new RangeError("presentation slide catalog has too many slides");
|
|
1811
|
+
validateProjectedTextBytes(response.projectedTextBytes);
|
|
1812
|
+
const ids = new Set<string>();
|
|
1813
|
+
let measured = 0;
|
|
1814
|
+
for (let offset = 0; offset < response.slides.length; offset += 1) {
|
|
1815
|
+
const slide = response.slides[offset]!;
|
|
1816
|
+
validateSlideCatalogItem(slide);
|
|
1817
|
+
if (slide.index !== response.startSlide + offset || ids.has(slide.id))
|
|
1818
|
+
throw new TypeError("invalid presentation slide catalog order");
|
|
1819
|
+
ids.add(slide.id);
|
|
1820
|
+
measured = addProjectedText(measured, slideCatalogItemTextBytes(slide));
|
|
1821
|
+
}
|
|
1822
|
+
const expectedNext = response.startSlide + response.slides.length;
|
|
1823
|
+
if (
|
|
1824
|
+
!Number.isInteger(response.startSlide) ||
|
|
1825
|
+
response.startSlide < 0 ||
|
|
1826
|
+
expectedNext > 0xffff_ffff ||
|
|
1827
|
+
response.truncated !== (response.nextSlide !== null) ||
|
|
1828
|
+
(response.nextSlide !== null && response.nextSlide !== expectedNext) ||
|
|
1829
|
+
(response.nextSlide !== null && response.slides.length === 0) ||
|
|
1830
|
+
measured !== response.projectedTextBytes
|
|
1831
|
+
)
|
|
1832
|
+
throw new TypeError("invalid presentation slide catalog boundary");
|
|
1833
|
+
}
|
|
1834
|
+
function validateEditorSlideResponse(
|
|
1835
|
+
response: Extract<PresentationArtifactQueryResponse, { kind: "editor-slide" }>,
|
|
1836
|
+
): void {
|
|
1837
|
+
if (response.nodes.length > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
1838
|
+
throw new RangeError("presentation editor scene has too many nodes");
|
|
1839
|
+
if (response.notes === null && !response.truncated)
|
|
1840
|
+
throw new TypeError("omitted presentation notes require truncation");
|
|
1841
|
+
validateSlideCatalogItem(response.slide);
|
|
1842
|
+
validateProjectedTextBytes(response.projectedTextBytes);
|
|
1843
|
+
let measured = slideCatalogItemTextBytes(response.slide);
|
|
1844
|
+
if (response.notes !== null)
|
|
1845
|
+
measured = addProjectedText(measured, richTextProjectionBytes(response.notes));
|
|
1846
|
+
const ids = new Set<string>();
|
|
1847
|
+
const sources = new Map<string, string>();
|
|
1848
|
+
const positions = new Set<string>();
|
|
1849
|
+
for (const node of response.nodes) {
|
|
1850
|
+
assertAllocatedId(node.id, "presentation editor node id");
|
|
1851
|
+
assertAllocatedId(node.source.id, "presentation editor node owner");
|
|
1852
|
+
const sourceKey = `${node.source.kind}:${node.source.id}`;
|
|
1853
|
+
const expectedInherited = !(
|
|
1854
|
+
node.source.kind === "slide" && node.source.id === response.slide.id
|
|
1855
|
+
);
|
|
1856
|
+
const position = `${sourceKey}:${node.parentId ?? "root"}:${node.order}`;
|
|
1857
|
+
if (
|
|
1858
|
+
ids.has(node.id) ||
|
|
1859
|
+
(node.source.kind === "slide" && node.source.id !== response.slide.id) ||
|
|
1860
|
+
node.inherited !== expectedInherited ||
|
|
1861
|
+
node.parentId === node.id ||
|
|
1862
|
+
(node.parentId !== null &&
|
|
1863
|
+
(!isAllocatedId(node.parentId) || sources.get(node.parentId) !== sourceKey)) ||
|
|
1864
|
+
positions.has(position)
|
|
1865
|
+
)
|
|
1866
|
+
throw new TypeError("invalid presentation editor scene node");
|
|
1867
|
+
validateEditorNodeReferences(node);
|
|
1868
|
+
ids.add(node.id);
|
|
1869
|
+
sources.set(node.id, sourceKey);
|
|
1870
|
+
positions.add(position);
|
|
1871
|
+
measured = addProjectedText(measured, editorSceneNodeTextBytes(node));
|
|
1872
|
+
}
|
|
1873
|
+
if (measured !== response.projectedTextBytes)
|
|
1874
|
+
throw new TypeError("presentation editor projected text mismatch");
|
|
1875
|
+
}
|
|
1876
|
+
function validateEditorNodeReferences(node: PresentationArtifactEditorSceneNode): void {
|
|
1877
|
+
if (node.content.kind === "group") {
|
|
1878
|
+
const children = new Set<string>();
|
|
1879
|
+
for (const id of node.content.children) {
|
|
1880
|
+
if (!isAllocatedId(id) || id === node.id || children.has(id))
|
|
1881
|
+
throw new TypeError("invalid presentation editor group children");
|
|
1882
|
+
children.add(id);
|
|
1883
|
+
}
|
|
1884
|
+
} else if (node.content.kind === "connector") {
|
|
1885
|
+
for (const endpoint of [node.content.start, node.content.end])
|
|
1886
|
+
if (
|
|
1887
|
+
endpoint.nodeId !== null &&
|
|
1888
|
+
(!isAllocatedId(endpoint.nodeId) || endpoint.nodeId === node.id)
|
|
1889
|
+
)
|
|
1890
|
+
throw new TypeError("invalid presentation editor connector endpoint");
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function slideCatalogItemTextBytes(item: PresentationArtifactSlideCatalogItem): number {
|
|
1894
|
+
let total = textProjectionBytes(item.title);
|
|
1895
|
+
if (item.layout !== null) total = addProjectedText(total, textProjectionBytes(item.layout.name));
|
|
1896
|
+
return total;
|
|
1897
|
+
}
|
|
1898
|
+
function editorSceneNodeTextBytes(node: PresentationArtifactEditorSceneNode): number {
|
|
1899
|
+
return addProjectedText(textProjectionBytes(node.name), nodeKindProjectionBytes(node.content));
|
|
1900
|
+
}
|
|
1901
|
+
function nodeKindProjectionBytes(content: PresentationArtifactNodeKind): number {
|
|
1902
|
+
if (content.kind === "shape") {
|
|
1903
|
+
let total = content.text === null ? 0 : richTextProjectionBytes(content.text);
|
|
1904
|
+
if (content.placeholder !== null)
|
|
1905
|
+
total = addProjectedText(total, textProjectionBytes(content.placeholder.kind));
|
|
1906
|
+
return total;
|
|
1907
|
+
}
|
|
1908
|
+
if (content.kind === "group" || content.kind === "connector") return 0;
|
|
1909
|
+
if (content.kind === "chart") {
|
|
1910
|
+
let total = richTextProjectionBytes(content.title);
|
|
1911
|
+
for (const series of content.series) {
|
|
1912
|
+
total = addProjectedText(total, textProjectionBytes(series.name));
|
|
1913
|
+
for (const category of series.categories)
|
|
1914
|
+
total = addProjectedText(total, textProjectionBytes(category));
|
|
1915
|
+
}
|
|
1916
|
+
return total;
|
|
1917
|
+
}
|
|
1918
|
+
if (content.kind === "table") {
|
|
1919
|
+
let total = 0;
|
|
1920
|
+
for (const row of content.rows)
|
|
1921
|
+
for (const cell of row)
|
|
1922
|
+
if (cell !== null) total = addProjectedText(total, richTextProjectionBytes(cell.text));
|
|
1923
|
+
return total;
|
|
1924
|
+
}
|
|
1925
|
+
return addProjectedText(
|
|
1926
|
+
textProjectionBytes(content.contentType),
|
|
1927
|
+
textProjectionBytes(content.altText),
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
function richTextProjectionBytes(text: PresentationArtifactRichText): number {
|
|
1931
|
+
let total = 0;
|
|
1932
|
+
for (const paragraph of text.paragraphs)
|
|
1933
|
+
for (const run of paragraph.runs) {
|
|
1934
|
+
total = addProjectedText(total, textProjectionBytes(run.text));
|
|
1935
|
+
total = addProjectedText(total, textProjectionBytes(run.style.fontFamily));
|
|
1936
|
+
if (run.style.language !== null)
|
|
1937
|
+
total = addProjectedText(total, textProjectionBytes(run.style.language));
|
|
1938
|
+
}
|
|
1939
|
+
return total;
|
|
1940
|
+
}
|
|
1941
|
+
function textProjectionBytes(value: string): number {
|
|
1942
|
+
return strictUtf8(value, "presentation projected text").byteLength;
|
|
1943
|
+
}
|
|
1944
|
+
function addProjectedText(left: number, right: number): number {
|
|
1945
|
+
const total = left + right;
|
|
1946
|
+
if (!Number.isSafeInteger(total)) throw new RangeError("presentation projected text is invalid");
|
|
1947
|
+
return total;
|
|
1948
|
+
}
|
|
1949
|
+
function nodeKindTag(kind: PresentationArtifactNodeKind["kind"]): number {
|
|
1950
|
+
return kind === "shape"
|
|
1951
|
+
? 0
|
|
1952
|
+
: kind === "group"
|
|
1953
|
+
? 1
|
|
1954
|
+
: kind === "connector"
|
|
1955
|
+
? 2
|
|
1956
|
+
: kind === "chart"
|
|
1957
|
+
? 3
|
|
1958
|
+
: kind === "table"
|
|
1959
|
+
? 4
|
|
1960
|
+
: kind === "media"
|
|
1961
|
+
? 5
|
|
1962
|
+
: bad("presentation node kind");
|
|
1963
|
+
}
|
|
1964
|
+
function readNodeKindTag(tag: number): PresentationArtifactNodeKind["kind"] {
|
|
1965
|
+
return tag === 0
|
|
1966
|
+
? "shape"
|
|
1967
|
+
: tag === 1
|
|
1968
|
+
? "group"
|
|
1969
|
+
: tag === 2
|
|
1970
|
+
? "connector"
|
|
1971
|
+
: tag === 3
|
|
1972
|
+
? "chart"
|
|
1973
|
+
: tag === 4
|
|
1974
|
+
? "table"
|
|
1975
|
+
: tag === 5
|
|
1976
|
+
? "media"
|
|
1977
|
+
: bad("presentation node kind");
|
|
1978
|
+
}
|
|
1979
|
+
function isAllocatedId(id: string): boolean {
|
|
1980
|
+
return (
|
|
1981
|
+
/^[0-9a-f]{32}$/u.test(id) &&
|
|
1982
|
+
id.slice(0, 16) !== "0000000000000000" &&
|
|
1983
|
+
id.slice(16) !== "0000000000000000"
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
function assertAllocatedId(id: string, label: string): void {
|
|
1987
|
+
if (!isAllocatedId(id)) throw new TypeError(`${label} is invalid`);
|
|
1988
|
+
}
|
|
1989
|
+
function validateProjectedResponse(
|
|
1990
|
+
response: Extract<PresentationArtifactQueryResponse, { kind: "viewport" | "hit-test" }>,
|
|
1991
|
+
): void {
|
|
1992
|
+
if (response.nodes.length > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
1993
|
+
throw new RangeError("presentation response has too many nodes");
|
|
1994
|
+
assertAllocatedId(response.owner.id, "presentation response owner");
|
|
1995
|
+
const ids = new Set<string>();
|
|
1996
|
+
let previous: number | null = null;
|
|
1997
|
+
for (const node of response.nodes) {
|
|
1998
|
+
assertAllocatedId(node.id, "presentation response node");
|
|
1999
|
+
if (
|
|
2000
|
+
node.owner.kind !== response.owner.kind ||
|
|
2001
|
+
node.owner.id !== response.owner.id ||
|
|
2002
|
+
ids.has(node.id) ||
|
|
2003
|
+
node.parentId === node.id ||
|
|
2004
|
+
(node.parentId !== null && !isAllocatedId(node.parentId)) ||
|
|
2005
|
+
(previous !== null &&
|
|
2006
|
+
(response.kind === "viewport" ? node.paintOrder <= previous : node.paintOrder >= previous))
|
|
2007
|
+
) {
|
|
2008
|
+
throw new TypeError("invalid presentation projection node");
|
|
2009
|
+
}
|
|
2010
|
+
ids.add(node.id);
|
|
2011
|
+
previous = node.paintOrder;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
function validateResolvedResponse(
|
|
2015
|
+
response: Extract<PresentationArtifactQueryResponse, { kind: "resolved-slide" }>,
|
|
2016
|
+
): void {
|
|
2017
|
+
if (response.nodes.length > PRESENTATION_ARTIFACT_QUERY_MAX_NODES)
|
|
2018
|
+
throw new RangeError("presentation response has too many nodes");
|
|
2019
|
+
assertAllocatedId(response.slideId, "resolved presentation slide");
|
|
2020
|
+
const ids = new Set<string>();
|
|
2021
|
+
for (const node of response.nodes) {
|
|
2022
|
+
assertAllocatedId(node.id, "resolved presentation node");
|
|
2023
|
+
assertAllocatedId(node.source.id, "resolved presentation owner");
|
|
2024
|
+
if (
|
|
2025
|
+
ids.has(node.id) ||
|
|
2026
|
+
(node.source.kind === "slide" && node.source.id !== response.slideId) ||
|
|
2027
|
+
node.inherited !== !(node.source.kind === "slide" && node.source.id === response.slideId)
|
|
2028
|
+
)
|
|
2029
|
+
throw new TypeError("invalid resolved presentation node");
|
|
2030
|
+
ids.add(node.id);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function bad(label: string): never {
|
|
2034
|
+
throw new TypeError(`invalid ${label}`);
|
|
2035
|
+
}
|