Skip to content

Commit 6dced4a

Browse files
committed
tests and export
1 parent 0fb9de7 commit 6dced4a

9 files changed

Lines changed: 210 additions & 30 deletions

File tree

packages/super-editor/src/core/super-converter/exporter.js

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,23 +1440,116 @@ function translateImageNode(params, imageSize) {
14401440
params.media[`${cleanUrl}_${hash}.${type}`] = src;
14411441
}
14421442

1443-
const inlineAttrs = attrs.originalPadding || {
1443+
let inlineAttrs = attrs.originalPadding || {
14441444
distT: 0,
14451445
distB: 0,
14461446
distL: 0,
14471447
distR: 0,
14481448
};
14491449

1450+
const anchorElements = [];
1451+
let wrapProp = [];
1452+
1453+
// Handle anchor image export
1454+
if (attrs.isAnchor) {
1455+
inlineAttrs = {
1456+
...inlineAttrs,
1457+
simplePos: attrs.originalAttributes?.simplePos,
1458+
relativeHeight: 1,
1459+
behindDoc: attrs.originalAttributes?.behindDoc,
1460+
locked: attrs.originalAttributes?.locked,
1461+
layoutInCell: attrs.originalAttributes?.layoutInCell,
1462+
allowOverlap: attrs.originalAttributes?.allowOverlap,
1463+
};
1464+
if (attrs.simplePos) {
1465+
anchorElements.push({
1466+
name: 'wp:simplePos',
1467+
attributes: {
1468+
x: 0,
1469+
y: 0,
1470+
}
1471+
});
1472+
}
1473+
1474+
if (attrs.anchorData) {
1475+
anchorElements.push({
1476+
name: 'wp:positionH',
1477+
attributes: {
1478+
relativeFrom: attrs.anchorData.hRelativeFrom,
1479+
},
1480+
...(attrs.marginOffset.left && {
1481+
elements: [{
1482+
name: 'wp:posOffset',
1483+
elements: [{
1484+
type: 'text',
1485+
text: pixelsToEmu(attrs.marginOffset.left).toString(),
1486+
}],
1487+
}]
1488+
}),
1489+
...(attrs.anchorData.alignH && {
1490+
elements: [{
1491+
name: 'wp:align',
1492+
elements: [{
1493+
type: 'text',
1494+
text: attrs.anchorData.alignH,
1495+
}],
1496+
}]
1497+
})
1498+
});
1499+
anchorElements.push({
1500+
name: 'wp:positionV',
1501+
attributes: {
1502+
relativeFrom: attrs.anchorData.vRelativeFrom,
1503+
},
1504+
...(attrs.marginOffset.top && {
1505+
elements: [{
1506+
name: 'wp:posOffset',
1507+
elements: [{
1508+
type: 'text',
1509+
text: pixelsToEmu(attrs.marginOffset.top).toString(),
1510+
}],
1511+
}]
1512+
}),
1513+
...(attrs.anchorData.alignV && {
1514+
elements: [{
1515+
name: 'wp:align',
1516+
elements: [{
1517+
type: 'text',
1518+
text: attrs.anchorData.alignV,
1519+
}],
1520+
}]
1521+
})
1522+
});
1523+
}
1524+
1525+
if (attrs.wrapText) {
1526+
wrapProp.push({
1527+
name: 'wp:wrapSquare',
1528+
attributes: {
1529+
wrapText: attrs.wrapText,
1530+
}
1531+
});
1532+
}
1533+
1534+
if (attrs.wrapTopAndBottom) {
1535+
wrapProp.push({
1536+
name: 'wp:wrapTopAndBottom',
1537+
});
1538+
}
1539+
}
1540+
14501541
const drawingXmlns = 'http://schemas.openxmlformats.org/drawingml/2006/main';
14511542
const pictureXmlns = 'http://schemas.openxmlformats.org/drawingml/2006/picture';
1452-
return wrapTextInRun(
1543+
1544+
const textNode = wrapTextInRun(
14531545
{
14541546
name: 'w:drawing',
14551547
elements: [
14561548
{
1457-
name: 'wp:inline',
1549+
name: attrs.isAnchor ? 'wp:anchor' : 'wp:inline',
14581550
attributes: inlineAttrs,
14591551
elements: [
1552+
...anchorElements,
14601553
{
14611554
name: 'wp:extent',
14621555
attributes: {
@@ -1473,12 +1566,13 @@ function translateImageNode(params, imageSize) {
14731566
b: 0,
14741567
},
14751568
},
1569+
...wrapProp,
14761570
{
14771571
name: 'wp:docPr',
14781572
attributes: {
1479-
id: 0,
1480-
name: '',
1481-
descr: '',
1573+
id: attrs.id || 0,
1574+
name: attrs.alt,
1575+
descr: attrs.title,
14821576
},
14831577
},
14841578
{
@@ -1511,9 +1605,8 @@ function translateImageNode(params, imageSize) {
15111605
{
15121606
name: 'pic:cNvPr',
15131607
attributes: {
1514-
id: 0,
1515-
name: '',
1516-
desc: '',
1608+
id: attrs.id || 0,
1609+
name: attrs.title,
15171610
},
15181611
},
15191612
{
@@ -1537,12 +1630,8 @@ function translateImageNode(params, imageSize) {
15371630
name: 'a:blip',
15381631
attributes: {
15391632
'r:embed': imageId,
1540-
cstate: 'none',
15411633
},
15421634
},
1543-
{
1544-
name: 'a:srcRect',
1545-
},
15461635
{
15471636
name: 'a:stretch',
15481637
elements: [{ name: 'a:fillRect' }],
@@ -1579,6 +1668,9 @@ function translateImageNode(params, imageSize) {
15791668
attributes: { prst: 'rect' },
15801669
elements: [{ name: 'a:avLst' }],
15811670
},
1671+
{
1672+
name: 'a:noFill'
1673+
}
15821674
],
15831675
},
15841676
],
@@ -1593,6 +1685,8 @@ function translateImageNode(params, imageSize) {
15931685
},
15941686
[],
15951687
);
1688+
1689+
return textNode;
15961690
}
15971691

15981692
/**

packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export const handleDrawingNode = (params) => {
2626

2727
// Some images are identified by wp:anchor
2828
const isAnchor = elements.find((el) => el.name === 'wp:anchor');
29-
if (isAnchor) result = handleImageImport(elements[0], currentFileName, params);
29+
if (isAnchor) {
30+
31+
result = handleImageImport(elements[0], currentFileName, params);
32+
result.attrs.isAnchor = isAnchor;
33+
}
3034

3135
// Others, wp:inline
3236
const inlineImage = elements.find((el) => el.name === 'wp:inline');
@@ -77,6 +81,12 @@ export function handleImageImport(node, currentFileName, params) {
7781
const vRelativeFrom = positionVTag?.attributes.relativeFrom;
7882
const alignV = positionVTag?.elements.find((el) => el.name === 'wp:align')?.elements[0]?.text;
7983

84+
const simplePos = node.elements.find((el) => el.name === 'wp:simplePos');
85+
const wrapSquare = node.elements.find((el) => el.name === 'wp:wrapSquare');
86+
const wrapTopAndBottom = node.elements.find((el) => el.name === 'wp:wrapTopAndBottom');
87+
88+
const docPr = node.elements.find((el) => el.name === 'wp:docPr');
89+
8090
let anchorData = null;
8191
if (hRelativeFrom || alignH || vRelativeFrom || alignV) {
8292
anchorData = {
@@ -86,7 +96,6 @@ export function handleImageImport(node, currentFileName, params) {
8696
alignV,
8797
};
8898
}
89-
9099

91100
const marginOffset = {
92101
left: positionHValue,
@@ -111,18 +120,31 @@ export function handleImageImport(node, currentFileName, params) {
111120
type: 'image',
112121
attrs: {
113122
src: path,
114-
alt: 'Image',
123+
alt: docPr?.attributes.name || 'Image',
124+
id: docPr?.attributes.id || '',
125+
title: docPr?.attributes.descr || 'Image',
115126
inline: true,
116127
padding,
117128
marginOffset,
118129
size,
119130
anchorData,
131+
...(simplePos && {
132+
simplePos: {
133+
x: simplePos.attributes.x,
134+
y: simplePos.attributes.y,
135+
}
136+
}),
137+
...(wrapSquare && {
138+
wrapText: wrapSquare.attributes.wrapText
139+
}),
140+
wrapTopAndBottom: !!wrapTopAndBottom,
120141
originalPadding: {
121142
distT: attributes['distT'],
122143
distB: attributes['distB'],
123144
distL: attributes['distL'],
124145
distR: attributes['distR'],
125146
},
147+
originalAttributes: node.attributes,
126148
rId: relAttributes['Id'],
127149
},
128150
};

packages/super-editor/src/extensions/image/image.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export const Image = Node.create({
4040
alt: {
4141
default: null,
4242
},
43+
44+
id: { rendered: false },
4345

4446
title: {
4547
default: null,
@@ -54,11 +56,17 @@ export const Image = Node.create({
5456
default: null,
5557
rendered: false,
5658
},
59+
originalAttributes: { rendered: false },
60+
wrapTopAndBottom: { rendered: false },
5761

5862
anchorData: {
5963
default: null,
6064
rendered: false,
6165
},
66+
67+
isAnchor: { rendered: false },
68+
simplePos: { rendered: false },
69+
wrapText: { rendered: false },
6270

6371
size: {
6472
default: {},

packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const ImagePositionPlugin = ({ editor }) => {
3434
);
3535
view.dispatch(updateTransaction);
3636
}
37-
if (pagination.isReadyToInit) {
37+
if (pagination?.isReadyToInit) {
3838
shouldUpdate = true;
3939
}
4040
},
26 KB
Binary file not shown.
-173 Bytes
Binary file not shown.
162 Bytes
Binary file not shown.

packages/super-editor/src/tests/export/imageNodeExporter.test.js

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,51 @@ describe('ImageNodeExporter', async () => {
1515

1616
it('export image node correctly', () => {
1717
const imageNode = body.elements[0].elements[1].elements[0];
18-
expect(imageNode.elements[0].attributes.distT).toBe('114935');
19-
expect(imageNode.elements[0].attributes.distB).toBe('114935');
20-
expect(imageNode.elements[0].attributes.distL).toBe('114300');
21-
expect(imageNode.elements[0].attributes.distR).toBe('114300');
22-
18+
expect(imageNode.elements[0].attributes.distT).toBe('0');
19+
expect(imageNode.elements[0].attributes.distB).toBe('0');
20+
expect(imageNode.elements[0].attributes.distL).toBe('0');
21+
expect(imageNode.elements[0].attributes.distR).toBe('0');
22+
2323
expect(imageNode.elements[0].elements[0].attributes.cx).toBe(5734050);
2424
expect(imageNode.elements[0].elements[0].attributes.cy).toBe(8601075);
25+
2526
expect(imageNode.elements[0].elements[4].elements[0].elements[0].elements[1].elements[0].attributes['r:embed']).toBe('rId4');
2627
});
28+
29+
it('exports anchor image node correctly', async() => {
30+
31+
});
32+
});
33+
34+
describe('ImageNodeExporter anchor image', async () => {
35+
window.URL.createObjectURL = vi.fn().mockImplementation((file) => {
36+
return file.name;
37+
});
38+
39+
const fileName = 'anchor_images.docx';
40+
const result = await getExportedResult(fileName);
41+
const body = {};
42+
43+
beforeEach(() => {
44+
Object.assign(body, result.elements?.find((el) => el.name === 'w:body'));
45+
});
46+
47+
it('exports anchor image node correctly', async() => {
48+
const imageNode = body.elements[1].elements[4].elements[0];
49+
const anchorNode = imageNode.elements[0];
50+
51+
expect(anchorNode.attributes).toHaveProperty('simplePos', '0');
52+
expect(anchorNode.elements[0].name).toBe('wp:simplePos');
53+
console.log(anchorNode.elements[5]);
54+
expect(anchorNode.elements[1].attributes.relativeFrom).toBe('margin');
55+
expect(anchorNode.elements[1].elements[0].name).toBe('wp:align');
56+
expect(anchorNode.elements[1].elements[0].elements[0].text).toBe('left');
57+
58+
expect(anchorNode.elements[2].attributes.relativeFrom).toBe('margin');
59+
expect(anchorNode.elements[2].elements[0].name).toBe('wp:align');
60+
expect(anchorNode.elements[2].elements[0].elements[0].text).toBe('top');
61+
62+
expect(anchorNode.elements[5].name).toBe('wp:wrapSquare');
63+
expect(anchorNode.elements[5].attributes.wrapText).toBe('bothSides');
64+
});
2765
});

packages/super-editor/src/tests/import/imageImporter.test.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('ImageNodeImporter', () => {
1717
const paragraphNode = nodes[0];
1818
const drawingNode = paragraphNode.content[0];
1919
const { attrs } = drawingNode;
20-
const { padding, marginOffset, size } = attrs;
20+
const { padding, size } = attrs;
2121

2222
expect(paragraphNode.type).toBe('paragraph');
2323
expect(drawingNode.type).toBe('image');
@@ -27,13 +27,31 @@ describe('ImageNodeImporter', () => {
2727

2828
expect(size).toHaveProperty('width', 602);
2929
expect(size).toHaveProperty('height', 903);
30-
31-
expect(marginOffset).toHaveProperty('left', 12);
32-
expect(marginOffset).toHaveProperty('top', 12);
3330

34-
expect(padding).toHaveProperty('left', 12);
35-
expect(padding).toHaveProperty('top', 12);
36-
expect(padding).toHaveProperty('bottom', 12);
37-
expect(padding).toHaveProperty('right', 12);
31+
expect(padding).toHaveProperty('left', 0);
32+
expect(padding).toHaveProperty('top', 0);
33+
expect(padding).toHaveProperty('bottom', 0);
34+
expect(padding).toHaveProperty('right', 0);
35+
});
36+
37+
it('imports anchor image node correctly', async() => {
38+
const dataName = 'anchor_images.docx';
39+
const docx = await getTestDataByFileName(dataName);
40+
const documentXml = docx['word/document.xml'];
41+
42+
const doc = documentXml.elements[0];
43+
const body = doc.elements[0];
44+
const content = body.elements;
45+
const { nodes } = handleParagraphNode({ nodes: [content[1]], docx, nodeListHandler: defaultNodeListHandler() });
46+
47+
const paragraphNode = nodes[0];
48+
const drawingNode = paragraphNode.content[3];
49+
const { attrs } = drawingNode;
50+
const { anchorData } = attrs;
51+
52+
expect(anchorData).toHaveProperty('hRelativeFrom', 'margin');
53+
expect(anchorData).toHaveProperty('vRelativeFrom', 'margin');
54+
expect(anchorData).toHaveProperty('alignH', 'left');
55+
expect(anchorData).toHaveProperty('alignV', 'top');
3856
});
3957
});

0 commit comments

Comments
 (0)