From 0fb9de797e74563eaefabf7b63f43f7e70f64edc Mon Sep 17 00:00:00 2001 From: VladaHarbour Date: Tue, 6 May 2025 18:49:25 +0300 Subject: [PATCH 1/2] HAR-9435 Anchor images --- .../v2/importer/imageImporter.js | 18 ++- .../src/extensions/image/image.js | 21 ++- .../image/imageHelpers/imagePositionPlugin.js | 120 ++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js diff --git a/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js b/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js index 902944f496..26a3a49dd1 100644 --- a/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js +++ b/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js @@ -57,7 +57,7 @@ export function handleImageImport(node, currentFileName, params) { const shapeURI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; if (!!uri && uri === shapeURI) { return handleShapeDrawing(params, node, graphicData); - }; + } const picture = graphicData.elements.find((el) => el.name === 'pic:pic'); if (!picture || !picture.elements) return null; @@ -68,11 +68,26 @@ export function handleImageImport(node, currentFileName, params) { const positionHTag = node.elements.find((el) => el.name === 'wp:positionH'); const positionH = positionHTag?.elements.find((el) => el.name === 'wp:posOffset'); const positionHValue = emuToPixels(positionH?.elements[0]?.text); + const hRelativeFrom = positionHTag?.attributes.relativeFrom; + const alignH = positionHTag?.elements.find((el) => el.name === 'wp:align')?.elements[0]?.text; const positionVTag = node.elements.find((el) => el.name === 'wp:positionV'); const positionV = positionVTag?.elements.find((el) => el.name === 'wp:posOffset'); const positionVValue = emuToPixels(positionV?.elements[0]?.text); + const vRelativeFrom = positionVTag?.attributes.relativeFrom; + const alignV = positionVTag?.elements.find((el) => el.name === 'wp:align')?.elements[0]?.text; + + let anchorData = null; + if (hRelativeFrom || alignH || vRelativeFrom || alignV) { + anchorData = { + hRelativeFrom, + vRelativeFrom, + alignH, + alignV, + }; + } + const marginOffset = { left: positionHValue, top: positionVValue, @@ -101,6 +116,7 @@ export function handleImageImport(node, currentFileName, params) { padding, marginOffset, size, + anchorData, originalPadding: { distT: attributes['distT'], distB: attributes['distB'], diff --git a/packages/super-editor/src/extensions/image/image.js b/packages/super-editor/src/extensions/image/image.js index 9306243e05..48c8068011 100644 --- a/packages/super-editor/src/extensions/image/image.js +++ b/packages/super-editor/src/extensions/image/image.js @@ -1,5 +1,6 @@ import { Node, Attribute } from '@core/index.js'; import { ImagePlaceholderPlugin } from './imageHelpers/imagePlaceholderPlugin.js'; +import { ImagePositionPlugin } from './imageHelpers/imagePositionPlugin.js'; export const Image = Node.create({ name: 'image', @@ -54,6 +55,11 @@ export const Image = Node.create({ rendered: false, }, + anchorData: { + default: null, + rendered: false, + }, + size: { default: {}, renderDOM: ({ size }) => { @@ -64,6 +70,19 @@ export const Image = Node.create({ return { style }; }, }, + + padding: { + default: {}, + renderDOM: ({ padding, marginOffset }) => { + let { left = 0, top = 0, bottom = 0, right = 0 } = padding ?? {}; + let style = ''; + if (left && !marginOffset?.left) style += `margin-left: ${left}px;`; + if (top && !marginOffset?.top) style += `margin-top: ${top}px;`; + if (bottom) style += `margin-bottom: ${bottom}px;`; + if (right) style += `margin-right: ${right}px;`; + return { style }; + }, + }, marginOffset: { default: {}, @@ -113,6 +132,6 @@ export const Image = Node.create({ }, addPmPlugins() { - return [ImagePlaceholderPlugin()]; + return [ImagePlaceholderPlugin(), ImagePositionPlugin({editor: this.editor })]; }, }); diff --git a/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js b/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js new file mode 100644 index 0000000000..416a73e83a --- /dev/null +++ b/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js @@ -0,0 +1,120 @@ +import { Plugin, PluginKey } from 'prosemirror-state'; +import { Decoration, DecorationSet } from 'prosemirror-view'; +import { PaginationPluginKey } from '../../pagination/pagination-helpers.js'; + +const ImagePositionPluginKey = new PluginKey('ImagePosition'); +export const ImagePositionPlugin = ({ editor }) => { + const { view } = editor; + let shouldUpdate = false; + return new Plugin({ + name: 'ImagePositionPlugin', + key: ImagePositionPluginKey, + + state: { + init(_, state) { + return DecorationSet.empty; + }, + + apply(tr, oldDecorationSet, oldState, newState) { + const decorations = getImagePositionDecorations(newState, view); + return DecorationSet.create(newState.doc, decorations); + }, + }, + + view: (view) => { + return { + update: (view, lastState) => { + const pagination = PaginationPluginKey.getState(lastState); + if (shouldUpdate) { + shouldUpdate = false; + const decorations = getImagePositionDecorations(lastState, view); + const updateTransaction = view.state.tr.setMeta( + ImagePositionPluginKey, + { decorations } + ); + view.dispatch(updateTransaction); + } + if (pagination.isReadyToInit) { + shouldUpdate = true; + } + }, + }; + }, + + props: { + decorations(state) { + return this.getState(state); + }, + }, + }); +}; + +const getImagePositionDecorations = (state, view) => { + let decorations = []; + state.doc.descendants((node, pos) => { + if (node.attrs.anchorData) { + let style = ''; + let className = ''; + const { vRelativeFrom, alignH } = node.attrs.anchorData; + const { size, padding } = node.attrs; + + const pageBreak = findPreviousDomNodeWithClass(view, pos, 'pagination-break-wrapper'); + if (pageBreak) { + switch (alignH) { + case 'left': + style += 'float: left; left: 0; margin-left: 0; '; + break; + case 'right': + style += 'float: right; right: 0; margin-right: 0; '; + break; + case 'center': + style += 'display: block; margin-left: auto; margin-right: auto; '; + break; + } + style += vRelativeFrom === 'margin' ? `position: absolute; top: ${pageBreak?.offsetTop + pageBreak?.offsetHeight}px; ` : ''; + + if (vRelativeFrom === 'margin') { + const nextPos = view.posAtDOM(pageBreak, 1); + const imageBlock = document.createElement('div'); + imageBlock.className = 'anchor-image-placeholder'; + imageBlock.style.float = alignH; + imageBlock.style.width = size.width + parseInt(padding[alignH]) + 'px'; + imageBlock.style.height = size.height + parseInt(padding.top) + parseInt(padding.bottom) + 'px'; + decorations.push(Decoration.widget(nextPos, imageBlock, { key: 'stable-key' })); + } + } + + decorations.push( + Decoration.node(pos, pos + node.nodeSize, { style, class: className }), + ); + } + }); + return decorations; +}; + +const findPreviousDomNodeWithClass = (view, pos, className) => { + let { node } = view.domAtPos(pos); + + // If you get a text node, go to its parent + if (node.nodeType === 3) { + node = node.parentNode; + } + + // Walk backward over siblings and their ancestors + while (node) { + if (node.classList && node.classList.contains(className)) { + return node; + } + if (node.previousSibling) { + node = node.previousSibling; + // Dive to the last child if it's an element with children + while (node && node.lastChild) { + node = node.lastChild; + } + } else { + node = node.parentNode; + } + } + + return null; // Not found +} From 6dced4af71603185e4e1d0eeaa225d8df7e7aaad Mon Sep 17 00:00:00 2001 From: VladaHarbour Date: Thu, 8 May 2025 14:21:33 +0300 Subject: [PATCH 2/2] tests and export --- .../src/core/super-converter/exporter.js | 120 ++++++++++++++++-- .../v2/importer/imageImporter.js | 28 +++- .../src/extensions/image/image.js | 8 ++ .../image/imageHelpers/imagePositionPlugin.js | 2 +- .../src/tests/data/anchor_images.docx | Bin 0 -> 26587 bytes .../src/tests/data/image_doc.docx | Bin 1038235 -> 1038062 bytes .../src/tests/data/~$age_doc.docx | Bin 0 -> 162 bytes .../tests/export/imageNodeExporter.test.js | 48 ++++++- .../src/tests/import/imageImporter.test.js | 34 +++-- 9 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 packages/super-editor/src/tests/data/anchor_images.docx create mode 100644 packages/super-editor/src/tests/data/~$age_doc.docx diff --git a/packages/super-editor/src/core/super-converter/exporter.js b/packages/super-editor/src/core/super-converter/exporter.js index cc3595e6b6..f73927206f 100644 --- a/packages/super-editor/src/core/super-converter/exporter.js +++ b/packages/super-editor/src/core/super-converter/exporter.js @@ -1440,23 +1440,116 @@ function translateImageNode(params, imageSize) { params.media[`${cleanUrl}_${hash}.${type}`] = src; } - const inlineAttrs = attrs.originalPadding || { + let inlineAttrs = attrs.originalPadding || { distT: 0, distB: 0, distL: 0, distR: 0, }; + const anchorElements = []; + let wrapProp = []; + + // Handle anchor image export + if (attrs.isAnchor) { + inlineAttrs = { + ...inlineAttrs, + simplePos: attrs.originalAttributes?.simplePos, + relativeHeight: 1, + behindDoc: attrs.originalAttributes?.behindDoc, + locked: attrs.originalAttributes?.locked, + layoutInCell: attrs.originalAttributes?.layoutInCell, + allowOverlap: attrs.originalAttributes?.allowOverlap, + }; + if (attrs.simplePos) { + anchorElements.push({ + name: 'wp:simplePos', + attributes: { + x: 0, + y: 0, + } + }); + } + + if (attrs.anchorData) { + anchorElements.push({ + name: 'wp:positionH', + attributes: { + relativeFrom: attrs.anchorData.hRelativeFrom, + }, + ...(attrs.marginOffset.left && { + elements: [{ + name: 'wp:posOffset', + elements: [{ + type: 'text', + text: pixelsToEmu(attrs.marginOffset.left).toString(), + }], + }] + }), + ...(attrs.anchorData.alignH && { + elements: [{ + name: 'wp:align', + elements: [{ + type: 'text', + text: attrs.anchorData.alignH, + }], + }] + }) + }); + anchorElements.push({ + name: 'wp:positionV', + attributes: { + relativeFrom: attrs.anchorData.vRelativeFrom, + }, + ...(attrs.marginOffset.top && { + elements: [{ + name: 'wp:posOffset', + elements: [{ + type: 'text', + text: pixelsToEmu(attrs.marginOffset.top).toString(), + }], + }] + }), + ...(attrs.anchorData.alignV && { + elements: [{ + name: 'wp:align', + elements: [{ + type: 'text', + text: attrs.anchorData.alignV, + }], + }] + }) + }); + } + + if (attrs.wrapText) { + wrapProp.push({ + name: 'wp:wrapSquare', + attributes: { + wrapText: attrs.wrapText, + } + }); + } + + if (attrs.wrapTopAndBottom) { + wrapProp.push({ + name: 'wp:wrapTopAndBottom', + }); + } + } + const drawingXmlns = 'http://schemas.openxmlformats.org/drawingml/2006/main'; const pictureXmlns = 'http://schemas.openxmlformats.org/drawingml/2006/picture'; - return wrapTextInRun( + + const textNode = wrapTextInRun( { name: 'w:drawing', elements: [ { - name: 'wp:inline', + name: attrs.isAnchor ? 'wp:anchor' : 'wp:inline', attributes: inlineAttrs, elements: [ + ...anchorElements, { name: 'wp:extent', attributes: { @@ -1473,12 +1566,13 @@ function translateImageNode(params, imageSize) { b: 0, }, }, + ...wrapProp, { name: 'wp:docPr', attributes: { - id: 0, - name: '', - descr: '', + id: attrs.id || 0, + name: attrs.alt, + descr: attrs.title, }, }, { @@ -1511,9 +1605,8 @@ function translateImageNode(params, imageSize) { { name: 'pic:cNvPr', attributes: { - id: 0, - name: '', - desc: '', + id: attrs.id || 0, + name: attrs.title, }, }, { @@ -1537,12 +1630,8 @@ function translateImageNode(params, imageSize) { name: 'a:blip', attributes: { 'r:embed': imageId, - cstate: 'none', }, }, - { - name: 'a:srcRect', - }, { name: 'a:stretch', elements: [{ name: 'a:fillRect' }], @@ -1579,6 +1668,9 @@ function translateImageNode(params, imageSize) { attributes: { prst: 'rect' }, elements: [{ name: 'a:avLst' }], }, + { + name: 'a:noFill' + } ], }, ], @@ -1593,6 +1685,8 @@ function translateImageNode(params, imageSize) { }, [], ); + + return textNode; } /** diff --git a/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js b/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js index 26a3a49dd1..f8d52ba4b3 100644 --- a/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js +++ b/packages/super-editor/src/core/super-converter/v2/importer/imageImporter.js @@ -26,7 +26,11 @@ export const handleDrawingNode = (params) => { // Some images are identified by wp:anchor const isAnchor = elements.find((el) => el.name === 'wp:anchor'); - if (isAnchor) result = handleImageImport(elements[0], currentFileName, params); + if (isAnchor) { + + result = handleImageImport(elements[0], currentFileName, params); + result.attrs.isAnchor = isAnchor; + } // Others, wp:inline const inlineImage = elements.find((el) => el.name === 'wp:inline'); @@ -77,6 +81,12 @@ export function handleImageImport(node, currentFileName, params) { const vRelativeFrom = positionVTag?.attributes.relativeFrom; const alignV = positionVTag?.elements.find((el) => el.name === 'wp:align')?.elements[0]?.text; + const simplePos = node.elements.find((el) => el.name === 'wp:simplePos'); + const wrapSquare = node.elements.find((el) => el.name === 'wp:wrapSquare'); + const wrapTopAndBottom = node.elements.find((el) => el.name === 'wp:wrapTopAndBottom'); + + const docPr = node.elements.find((el) => el.name === 'wp:docPr'); + let anchorData = null; if (hRelativeFrom || alignH || vRelativeFrom || alignV) { anchorData = { @@ -86,7 +96,6 @@ export function handleImageImport(node, currentFileName, params) { alignV, }; } - const marginOffset = { left: positionHValue, @@ -111,18 +120,31 @@ export function handleImageImport(node, currentFileName, params) { type: 'image', attrs: { src: path, - alt: 'Image', + alt: docPr?.attributes.name || 'Image', + id: docPr?.attributes.id || '', + title: docPr?.attributes.descr || 'Image', inline: true, padding, marginOffset, size, anchorData, + ...(simplePos && { + simplePos: { + x: simplePos.attributes.x, + y: simplePos.attributes.y, + } + }), + ...(wrapSquare && { + wrapText: wrapSquare.attributes.wrapText + }), + wrapTopAndBottom: !!wrapTopAndBottom, originalPadding: { distT: attributes['distT'], distB: attributes['distB'], distL: attributes['distL'], distR: attributes['distR'], }, + originalAttributes: node.attributes, rId: relAttributes['Id'], }, }; diff --git a/packages/super-editor/src/extensions/image/image.js b/packages/super-editor/src/extensions/image/image.js index 48c8068011..2cc5c8190a 100644 --- a/packages/super-editor/src/extensions/image/image.js +++ b/packages/super-editor/src/extensions/image/image.js @@ -40,6 +40,8 @@ export const Image = Node.create({ alt: { default: null, }, + + id: { rendered: false }, title: { default: null, @@ -54,11 +56,17 @@ export const Image = Node.create({ default: null, rendered: false, }, + originalAttributes: { rendered: false }, + wrapTopAndBottom: { rendered: false }, anchorData: { default: null, rendered: false, }, + + isAnchor: { rendered: false }, + simplePos: { rendered: false }, + wrapText: { rendered: false }, size: { default: {}, diff --git a/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js b/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js index 416a73e83a..c720088cfb 100644 --- a/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js +++ b/packages/super-editor/src/extensions/image/imageHelpers/imagePositionPlugin.js @@ -34,7 +34,7 @@ export const ImagePositionPlugin = ({ editor }) => { ); view.dispatch(updateTransaction); } - if (pagination.isReadyToInit) { + if (pagination?.isReadyToInit) { shouldUpdate = true; } }, diff --git a/packages/super-editor/src/tests/data/anchor_images.docx b/packages/super-editor/src/tests/data/anchor_images.docx new file mode 100644 index 0000000000000000000000000000000000000000..523d908cdb14e9b9ef97189b902b12c8899cf3d2 GIT binary patch literal 26587 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 literal 0 HcmV?d00001 diff --git a/packages/super-editor/src/tests/data/image_doc.docx b/packages/super-editor/src/tests/data/image_doc.docx index f9a55a41ebf53f389e79db84511ee635263fc22b..27861b2c1ca926fea0a875972d82412445d2f2df 100644 GIT binary patch delta 3780 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 { it('export image node correctly', () => { const imageNode = body.elements[0].elements[1].elements[0]; - expect(imageNode.elements[0].attributes.distT).toBe('114935'); - expect(imageNode.elements[0].attributes.distB).toBe('114935'); - expect(imageNode.elements[0].attributes.distL).toBe('114300'); - expect(imageNode.elements[0].attributes.distR).toBe('114300'); - + expect(imageNode.elements[0].attributes.distT).toBe('0'); + expect(imageNode.elements[0].attributes.distB).toBe('0'); + expect(imageNode.elements[0].attributes.distL).toBe('0'); + expect(imageNode.elements[0].attributes.distR).toBe('0'); + expect(imageNode.elements[0].elements[0].attributes.cx).toBe(5734050); expect(imageNode.elements[0].elements[0].attributes.cy).toBe(8601075); + expect(imageNode.elements[0].elements[4].elements[0].elements[0].elements[1].elements[0].attributes['r:embed']).toBe('rId4'); }); + + it('exports anchor image node correctly', async() => { + + }); +}); + +describe('ImageNodeExporter anchor image', async () => { + window.URL.createObjectURL = vi.fn().mockImplementation((file) => { + return file.name; + }); + + const fileName = 'anchor_images.docx'; + const result = await getExportedResult(fileName); + const body = {}; + + beforeEach(() => { + Object.assign(body, result.elements?.find((el) => el.name === 'w:body')); + }); + + it('exports anchor image node correctly', async() => { + const imageNode = body.elements[1].elements[4].elements[0]; + const anchorNode = imageNode.elements[0]; + + expect(anchorNode.attributes).toHaveProperty('simplePos', '0'); + expect(anchorNode.elements[0].name).toBe('wp:simplePos'); + console.log(anchorNode.elements[5]); + expect(anchorNode.elements[1].attributes.relativeFrom).toBe('margin'); + expect(anchorNode.elements[1].elements[0].name).toBe('wp:align'); + expect(anchorNode.elements[1].elements[0].elements[0].text).toBe('left'); + + expect(anchorNode.elements[2].attributes.relativeFrom).toBe('margin'); + expect(anchorNode.elements[2].elements[0].name).toBe('wp:align'); + expect(anchorNode.elements[2].elements[0].elements[0].text).toBe('top'); + + expect(anchorNode.elements[5].name).toBe('wp:wrapSquare'); + expect(anchorNode.elements[5].attributes.wrapText).toBe('bothSides'); + }); }); diff --git a/packages/super-editor/src/tests/import/imageImporter.test.js b/packages/super-editor/src/tests/import/imageImporter.test.js index 939ad16a2d..151ec5c2ed 100644 --- a/packages/super-editor/src/tests/import/imageImporter.test.js +++ b/packages/super-editor/src/tests/import/imageImporter.test.js @@ -17,7 +17,7 @@ describe('ImageNodeImporter', () => { const paragraphNode = nodes[0]; const drawingNode = paragraphNode.content[0]; const { attrs } = drawingNode; - const { padding, marginOffset, size } = attrs; + const { padding, size } = attrs; expect(paragraphNode.type).toBe('paragraph'); expect(drawingNode.type).toBe('image'); @@ -27,13 +27,31 @@ describe('ImageNodeImporter', () => { expect(size).toHaveProperty('width', 602); expect(size).toHaveProperty('height', 903); - - expect(marginOffset).toHaveProperty('left', 12); - expect(marginOffset).toHaveProperty('top', 12); - expect(padding).toHaveProperty('left', 12); - expect(padding).toHaveProperty('top', 12); - expect(padding).toHaveProperty('bottom', 12); - expect(padding).toHaveProperty('right', 12); + expect(padding).toHaveProperty('left', 0); + expect(padding).toHaveProperty('top', 0); + expect(padding).toHaveProperty('bottom', 0); + expect(padding).toHaveProperty('right', 0); + }); + + it('imports anchor image node correctly', async() => { + const dataName = 'anchor_images.docx'; + const docx = await getTestDataByFileName(dataName); + const documentXml = docx['word/document.xml']; + + const doc = documentXml.elements[0]; + const body = doc.elements[0]; + const content = body.elements; + const { nodes } = handleParagraphNode({ nodes: [content[1]], docx, nodeListHandler: defaultNodeListHandler() }); + + const paragraphNode = nodes[0]; + const drawingNode = paragraphNode.content[3]; + const { attrs } = drawingNode; + const { anchorData } = attrs; + + expect(anchorData).toHaveProperty('hRelativeFrom', 'margin'); + expect(anchorData).toHaveProperty('vRelativeFrom', 'margin'); + expect(anchorData).toHaveProperty('alignH', 'left'); + expect(anchorData).toHaveProperty('alignV', 'top'); }); });