@kerebron/extension-odt 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/esm/ExtensionOdt.d.ts.map +1 -1
- package/esm/ExtensionOdt.js +28 -77
- package/esm/ExtensionOdt.js.map +1 -1
- package/esm/OdtParser.d.ts +41 -7
- package/esm/OdtParser.d.ts.map +1 -1
- package/esm/OdtParser.js +26 -19
- package/esm/OdtParser.js.map +1 -1
- package/esm/lists.d.ts +9 -9
- package/esm/lists.d.ts.map +1 -1
- package/esm/lists.js +21 -7
- package/esm/lists.js.map +1 -1
- package/esm/node_handlers/basic_node_handlers.d.ts +1 -0
- package/esm/node_handlers/basic_node_handlers.d.ts.map +1 -1
- package/esm/node_handlers/basic_node_handlers.js +77 -9
- package/esm/node_handlers/basic_node_handlers.js.map +1 -1
- package/esm/node_handlers/list_node_handlers.d.ts.map +1 -1
- package/esm/node_handlers/list_node_handlers.js +61 -65
- package/esm/node_handlers/list_node_handlers.js.map +1 -1
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.d.ts.map +1 -1
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js +40 -66
- package/esm/postprocess/convertCodeParagraphsToCodeBlocks.js.map +1 -1
- package/esm/postprocess/convertMathMl.d.ts.map +1 -1
- package/esm/postprocess/convertMathMl.js +1 -1
- package/esm/postprocess/convertMathMl.js.map +1 -1
- package/esm/postprocess/fixContinuedLists.d.ts.map +1 -1
- package/esm/postprocess/fixContinuedLists.js +80 -67
- package/esm/postprocess/fixContinuedLists.js.map +1 -1
- package/esm/postprocess/fixListsLevels.d.ts +3 -0
- package/esm/postprocess/fixListsLevels.d.ts.map +1 -0
- package/esm/postprocess/fixListsLevels.js +62 -0
- package/esm/postprocess/fixListsLevels.js.map +1 -0
- package/esm/postprocess/mergeCodeBlocks.d.ts +3 -0
- package/esm/postprocess/mergeCodeBlocks.d.ts.map +1 -0
- package/esm/postprocess/mergeCodeBlocks.js +83 -0
- package/esm/postprocess/mergeCodeBlocks.js.map +1 -0
- package/esm/postprocess/postProcess.d.ts.map +1 -1
- package/esm/postprocess/postProcess.js +5 -2
- package/esm/postprocess/postProcess.js.map +1 -1
- package/esm/postprocess/removeUnusedBookmarks.d.ts +1 -1
- package/esm/postprocess/removeUnusedBookmarks.d.ts.map +1 -1
- package/esm/postprocess/removeUnusedBookmarks.js +16 -19
- package/esm/postprocess/removeUnusedBookmarks.js.map +1 -1
- package/esm/postprocess/urlRewrite.d.ts +4 -0
- package/esm/postprocess/urlRewrite.d.ts.map +1 -0
- package/esm/postprocess/urlRewrite.js +60 -0
- package/esm/postprocess/urlRewrite.js.map +1 -0
- package/package.json +3 -3
- package/src/ExtensionOdt.ts +30 -114
- package/src/OdtParser.ts +86 -31
- package/src/lists.ts +24 -10
- package/src/node_handlers/basic_node_handlers.ts +82 -10
- package/src/node_handlers/list_node_handlers.ts +91 -90
- package/src/postprocess/convertCodeParagraphsToCodeBlocks.ts +44 -86
- package/src/postprocess/convertMathMl.ts +1 -2
- package/src/postprocess/fixContinuedLists.ts +95 -78
- package/src/postprocess/fixListsLevels.ts +93 -0
- package/src/postprocess/mergeCodeBlocks.ts +114 -0
- package/src/postprocess/postProcess.ts +5 -1
- package/src/postprocess/removeUnusedBookmarks.ts +33 -21
- package/src/postprocess/urlRewrite.ts +95 -0
package/src/ExtensionOdt.ts
CHANGED
|
@@ -6,24 +6,19 @@ import {
|
|
|
6
6
|
Extension,
|
|
7
7
|
type UrlRewriter,
|
|
8
8
|
} from '@kerebron/editor';
|
|
9
|
-
import {
|
|
10
|
-
init_debug,
|
|
11
|
-
parse_content,
|
|
12
|
-
parse_styles,
|
|
13
|
-
unzip,
|
|
14
|
-
} from '@kerebron/odt-wasm';
|
|
15
9
|
|
|
16
10
|
import { OdtParser, OdtParserConfig } from './OdtParser.js';
|
|
17
11
|
import { getDefaultsPostProcessFilters } from './postprocess/postProcess.js';
|
|
18
12
|
import { Command } from '@kerebron/editor/commands';
|
|
19
|
-
import {
|
|
13
|
+
import { EditorState, Transaction } from 'prosemirror-state';
|
|
14
|
+
import { urlRewrite } from './postprocess/urlRewrite.js';
|
|
20
15
|
|
|
21
16
|
export interface OdtConfig extends OdtParserConfig {
|
|
22
17
|
debug?: boolean;
|
|
23
18
|
postProcessCommands?: Command[];
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
let odtWasm: Record<string, any> | undefined = undefined;
|
|
27
22
|
|
|
28
23
|
export class ExtensionOdt extends Extension {
|
|
29
24
|
name = 'odt';
|
|
@@ -37,14 +32,19 @@ export class ExtensionOdt extends Extension {
|
|
|
37
32
|
editor: CoreEditor,
|
|
38
33
|
schema: Schema,
|
|
39
34
|
): Record<string, Converter> {
|
|
40
|
-
const config = this.config;
|
|
41
|
-
|
|
42
35
|
const odtConverter = {
|
|
43
36
|
fromDoc: async (document: Node): Promise<Uint8Array> => {
|
|
44
37
|
throw new Error('Not implemented');
|
|
45
38
|
},
|
|
46
39
|
toDoc: async (buffer: Uint8Array): Promise<Node> => {
|
|
47
|
-
|
|
40
|
+
if (!odtWasm) {
|
|
41
|
+
odtWasm = this.config.debug
|
|
42
|
+
? await import('@kerebron/odt-wasm/debug')
|
|
43
|
+
: await import('@kerebron/odt-wasm');
|
|
44
|
+
odtWasm = await odtWasm.init();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { doc, filesMap } = odtConverter.odtToJson(buffer, odtWasm);
|
|
48
48
|
|
|
49
49
|
const filterCommands = getDefaultsPostProcessFilters({
|
|
50
50
|
doc,
|
|
@@ -53,128 +53,44 @@ export class ExtensionOdt extends Extension {
|
|
|
53
53
|
this.config.postProcessCommands || [],
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// plugin.props.
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const subEditor = editor.clone();
|
|
64
|
-
subEditor.setDocument(doc.toJSON());
|
|
56
|
+
let state = EditorState.create({ doc });
|
|
57
|
+
const dispatch = (tr: Transaction) => {
|
|
58
|
+
state = state.apply(tr);
|
|
59
|
+
};
|
|
65
60
|
|
|
66
|
-
let modified = false;
|
|
67
61
|
if (this.urlFromRewriter) {
|
|
68
|
-
|
|
69
|
-
subEditor.getDocument().descendants((node, pos) => {
|
|
70
|
-
if (node.type.name === 'image') {
|
|
71
|
-
imageNodes.push({ node, pos });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const linkNodes: Array<{ node: Node; pos: number }> = [];
|
|
76
|
-
subEditor.getDocument().descendants((node, pos) => {
|
|
77
|
-
if (node.marks.find((mark) => mark.type.name === 'link')) {
|
|
78
|
-
linkNodes.push({ node, pos });
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const tr = subEditor.state.tr;
|
|
83
|
-
|
|
84
|
-
for (const { node, pos } of linkNodes) {
|
|
85
|
-
const linkMark = node.marks.find((mark) =>
|
|
86
|
-
mark.type.name === 'link'
|
|
87
|
-
);
|
|
88
|
-
if (!linkMark) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
let href = linkMark.attrs.href || '';
|
|
92
|
-
href = await this.urlFromRewriter(href, {
|
|
93
|
-
type: 'A',
|
|
94
|
-
dest: 'kerebron',
|
|
95
|
-
});
|
|
96
|
-
if (href !== linkMark.attrs.href) {
|
|
97
|
-
const newMarks = node.marks.map((mark) => {
|
|
98
|
-
if (mark.type.name === 'link') {
|
|
99
|
-
const markType = this.editor.schema.marks['link'];
|
|
100
|
-
return markType.create({ ...mark.attrs, href });
|
|
101
|
-
}
|
|
102
|
-
return mark;
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const nodeType = this.editor.schema.nodes[node.type.name];
|
|
106
|
-
let replaceNode;
|
|
107
|
-
if (nodeType.isText) {
|
|
108
|
-
replaceNode = this.editor.schema.text(
|
|
109
|
-
node.text || '',
|
|
110
|
-
newMarks,
|
|
111
|
-
);
|
|
112
|
-
} else {
|
|
113
|
-
replaceNode = nodeType.create(
|
|
114
|
-
node.attrs,
|
|
115
|
-
node.content,
|
|
116
|
-
newMarks,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
tr.replaceWith(
|
|
120
|
-
tr.mapping.map(pos),
|
|
121
|
-
tr.mapping.map(pos + node.nodeSize),
|
|
122
|
-
replaceNode,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
for (const { node, pos } of imageNodes) {
|
|
128
|
-
let src = node.attrs.src || '';
|
|
129
|
-
|
|
130
|
-
src = await this.urlFromRewriter(src, {
|
|
131
|
-
type: 'IMG',
|
|
132
|
-
dest: 'kerebron',
|
|
133
|
-
filesMap,
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (src !== node.attrs.src) {
|
|
137
|
-
const nodeType = this.editor.schema.nodes[node.type.name];
|
|
138
|
-
const replaceNode = nodeType.create(
|
|
139
|
-
{ ...node.attrs, src },
|
|
140
|
-
node.content,
|
|
141
|
-
node.marks,
|
|
142
|
-
);
|
|
143
|
-
tr.replaceWith(
|
|
144
|
-
tr.mapping.map(pos),
|
|
145
|
-
tr.mapping.map(pos + node.nodeSize),
|
|
146
|
-
replaceNode,
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
subEditor.dispatchTransaction(tr), modified = true;
|
|
62
|
+
await urlRewrite(this.urlFromRewriter, filesMap, state, dispatch);
|
|
151
63
|
}
|
|
152
64
|
|
|
153
65
|
if (filterCommands.length > 0) {
|
|
154
66
|
for (const filter of filterCommands) {
|
|
155
67
|
filter(
|
|
156
|
-
|
|
157
|
-
(tr) =>
|
|
68
|
+
state,
|
|
69
|
+
(tr) => dispatch(tr),
|
|
158
70
|
);
|
|
159
71
|
}
|
|
160
|
-
modified = true;
|
|
161
72
|
}
|
|
162
73
|
|
|
163
|
-
if (
|
|
164
|
-
|
|
74
|
+
if (this.config.debug) {
|
|
75
|
+
const event = new CustomEvent('odt:pmdoc:filtered', {
|
|
76
|
+
detail: {
|
|
77
|
+
doc: state.doc,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
this.editor.dispatchEvent(event);
|
|
165
81
|
}
|
|
166
82
|
|
|
167
|
-
return doc;
|
|
83
|
+
return state.doc;
|
|
168
84
|
},
|
|
169
|
-
odtToJson: (buffer: Uint8Array) => {
|
|
170
|
-
const files = unzip(buffer);
|
|
85
|
+
odtToJson: (buffer: Uint8Array, odtWasm: any) => {
|
|
86
|
+
const files = odtWasm.unzip(buffer);
|
|
171
87
|
const filesMap: Record<string, Uint8Array> = {};
|
|
172
88
|
for (const k of files.keys()) {
|
|
173
89
|
filesMap[k] = Uint8Array.from(files.get(k));
|
|
174
90
|
}
|
|
175
91
|
|
|
176
|
-
const stylesTree = parse_styles(files.get('styles.xml'));
|
|
177
|
-
const contentTree = parse_content(files.get('content.xml'));
|
|
92
|
+
const stylesTree = odtWasm.parse_styles(files.get('styles.xml'));
|
|
93
|
+
const contentTree = odtWasm.parse_content(files.get('content.xml'));
|
|
178
94
|
|
|
179
95
|
if (this.config.debug) {
|
|
180
96
|
const event = new CustomEvent('odt:parsed', {
|
package/src/OdtParser.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
import { getListNodesHandlers } from './node_handlers/list_node_handlers.js';
|
|
7
7
|
import { getTableNodesHandlers } from './node_handlers/table_node_handlers.js';
|
|
8
8
|
import { ListTracker } from './lists.js';
|
|
9
|
-
import { type UrlRewriter } from '@kerebron/editor';
|
|
10
9
|
|
|
11
10
|
const COURIER_FONTS = ['Courier New', 'Courier', 'Roboto Mono'];
|
|
12
11
|
|
|
@@ -16,39 +15,95 @@ export interface OdtElement {
|
|
|
16
15
|
|
|
17
16
|
export type NodeHandler = (ctx: OdtStashContext, value: any) => void;
|
|
18
17
|
|
|
19
|
-
interface
|
|
20
|
-
'@
|
|
18
|
+
export interface ListLLevelLabelAlignment {
|
|
19
|
+
'@margin-left'?: string;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
interface
|
|
24
|
-
'
|
|
22
|
+
export interface ListLevelProperties {
|
|
23
|
+
'list-level-label-alignment': ListLLevelLabelAlignment;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ListLevelStyleBullet {
|
|
27
|
+
'@level': number;
|
|
28
|
+
'list-level-properties': ListLevelProperties;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ListLevelStyleNumber {
|
|
32
|
+
'@level': number;
|
|
33
|
+
'@start-value'?: number;
|
|
34
|
+
'@num-format': string;
|
|
35
|
+
'list-level-properties': ListLevelProperties;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ListStyle {
|
|
39
|
+
'@name'?: string;
|
|
40
|
+
'list-level-style-bullet': ListLevelStyleBullet[];
|
|
41
|
+
'list-level-style-number': ListLevelStyleNumber[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TextProperty {
|
|
45
|
+
'@font-name'?: string;
|
|
46
|
+
'@font-weight'?: string;
|
|
47
|
+
'@font-style'?: string;
|
|
48
|
+
'@font-size'?: string;
|
|
49
|
+
'@text-underline-style'?: string;
|
|
50
|
+
'@color'?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ParagraphProperty {
|
|
54
|
+
'@break-before'?: string;
|
|
55
|
+
'@break-after'?: string;
|
|
56
|
+
'@margin-left'?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Style {
|
|
60
|
+
'@name'?: string;
|
|
25
61
|
'@parent-style-name'?: string;
|
|
26
62
|
styles: string[];
|
|
63
|
+
'text-properties'?: TextProperty;
|
|
64
|
+
'paragraph-properties'?: TextProperty;
|
|
27
65
|
}
|
|
28
66
|
|
|
29
|
-
interface StylesTree {
|
|
67
|
+
export interface StylesTree {
|
|
30
68
|
styles: {
|
|
31
69
|
'list-style': Array<ListStyle>;
|
|
32
70
|
'style': Array<Style>;
|
|
33
71
|
};
|
|
34
72
|
}
|
|
35
73
|
|
|
36
|
-
interface AutomaticStyles {
|
|
74
|
+
export interface AutomaticStyles {
|
|
37
75
|
'style': Array<Style>;
|
|
38
76
|
}
|
|
39
77
|
|
|
40
|
-
export function
|
|
78
|
+
export function resolveListStyle(
|
|
41
79
|
stylesTree: StylesTree,
|
|
42
80
|
automaticStyles: AutomaticStyles,
|
|
43
81
|
name: string,
|
|
44
|
-
):
|
|
45
|
-
let style:
|
|
82
|
+
): ListStyle {
|
|
83
|
+
let style: ListStyle | undefined;
|
|
84
|
+
|
|
85
|
+
style = stylesTree.styles['list-style'].find((item) =>
|
|
86
|
+
item['@name'] === name
|
|
87
|
+
);
|
|
46
88
|
|
|
47
89
|
if (!style) {
|
|
48
|
-
style =
|
|
49
|
-
|
|
50
|
-
|
|
90
|
+
style = {
|
|
91
|
+
'@name': name,
|
|
92
|
+
'list-level-style-number': [],
|
|
93
|
+
'list-level-style-bullet': [],
|
|
94
|
+
};
|
|
51
95
|
}
|
|
96
|
+
|
|
97
|
+
return style;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function resolveStyle(
|
|
101
|
+
stylesTree: StylesTree,
|
|
102
|
+
automaticStyles: AutomaticStyles,
|
|
103
|
+
name: string,
|
|
104
|
+
): Style {
|
|
105
|
+
let style: Style | undefined;
|
|
106
|
+
|
|
52
107
|
if (!style) {
|
|
53
108
|
style = stylesTree.styles['style'].find((item) => item['@name'] === name);
|
|
54
109
|
}
|
|
@@ -59,6 +114,7 @@ export function resolveStyle(
|
|
|
59
114
|
if (!style) {
|
|
60
115
|
style = {
|
|
61
116
|
'@name': name,
|
|
117
|
+
styles: [],
|
|
62
118
|
};
|
|
63
119
|
}
|
|
64
120
|
|
|
@@ -72,7 +128,7 @@ export function resolveStyle(
|
|
|
72
128
|
);
|
|
73
129
|
if (parentStyle) {
|
|
74
130
|
const styles = [...style['styles'], ...parentStyle['styles']];
|
|
75
|
-
for (const key
|
|
131
|
+
for (const key of Object.keys(style) as (keyof Style)[]) {
|
|
76
132
|
if (typeof style[key] === 'undefined') {
|
|
77
133
|
delete style[key];
|
|
78
134
|
}
|
|
@@ -198,6 +254,10 @@ export class OdtStashContext {
|
|
|
198
254
|
this.current.content = [];
|
|
199
255
|
}
|
|
200
256
|
|
|
257
|
+
public dropNode() {
|
|
258
|
+
this.unstash();
|
|
259
|
+
}
|
|
260
|
+
|
|
201
261
|
public closeNode(type: string, attrs = {}, marks = Mark.none) {
|
|
202
262
|
const node = this.createNode(type, attrs, marks);
|
|
203
263
|
this.unstash();
|
|
@@ -264,7 +324,9 @@ export class OdtStashContext {
|
|
|
264
324
|
this.automaticStyles,
|
|
265
325
|
element['@style-name'],
|
|
266
326
|
)
|
|
267
|
-
: {
|
|
327
|
+
: {
|
|
328
|
+
styles: [],
|
|
329
|
+
};
|
|
268
330
|
|
|
269
331
|
return style;
|
|
270
332
|
}
|
|
@@ -291,21 +353,14 @@ export class OdtParser {
|
|
|
291
353
|
...getBasicNodesHandlers(),
|
|
292
354
|
...getListNodesHandlers(),
|
|
293
355
|
...getTableNodesHandlers(),
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
},
|
|
303
|
-
'change-end': {
|
|
304
|
-
// custom(state) {
|
|
305
|
-
// state.textMarks.forEach((x) =>
|
|
306
|
-
// x.markName === 'change' ? state.textMarks.delete(x) : x
|
|
307
|
-
// );
|
|
308
|
-
// },
|
|
356
|
+
'g': () => { // Test is: embedded-diagram-example.odt
|
|
357
|
+
// DrawG draw:g
|
|
358
|
+
const node = ctx.createText(
|
|
359
|
+
'INSTEAD OF EMBEDDED DIAGRAM ABOVE USE EMBEDDED DIAGRAM FROM DRIVE AND PUT LINK TO IT IN THE DESCRIPTION. See: https://github.com/mieweb/wikiGDrive/issues/353',
|
|
360
|
+
);
|
|
361
|
+
if (node) {
|
|
362
|
+
ctx.current.content.push(node);
|
|
363
|
+
}
|
|
309
364
|
},
|
|
310
365
|
'frame': (ctx: OdtStashContext, odtElement: any) => {
|
|
311
366
|
if (odtElement.object && odtElement.object['@href']) {
|
|
@@ -322,7 +377,7 @@ export class OdtParser {
|
|
|
322
377
|
}
|
|
323
378
|
}
|
|
324
379
|
if (odtElement.image && odtElement.image['@href']) { // TODO links rewrite
|
|
325
|
-
const alt = odtElement.
|
|
380
|
+
const alt = odtElement.desc?.['$value'] || '';
|
|
326
381
|
const src = odtElement.image['@href'];
|
|
327
382
|
ctx.openNode();
|
|
328
383
|
ctx.closeNode('image', {
|
package/src/lists.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { OdtElement } from './OdtParser.js';
|
|
2
|
-
|
|
3
1
|
export class ListNumbering {
|
|
4
2
|
levels: { [level: number]: number } = {};
|
|
5
|
-
|
|
3
|
+
forceStart: { [level: number]: boolean } = {};
|
|
6
4
|
|
|
7
|
-
constructor() {
|
|
5
|
+
constructor(public readonly id: string) {
|
|
8
6
|
for (let i = 0; i < 20; i++) {
|
|
9
7
|
this.levels[i] = 1;
|
|
10
8
|
}
|
|
@@ -16,20 +14,36 @@ export class ListNumbering {
|
|
|
16
14
|
}
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
clone(id: string): ListNumbering {
|
|
18
|
+
const retVal = new ListNumbering(id);
|
|
19
|
+
|
|
20
|
+
retVal.levels = structuredClone(retVal.levels);
|
|
21
|
+
retVal.forceStart = structuredClone(retVal.forceStart);
|
|
22
|
+
|
|
23
|
+
return retVal;
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export interface List {
|
|
25
28
|
level: number;
|
|
26
|
-
|
|
29
|
+
id?: string;
|
|
30
|
+
styleName: string;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export class ListTracker {
|
|
30
34
|
listStack: List[] = [];
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
pushList(id?: string, styleName = ''): List {
|
|
37
|
+
const list: List = {
|
|
38
|
+
id,
|
|
39
|
+
styleName,
|
|
40
|
+
level: this.listStack.length + 1,
|
|
41
|
+
};
|
|
42
|
+
this.listStack.push(list);
|
|
43
|
+
return list;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getCurrentList(): List {
|
|
47
|
+
return this.listStack[this.listStack.length - 1];
|
|
48
|
+
}
|
|
35
49
|
}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
|
+
import { NESTING_CLOSING, NESTING_OPENING } from '@kerebron/editor';
|
|
1
2
|
import { iterateChildren, NodeHandler, OdtStashContext } from '../OdtParser.js';
|
|
2
3
|
|
|
4
|
+
export function inchesToMm(value: string): number {
|
|
5
|
+
if (!value) {
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
8
|
+
if (value.endsWith('pt')) {
|
|
9
|
+
return parseFloat(value.substring(0, value.length - 2)) * 0.3528;
|
|
10
|
+
}
|
|
11
|
+
if (value.endsWith('in')) {
|
|
12
|
+
return parseFloat(value.substring(0, value.length - 2)) * 25.4;
|
|
13
|
+
}
|
|
14
|
+
if (value.endsWith('em')) {
|
|
15
|
+
return parseFloat(value.substring(0, value.length - 2)) / 0.125 * 25.4;
|
|
16
|
+
}
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
export function getInlineNodesHandlers(): Record<string, NodeHandler> {
|
|
4
21
|
return {
|
|
5
22
|
'$text': (ctx: OdtStashContext, value: any) => {
|
|
@@ -23,23 +40,21 @@ export function getInlineNodesHandlers(): Record<string, NodeHandler> {
|
|
|
23
40
|
}
|
|
24
41
|
},
|
|
25
42
|
'rect': (ctx: OdtStashContext, odtElement: any) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
if (odtElement['@rel-width'] === '100%') {
|
|
44
|
+
ctx.openNode();
|
|
45
|
+
ctx.closeNode('hr');
|
|
46
|
+
}
|
|
30
47
|
},
|
|
31
48
|
'line-break': (ctx: OdtStashContext, odtElement: any) => {
|
|
32
49
|
ctx.openNode();
|
|
33
50
|
ctx.closeNode('br');
|
|
34
51
|
},
|
|
35
52
|
'soft-page-break': (ctx: OdtStashContext, odtElement: any) => {
|
|
36
|
-
ctx.openNode();
|
|
37
|
-
ctx.closeNode('br');
|
|
38
53
|
},
|
|
39
54
|
|
|
40
55
|
'bookmark': (ctx: OdtStashContext, element: any) => { // bookmark for parent para
|
|
41
56
|
ctx.openNode();
|
|
42
|
-
ctx.closeNode('
|
|
57
|
+
ctx.closeNode('node_bookmark', {
|
|
43
58
|
id: element['@name'],
|
|
44
59
|
});
|
|
45
60
|
},
|
|
@@ -59,6 +74,20 @@ export function getInlineNodesHandlers(): Record<string, NodeHandler> {
|
|
|
59
74
|
// : x
|
|
60
75
|
// );
|
|
61
76
|
},
|
|
77
|
+
'change-start': (ctx: OdtStashContext, element: any) => {
|
|
78
|
+
ctx.openNode();
|
|
79
|
+
ctx.closeNode('comment', {
|
|
80
|
+
id: element['@change-id'],
|
|
81
|
+
nesting: NESTING_OPENING,
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
'change-end': (ctx: OdtStashContext, element: any) => {
|
|
85
|
+
ctx.openNode();
|
|
86
|
+
ctx.closeNode('comment', {
|
|
87
|
+
id: element['@change-id'],
|
|
88
|
+
nesting: NESTING_CLOSING,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
62
91
|
};
|
|
63
92
|
}
|
|
64
93
|
|
|
@@ -74,7 +103,7 @@ export function getBasicNodesHandlers(): Record<string, NodeHandler> {
|
|
|
74
103
|
);
|
|
75
104
|
},
|
|
76
105
|
'p': (ctx: OdtStashContext, value: any) => {
|
|
77
|
-
const attrs = {};
|
|
106
|
+
const attrs: Record<string, any> = {};
|
|
78
107
|
|
|
79
108
|
const style = ctx.getElementStyle(value);
|
|
80
109
|
const heading = style.styles.find((item) =>
|
|
@@ -103,13 +132,56 @@ export function getBasicNodesHandlers(): Record<string, NodeHandler> {
|
|
|
103
132
|
},
|
|
104
133
|
|
|
105
134
|
'table-of-content': (ctx: OdtStashContext, value: any) => {
|
|
106
|
-
|
|
135
|
+
const levels: number[] = [];
|
|
136
|
+
|
|
137
|
+
let prevMarginLeft = -1;
|
|
107
138
|
for (const pElem of value['index-body']['p']) {
|
|
139
|
+
const style = ctx.getElementStyle(pElem) as any;
|
|
140
|
+
let marginLeft = 0;
|
|
141
|
+
if ('paragraph-properties' in style) {
|
|
142
|
+
marginLeft = inchesToMm(
|
|
143
|
+
style['paragraph-properties']['@margin-left'],
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (prevMarginLeft < marginLeft) {
|
|
148
|
+
if (levels.length > 0) {
|
|
149
|
+
ctx.openNode(); // list_item
|
|
150
|
+
}
|
|
151
|
+
ctx.openNode(); // bullet_list
|
|
152
|
+
levels.push(marginLeft);
|
|
153
|
+
} else {
|
|
154
|
+
for (let i = levels.length - 1; i >= 0; i--) {
|
|
155
|
+
if (levels[i] > marginLeft) {
|
|
156
|
+
ctx.closeNode('bullet_list', { odtMarginLeft: marginLeft });
|
|
157
|
+
if (i > 0) {
|
|
158
|
+
ctx.closeNode('list_item');
|
|
159
|
+
}
|
|
160
|
+
levels.pop();
|
|
161
|
+
} else {
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
108
166
|
ctx.openNode();
|
|
109
167
|
ctx.handle('p', pElem);
|
|
110
168
|
ctx.closeNode('list_item');
|
|
169
|
+
|
|
170
|
+
prevMarginLeft = marginLeft;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (let i = levels.length - 1; i >= 0; i--) {
|
|
174
|
+
const marginLeft = levels.pop();
|
|
175
|
+
if (i > 0) {
|
|
176
|
+
ctx.closeNode('bullet_list', { odtMarginLeft: marginLeft });
|
|
177
|
+
ctx.closeNode('list_item');
|
|
178
|
+
} else {
|
|
179
|
+
ctx.closeNode('bullet_list', {
|
|
180
|
+
odtMarginLeft: marginLeft,
|
|
181
|
+
toc: true,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
111
184
|
}
|
|
112
|
-
ctx.closeNode('bullet_list');
|
|
113
185
|
},
|
|
114
186
|
|
|
115
187
|
'span': (ctx: OdtStashContext, value: any) => {
|