Ver Fonte

Handle pasted HTML from Office even if there's an image file on the clipboard (#14852)

GitOrigin-RevId: f0413391ec421c845b435e1392140d265a528ee7
Alf Eaton há 2 anos atrás
pai
commit
a2bb8a3ecd

+ 15 - 7
services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts

@@ -36,17 +36,18 @@ export const pasteHtml = [
           return false
         }
 
-        // allow pasting an image to create a figure, if the HTML doesn't contain a table
-        // (because desktop Excel puts both an image and the HTML table on the clipboard)
-        if (clipboardData.files.length > 0 && !html.includes('<table')) {
-          return false
-        }
-
         // convert the HTML to LaTeX
         try {
           const parser = new DOMParser()
           const { documentElement } = parser.parseFromString(html, 'text/html')
 
+          // fall back to creating a figure when there's an image on the clipoard,
+          // unless the HTML indicates that it came from an Office application
+          // (which also puts an image on the clipboard)
+          if (clipboardData.files.length > 0 && !hasProgId(documentElement)) {
+            return false
+          }
+
           // if the only content is in a code block, use the plain text version
           if (onlyCode(documentElement)) {
             return false
@@ -55,7 +56,7 @@ export const pasteHtml = [
           const latex = htmlToLaTeX(documentElement)
 
           // if there's no formatting, use the plain text version
-          if (latex === text) {
+          if (latex === text && clipboardData.files.length === 0) {
             return false
           }
 
@@ -112,6 +113,13 @@ const onlyCode = (documentElement: HTMLElement) => {
   )
 }
 
+const hasProgId = (documentElement: HTMLElement) => {
+  const meta = documentElement.querySelector<HTMLMetaElement>(
+    'meta[name="ProgId"]'
+  )
+  return meta && meta.content.trim().length > 0
+}
+
 const htmlToLaTeX = (documentElement: HTMLElement) => {
   // remove style elements
   removeUnwantedElements(documentElement, 'style')

+ 35 - 0
services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx

@@ -29,6 +29,7 @@ describe('<CodeMirrorEditor/> paste HTML in Visual mode', function () {
     window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
     window.metaAttributesCache.set('ol-splitTestVariants', {
       'paste-html': 'enabled',
+      'figure-modal': 'enabled',
     })
     cy.interceptEvents()
     cy.interceptSpelling()
@@ -659,4 +660,38 @@ describe('<CodeMirrorEditor/> paste HTML in Visual mode', function () {
 
     cy.get('.cm-line').should('have.length', 8)
   })
+
+  it('treats a pasted image as a figure even if there is HTML', function () {
+    mountEditor()
+
+    cy.fixture<Uint8Array>('images/gradient.png').then(image => {
+      const file = new File([image], 'gradient.png', { type: 'image/png' })
+      const html = `<meta charset="utf-8"><img src="https://example.com/gradient.png" alt="gradient">`
+
+      const clipboardData = new DataTransfer()
+      clipboardData.setData('text/html', html)
+      clipboardData.items.add(file)
+      cy.get('.cm-content').trigger('paste', { clipboardData })
+
+      // figure modal paste handler should appear
+      cy.findByText('Upload from computer').should('be.visible')
+    })
+  })
+
+  it('does not treat a pasted image as a figure if there is Office HTML', function () {
+    mountEditor()
+
+    cy.fixture<Uint8Array>('images/gradient.png').then(image => {
+      const file = new File([image], 'gradient.png', { type: 'image/png' })
+      const html = `<meta charset="utf-8"><meta name="ProgId" content="MS.Word"><img src="https://example.com/gradient.png" alt="gradient">`
+
+      const clipboardData = new DataTransfer()
+      clipboardData.setData('text/html', html)
+      clipboardData.items.add(file)
+      cy.get('.cm-content').trigger('paste', { clipboardData })
+
+      // paste options button should appear
+      cy.findByLabelText('Paste options').should('be.visible')
+    })
+  })
 })