@adobe/helix-md2docx 1.4.0 → 1.4.3
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/CHANGELOG.md +22 -0
- package/package.json +7 -6
- package/src/md2docx/index.d.ts +4 -5
- package/src/md2docx/index.js +2 -2
- package/src/mdast2docx/handlers/image.js +87 -1
- package/src/mdast2docx/index.d.ts +25 -4
- package/src/mdast2docx/index.js +11 -1
- package/src/mdast2docx/mdast-download-images.js +27 -6
- package/src/mdast2docx/template/word/styles.xml +9 -0
- package/src/mdast2docx/template.docx +0 -0
- package/src/mdast2docx/utils.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [1.4.3](https://github.com/adobe/helix-md2docx/compare/v1.4.2...v1.4.3) (2022-04-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add support for svg images ([1f66ff4](https://github.com/adobe/helix-md2docx/commit/1f66ff4869a1bcd31e7cc092df623a55c8c83dab))
|
|
7
|
+
* **deps:** update dependency @adobe/helix-fetch to v3.0.9 ([35ffd0e](https://github.com/adobe/helix-md2docx/commit/35ffd0e867e61d4aa1ad0de50ef26ebb35d4fe1c))
|
|
8
|
+
|
|
9
|
+
## [1.4.2](https://github.com/adobe/helix-md2docx/compare/v1.4.1...v1.4.2) (2022-04-02)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **deps:** update adobe fixes ([#55](https://github.com/adobe/helix-md2docx/issues/55)) ([dc33fdc](https://github.com/adobe/helix-md2docx/commit/dc33fdcb4b55f4cc8aaf44be99f81bf9f8918fd1))
|
|
15
|
+
|
|
16
|
+
## [1.4.1](https://github.com/adobe/helix-md2docx/compare/v1.4.0...v1.4.1) (2022-03-24)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* link style ([2711009](https://github.com/adobe/helix-md2docx/commit/2711009ba525024bedd65361fd62dae1f0432fdb))
|
|
22
|
+
|
|
1
23
|
# [1.4.0](https://github.com/adobe/helix-md2docx/compare/v1.3.11...v1.4.0) (2022-03-24)
|
|
2
24
|
|
|
3
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-md2docx",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Helix Service that converts markdown to word documents",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -27,14 +27,15 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/adobe/helix-md2docx#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@adobe/helix-
|
|
30
|
+
"@adobe/helix-docx2md": "1.0.10",
|
|
31
|
+
"@adobe/helix-fetch": "3.0.9",
|
|
31
32
|
"@adobe/helix-markdown-support": "3.1.2",
|
|
32
33
|
"@adobe/helix-shared-process-queue": "1.1.4",
|
|
33
|
-
"@adobe/helix-docx2md": "1.0.10",
|
|
34
34
|
"docx": "7.3.0",
|
|
35
35
|
"hast-util-is-element": "2.1.2",
|
|
36
36
|
"hast-util-to-mdast": "8.3.1",
|
|
37
37
|
"image-size": "1.0.1",
|
|
38
|
+
"mime": "3.0.0",
|
|
38
39
|
"rehype-parse": "8.0.4",
|
|
39
40
|
"remark-gfm": "3.0.1",
|
|
40
41
|
"remark-parse": "10.0.1",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@adobe/eslint-config-helix": "1.3.2",
|
|
46
|
-
"@adobe/helix-mediahandler": "1.0.
|
|
47
|
+
"@adobe/helix-mediahandler": "1.0.18",
|
|
47
48
|
"@semantic-release/changelog": "6.0.1",
|
|
48
49
|
"@semantic-release/exec": "6.0.3",
|
|
49
50
|
"@semantic-release/git": "10.0.1",
|
|
@@ -51,9 +52,9 @@
|
|
|
51
52
|
"chai": "4.3.6",
|
|
52
53
|
"codecov": "3.8.3",
|
|
53
54
|
"dotenv": "16.0.0",
|
|
54
|
-
"eslint": "8.
|
|
55
|
+
"eslint": "8.13.0",
|
|
55
56
|
"eslint-plugin-header": "3.1.1",
|
|
56
|
-
"eslint-plugin-import": "2.
|
|
57
|
+
"eslint-plugin-import": "2.26.0",
|
|
57
58
|
"fs-extra": "10.0.1",
|
|
58
59
|
"husky": "7.0.4",
|
|
59
60
|
"junit-report-builder": "3.0.0",
|
package/src/md2docx/index.d.ts
CHANGED
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
import { Mdast2DocxOptions } from '../mdast2docx'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Converts the md to a word document (docx).
|
|
16
16
|
*
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {string} [stylesXML] The content of the styles.xml file of a Word template (to override provided default)
|
|
17
|
+
* @param {string} md The markdown document
|
|
18
|
+
* @param {Mdast2DocxOptions} [opts] options
|
|
20
19
|
* @returns {Promise<Buffer>} the docx
|
|
21
20
|
*/
|
|
22
|
-
export default function md2docx(
|
|
21
|
+
export default function md2docx(md: string, opts?: Mdast2DocxOptions): Promise<Buffer>;
|
package/src/md2docx/index.js
CHANGED
|
@@ -15,12 +15,12 @@ import gfm from 'remark-gfm';
|
|
|
15
15
|
import { remarkMatter } from '@adobe/helix-markdown-support';
|
|
16
16
|
import mdast2docx from '../mdast2docx/index.js';
|
|
17
17
|
|
|
18
|
-
export default async function md2docx(md,
|
|
18
|
+
export default async function md2docx(md, opts) {
|
|
19
19
|
const mdast = unified()
|
|
20
20
|
.use(remark, { position: false })
|
|
21
21
|
.use(gfm)
|
|
22
22
|
.use(remarkMatter)
|
|
23
23
|
.parse(md);
|
|
24
24
|
|
|
25
|
-
return mdast2docx(mdast,
|
|
25
|
+
return mdast2docx(mdast, opts);
|
|
26
26
|
}
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
import docx from 'docx';
|
|
13
13
|
import { findXMLComponent } from '../utils.js';
|
|
14
14
|
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
Drawing, ImageRun, XmlComponent, XmlAttributeComponent,
|
|
17
|
+
} = docx;
|
|
16
18
|
|
|
17
19
|
// max image width (6.5") and height (2")
|
|
18
20
|
const LIMITS = {
|
|
@@ -86,5 +88,89 @@ export default async function image(ctx, node) {
|
|
|
86
88
|
pic.root.push(drawing);
|
|
87
89
|
pic.key = data.key;
|
|
88
90
|
pic.imageData = imageData;
|
|
91
|
+
|
|
92
|
+
// for SVGs, we need to generate a proper svgBlip
|
|
93
|
+
/*
|
|
94
|
+
<pic:blipFill>
|
|
95
|
+
<a:blip r:embed="rId4">
|
|
96
|
+
<a:extLst>
|
|
97
|
+
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
|
|
98
|
+
<a14:useLocalDpi val="0" xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main"/>
|
|
99
|
+
</a:ext>
|
|
100
|
+
<a:ext uri="{96DAC541-7B7A-43D3-8B79-37D633B846F1}">
|
|
101
|
+
<asvg:svgBlip r:embed="rId5" xmlns:asvg="http://schemas.microsoft.com/office/drawing/2016/SVG/main"/>
|
|
102
|
+
</a:ext>
|
|
103
|
+
</a:extLst>
|
|
104
|
+
</a:blip>
|
|
105
|
+
<a:stretch>
|
|
106
|
+
<a:fillRect/>
|
|
107
|
+
</a:stretch>
|
|
108
|
+
</pic:blipFill>
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
class SvgBlip extends XmlComponent {
|
|
112
|
+
constructor(imgData) {
|
|
113
|
+
super('asvg:svgBlip');
|
|
114
|
+
this.imageData = imgData;
|
|
115
|
+
this.addChildElement(new XmlAttributeComponent({
|
|
116
|
+
'xmlns:asvg': 'http://schemas.microsoft.com/office/drawing/2016/SVG/main',
|
|
117
|
+
'r:embed': `rId{${imgData.fileName}}`,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
prepForXml(context) {
|
|
122
|
+
// add the svg data if it has a stream
|
|
123
|
+
if (this.imageData.stream) {
|
|
124
|
+
context.file.Media.addImage(this.imageData.fileName, this.imageData);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// add svg content type if missing
|
|
128
|
+
if (!context.file.contentTypes.root.find((entry) => entry.rootKey === 'Default'
|
|
129
|
+
&& (entry.root[0].root.extension === 'svg' || entry.root[0].root.Extension === 'svg'))) {
|
|
130
|
+
context.file.contentTypes.root.push(new XmlComponent('Default').addChildElement(new XmlAttributeComponent({
|
|
131
|
+
ContentType: 'image/svg+xml',
|
|
132
|
+
Extension: 'svg',
|
|
133
|
+
})));
|
|
134
|
+
}
|
|
135
|
+
return super.prepForXml(context);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (data.ext === 'svg' || data.svgBuffer) {
|
|
140
|
+
// create a fake image run for the svg image
|
|
141
|
+
const ir = new ImageRun({
|
|
142
|
+
data: data.svgBuffer,
|
|
143
|
+
transformation: data.dimensions,
|
|
144
|
+
});
|
|
145
|
+
ir.imageData.fileName = data.svgKey;
|
|
146
|
+
|
|
147
|
+
const blipFill = findXMLComponent(drawing, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill');
|
|
148
|
+
const blip = findXMLComponent(blipFill, 'a:blip');
|
|
149
|
+
|
|
150
|
+
// const blipAttrs = findXMLComponent(oldBlip, '_attr');
|
|
151
|
+
// blipAttrs.root.embed = `rId{${ir.imageData.fileName}}`;
|
|
152
|
+
|
|
153
|
+
// add svg stuff
|
|
154
|
+
// const newBlip = new XmlComponent('a:blip')
|
|
155
|
+
blip
|
|
156
|
+
.addChildElement(new XmlComponent('a:extLst')
|
|
157
|
+
.addChildElement(new XmlComponent('a:ext')
|
|
158
|
+
.addChildElement(new XmlAttributeComponent({
|
|
159
|
+
uri: '{28A0092B-C50C-407E-A947-70E740481C1C}',
|
|
160
|
+
}))
|
|
161
|
+
.addChildElement(new XmlComponent('a14:useLocalDpi')
|
|
162
|
+
.addChildElement(new XmlAttributeComponent({
|
|
163
|
+
'xmlns:a14': 'http://schemas.microsoft.com/office/drawing/2010/main',
|
|
164
|
+
val: '0',
|
|
165
|
+
}))))
|
|
166
|
+
.addChildElement(new XmlComponent('a:ext')
|
|
167
|
+
.addChildElement(new XmlAttributeComponent({
|
|
168
|
+
uri: '{96DAC541-7B7A-43D3-8B79-37D633B846F1}',
|
|
169
|
+
}))
|
|
170
|
+
.addChildElement(new SvgBlip(ir.imageData))));
|
|
171
|
+
|
|
172
|
+
// replace blip
|
|
173
|
+
// blipFill.root.splice(blipFill.root.indexOf(oldBlip), 1, newBlip);
|
|
174
|
+
}
|
|
89
175
|
return pic;
|
|
90
176
|
}
|
|
@@ -9,14 +9,35 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts an SVG to a PNG.
|
|
15
|
+
*
|
|
16
|
+
* @function
|
|
17
|
+
* @param {string} svg the input svg
|
|
18
|
+
* @return {string} the base64 encoded png
|
|
19
|
+
*/
|
|
20
|
+
declare type Svg2PngConverter = (svg:string) => Promise<string>;
|
|
21
|
+
|
|
22
|
+
declare interface Mdast2DocxOptions {
|
|
23
|
+
/**
|
|
24
|
+
* A console like logger
|
|
25
|
+
*/
|
|
26
|
+
log: Console;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The content of the styles.xml file of a Word template (to override provided default)
|
|
30
|
+
*/
|
|
31
|
+
stylesXML: string;
|
|
32
|
+
|
|
33
|
+
svg2png?: Svg2PngConverter;
|
|
34
|
+
}
|
|
13
35
|
|
|
14
36
|
/**
|
|
15
37
|
* Converts the mdast to a word document (docx).
|
|
16
38
|
*
|
|
17
39
|
* @param {Node} mdast The mdast
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {string} [stylesXML] The content of the styles.xml file of a Word template (to override provided default)
|
|
40
|
+
* @param {Mdast2DocxOptions} [opts] options
|
|
20
41
|
* @returns {Promise<Buffer>} the docx
|
|
21
42
|
*/
|
|
22
|
-
export default function mdast2docx(mdast: object,
|
|
43
|
+
export default function mdast2docx(mdast: object, opts?: Mdast2DocxOptions): Promise<Buffer>;
|
package/src/mdast2docx/index.js
CHANGED
|
@@ -24,7 +24,16 @@ import downloadImages from './mdast-download-images.js';
|
|
|
24
24
|
|
|
25
25
|
const { Document, Packer } = docx;
|
|
26
26
|
|
|
27
|
-
export default async function mdast2docx(mdast,
|
|
27
|
+
export default async function mdast2docx(mdast, opts = {}) {
|
|
28
|
+
const {
|
|
29
|
+
log = console,
|
|
30
|
+
svg2png,
|
|
31
|
+
} = opts;
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
stylesXML = null,
|
|
35
|
+
} = opts;
|
|
36
|
+
|
|
28
37
|
const ctx = {
|
|
29
38
|
handlers,
|
|
30
39
|
style: {},
|
|
@@ -33,6 +42,7 @@ export default async function mdast2docx(mdast, log = console, stylesXML = null)
|
|
|
33
42
|
listLevel: -1,
|
|
34
43
|
lists: [],
|
|
35
44
|
log,
|
|
45
|
+
svg2png,
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -15,6 +15,7 @@ import { context as fetchAPI, h1 } from '@adobe/helix-fetch';
|
|
|
15
15
|
import processQueue from '@adobe/helix-shared-process-queue';
|
|
16
16
|
import { visit } from 'unist-util-visit';
|
|
17
17
|
import getDimensions from 'image-size';
|
|
18
|
+
import mime from 'mime';
|
|
18
19
|
|
|
19
20
|
function createFetchContext() {
|
|
20
21
|
return process.env.HELIX_FETCH_FORCE_HTTP1
|
|
@@ -33,6 +34,11 @@ function hsize(bytes, decimals = 2) {
|
|
|
33
34
|
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @param ctx
|
|
39
|
+
* @param tree
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
36
42
|
export default async function downloadImages(ctx, tree) {
|
|
37
43
|
const context = createFetchContext();
|
|
38
44
|
const { fetch } = context;
|
|
@@ -53,15 +59,16 @@ export default async function downloadImages(ctx, tree) {
|
|
|
53
59
|
const ref = crypto.createHash('sha1')
|
|
54
60
|
.update(node.url)
|
|
55
61
|
.digest('hex');
|
|
56
|
-
|
|
57
|
-
node.data = ctx.images[key];
|
|
62
|
+
node.data = ctx.images[ref];
|
|
58
63
|
if (node.data) {
|
|
59
64
|
return;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
let buffer;
|
|
68
|
+
let type;
|
|
63
69
|
if (node.url.startsWith('data:image/png;base64,')) {
|
|
64
70
|
buffer = Buffer.from(node.url.split(',').pop(), 'base64');
|
|
71
|
+
type = 'image/png';
|
|
65
72
|
} else {
|
|
66
73
|
const idx = String(count).padStart(2, ' ');
|
|
67
74
|
count += 1;
|
|
@@ -73,15 +80,29 @@ export default async function downloadImages(ctx, tree) {
|
|
|
73
80
|
return;
|
|
74
81
|
}
|
|
75
82
|
buffer = await ret.buffer();
|
|
76
|
-
|
|
83
|
+
type = ret.headers.get('content-type');
|
|
84
|
+
ctx.log.info(`[${idx}] ${ret.status} ${hsize(buffer.length).padStart(10)} ${type}`);
|
|
85
|
+
}
|
|
86
|
+
const dimensions = getDimensions(buffer);
|
|
87
|
+
|
|
88
|
+
let svgBuffer;
|
|
89
|
+
if (type === 'image/svg+xml' && ctx.svg2png) {
|
|
90
|
+
svgBuffer = buffer;
|
|
91
|
+
buffer = Buffer.from(await ctx.svg2png(svgBuffer.toString('utf-8')), 'base64');
|
|
92
|
+
type = 'image/png';
|
|
77
93
|
}
|
|
78
94
|
|
|
95
|
+
const ext = mime.getExtension(type);
|
|
96
|
+
|
|
79
97
|
node.data = {
|
|
80
|
-
|
|
98
|
+
ext,
|
|
99
|
+
key: `${ref}.${ext}`,
|
|
81
100
|
buffer,
|
|
82
|
-
|
|
101
|
+
svgBuffer,
|
|
102
|
+
svgKey: `${ref}.svg`,
|
|
103
|
+
dimensions,
|
|
83
104
|
};
|
|
84
|
-
ctx.images[
|
|
105
|
+
ctx.images[ref] = node.data;
|
|
85
106
|
} catch (error) {
|
|
86
107
|
ctx.log.error(`Cannot download image ${node.url}: ${error.message}`);
|
|
87
108
|
}
|
|
@@ -1521,6 +1521,15 @@
|
|
|
1521
1521
|
<w:semiHidden/>
|
|
1522
1522
|
<w:unhideWhenUsed/>
|
|
1523
1523
|
</w:style>
|
|
1524
|
+
<w:style w:type="character" w:styleId="Hyperlink">
|
|
1525
|
+
<w:name w:val="Hyperlink"/>
|
|
1526
|
+
<w:uiPriority w:val="99"/>
|
|
1527
|
+
<w:unhideWhenUsed/>
|
|
1528
|
+
<w:rPr>
|
|
1529
|
+
<w:color w:val="0563C1"/>
|
|
1530
|
+
<w:u w:val="single"/>
|
|
1531
|
+
</w:rPr>
|
|
1532
|
+
</w:style>
|
|
1524
1533
|
<w:style
|
|
1525
1534
|
w:type="numbering"
|
|
1526
1535
|
w:default="1"
|
|
Binary file
|
package/src/mdast2docx/utils.js
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('docx').XmlComponent} XmlComponent
|
|
15
|
+
*
|
|
16
|
+
* @param {XmlComponent} root
|
|
17
|
+
* @param path
|
|
18
|
+
* @returns {XmlComponent}
|
|
19
|
+
*/
|
|
13
20
|
// eslint-disable-next-line import/prefer-default-export
|
|
14
21
|
export function findXMLComponent(root, path) {
|
|
15
22
|
const segs = path.split('/');
|