jack 16 hours ago
parent
commit
09f11deb5a
1 changed files with 44 additions and 26 deletions
  1. 44 26
      src/hooks/useImport.ts

+ 44 - 26
src/hooks/useImport.ts

@@ -33,7 +33,7 @@ import type {
 
 
 const convertFontSizePtToPx = (html: string, ratio: number, autoFit: any) => {
 const convertFontSizePtToPx = (html: string, ratio: number, autoFit: any) => {
   if (autoFit?.fontScale && autoFit?.type == 'text') {
   if (autoFit?.fontScale && autoFit?.type == 'text') {
-    ratio = ratio * autoFit.fontScale / 100 
+    ratio = ratio * autoFit.fontScale / 100
   }
   }
   // return html;
   // return html;
   return html.replace(/\s*([\d.]+)pt/g, (match, p1) => {
   return html.replace(/\s*([\d.]+)pt/g, (match, p1) => {
@@ -742,16 +742,16 @@ export default () => {
     options?: { tolerance?: number }
     options?: { tolerance?: number }
   ): Promise<File> => {
   ): Promise<File> => {
     const tolerance = options?.tolerance ?? 15
     const tolerance = options?.tolerance ?? 15
-  
+
     // 1. 统一输入为 Blob 和 MIME
     // 1. 统一输入为 Blob 和 MIME
     const { blob, mime } = await getBlobAndMime(data)
     const { blob, mime } = await getBlobAndMime(data)
     const format = getFormat(mime, filename)
     const format = getFormat(mime, filename)
-  
+
     // 2. 浏览器原生支持的格式直接返回
     // 2. 浏览器原生支持的格式直接返回
     if (format === 'browser') {
     if (format === 'browser') {
       return new File([blob], filename, { type: mime })
       return new File([blob], filename, { type: mime })
     }
     }
-  
+
     // 3. 需要转换成 PNG 的格式
     // 3. 需要转换成 PNG 的格式
     let pngBlob: Blob
     let pngBlob: Blob
     if (format === 'tiff') {
     if (format === 'tiff') {
@@ -767,7 +767,7 @@ export default () => {
       // format === 'png' 的情况
       // format === 'png' 的情况
       pngBlob = blob
       pngBlob = blob
     }
     }
-  
+
     // --- 新增:检测 PNG 是否已经包含透明背景 ---
     // --- 新增:检测 PNG 是否已经包含透明背景 ---
     let alreadyTransparent = false
     let alreadyTransparent = false
     // 无论原始格式是 PNG 还是转换后得到的 PNG,都进行检测
     // 无论原始格式是 PNG 还是转换后得到的 PNG,都进行检测
@@ -789,7 +789,7 @@ export default () => {
     finally {
     finally {
       URL.revokeObjectURL(checkUrl)
       URL.revokeObjectURL(checkUrl)
     }
     }
-  
+
     let transparentPngBlob: Blob
     let transparentPngBlob: Blob
     transparentPngBlob = pngBlob
     transparentPngBlob = pngBlob
     /*
     /*
@@ -806,7 +806,7 @@ export default () => {
     const finalFilename = format === 'png' ? filename : filename.replace(/\.[^.]*$/, '') + '.png'
     const finalFilename = format === 'png' ? filename : filename.replace(/\.[^.]*$/, '') + '.png'
     return new File([transparentPngBlob], finalFilename, { type: 'image/png' })
     return new File([transparentPngBlob], finalFilename, { type: 'image/png' })
   }
   }
-  
+
 
 
   // ================== 辅助函数 ==================
   // ================== 辅助函数 ==================
 
 
@@ -854,21 +854,20 @@ export default () => {
   // 参考示例:https://github.com/wood/rtf.js/blob/master/demo/WMFJS.html
   // 参考示例:https://github.com/wood/rtf.js/blob/master/demo/WMFJS.html
   async function convertMetafileToPng(
   async function convertMetafileToPng(
     arrayBuffer: ArrayBuffer,
     arrayBuffer: ArrayBuffer,
-    RendererClass: any // new (data: ArrayBuffer) => { render(settings: any): SVGElement }
+    RendererClass: any
   ): Promise<Blob> {
   ): Promise<Blob> {
     // 1. 创建 Renderer 实例
     // 1. 创建 Renderer 实例
     const renderer = new RendererClass(arrayBuffer)
     const renderer = new RendererClass(arrayBuffer)
 
 
-    // 2. 先尝试获取图片的真实尺寸(通过临时渲染并解析 SVG 的 viewBox
-    let width = 800, height = 600 // 默认值
+    // 2. 获取真实尺寸(保持原逻辑
+    let width = 800, height = 600
     try {
     try {
-      // 使用一个较大的临时尺寸进行第一次渲染,以获取 SVG 的 viewBox
       const tempSettings = {
       const tempSettings = {
         width: '100%',
         width: '100%',
         height: '100%',
         height: '100%',
         xExt: 1000,
         xExt: 1000,
         yExt: 1000,
         yExt: 1000,
-        mapMode: 8, // 保持宽高比
+        mapMode: 8,
       }
       }
       const tempSvg = renderer.render(tempSettings)
       const tempSvg = renderer.render(tempSettings)
       const viewBox = tempSvg.getAttribute('viewBox')
       const viewBox = tempSvg.getAttribute('viewBox')
@@ -878,9 +877,7 @@ export default () => {
           width = parseFloat(parts[2])
           width = parseFloat(parts[2])
           height = parseFloat(parts[3])
           height = parseFloat(parts[3])
         }
         }
-      }
-      else {
-        // 尝试从 width/height 属性获取
+      } else {
         const svgWidth = tempSvg.getAttribute('width')
         const svgWidth = tempSvg.getAttribute('width')
         const svgHeight = tempSvg.getAttribute('height')
         const svgHeight = tempSvg.getAttribute('height')
         if (svgWidth && svgHeight) {
         if (svgWidth && svgHeight) {
@@ -888,28 +885,46 @@ export default () => {
           height = parseFloat(svgHeight)
           height = parseFloat(svgHeight)
         }
         }
       }
       }
-    }
-    catch (e) {
+    } catch (e) {
       console.warn('Failed to get dimensions from SVG, using default', e)
       console.warn('Failed to get dimensions from SVG, using default', e)
     }
     }
 
 
-    // 3. 使用实际尺寸重新渲染
+    // 3. 使用实际尺寸渲染最终 SVG
     const settings = {
     const settings = {
       width: width + 'px',
       width: width + 'px',
       height: height + 'px',
       height: height + 'px',
       xExt: width,
       xExt: width,
       yExt: height,
       yExt: height,
-      mapMode: 8, // 保持宽高比
+      mapMode: 8,
     }
     }
     const svg = renderer.render(settings)
     const svg = renderer.render(settings)
 
 
-    // 4. 将 SVG 转为 data URL 并用 Image 加载
+    // 4. 序列化并修补 SVG 字符串
     const serializer = new XMLSerializer()
     const serializer = new XMLSerializer()
     let svgString = serializer.serializeToString(svg)
     let svgString = serializer.serializeToString(svg)
+
+    // ✅ 关键修复:强制设置根元素宽高与 viewBox 一致
+    const parser = new DOMParser()
+    const svgDoc = parser.parseFromString(svgString, 'image/svg+xml')
+    const svgRoot = svgDoc.documentElement
+
     // 确保有命名空间
     // 确保有命名空间
-    if (!svgString.includes('xmlns="http://www.w3.org/2000/svg"')) {
-      svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"')
+    if (!svgRoot.hasAttribute('xmlns')) {
+      svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
+    }
+
+    // 强制设置宽高为确定的像素值(与 viewBox 的宽高一致)
+    svgRoot.setAttribute('width', width.toString())
+    svgRoot.setAttribute('height', height.toString())
+
+    // 若原本没有 viewBox,根据获取的宽高补上
+    if (!svgRoot.hasAttribute('viewBox')) {
+      svgRoot.setAttribute('viewBox', `0 0 ${width} ${height}`)
     }
     }
+
+    svgString = new XMLSerializer().serializeToString(svgRoot)
+
+    // 5. 后续 Image 加载与 canvas 绘制保持不变
     const blob = new Blob([svgString], { type: 'image/svg+xml' })
     const blob = new Blob([svgString], { type: 'image/svg+xml' })
     const url = URL.createObjectURL(blob)
     const url = URL.createObjectURL(blob)
     try {
     try {
@@ -919,17 +934,20 @@ export default () => {
         image.onerror = reject
         image.onerror = reject
         image.src = url
         image.src = url
       })
       })
-      // 5. 绘制到 canvas
+
       const canvas = document.createElement('canvas')
       const canvas = document.createElement('canvas')
       canvas.width = img.width
       canvas.width = img.width
       canvas.height = img.height
       canvas.height = img.height
       const ctx = canvas.getContext('2d')!
       const ctx = canvas.getContext('2d')!
       ctx.drawImage(img, 0, 0)
       ctx.drawImage(img, 0, 0)
+
       return new Promise((resolve, reject) => {
       return new Promise((resolve, reject) => {
-        canvas.toBlob(blob => (blob ? resolve(blob) : reject(new Error('Metafile to PNG failed'))), 'image/png')
+        canvas.toBlob(
+          (blob) => (blob ? resolve(blob) : reject(new Error('Metafile to PNG failed'))),
+          'image/png'
+        )
       })
       })
-    }
-    finally {
+    } finally {
       URL.revokeObjectURL(url)
       URL.revokeObjectURL(url)
     }
     }
   }
   }