|
|
@@ -33,7 +33,7 @@ import type {
|
|
|
|
|
|
const convertFontSizePtToPx = (html: string, ratio: number, autoFit: any) => {
|
|
|
if (autoFit?.fontScale && autoFit?.type == 'text') {
|
|
|
- ratio = ratio * autoFit.fontScale / 100
|
|
|
+ ratio = ratio * autoFit.fontScale / 100
|
|
|
}
|
|
|
// return html;
|
|
|
return html.replace(/\s*([\d.]+)pt/g, (match, p1) => {
|
|
|
@@ -742,16 +742,16 @@ export default () => {
|
|
|
options?: { tolerance?: number }
|
|
|
): Promise<File> => {
|
|
|
const tolerance = options?.tolerance ?? 15
|
|
|
-
|
|
|
+
|
|
|
// 1. 统一输入为 Blob 和 MIME
|
|
|
const { blob, mime } = await getBlobAndMime(data)
|
|
|
const format = getFormat(mime, filename)
|
|
|
-
|
|
|
+
|
|
|
// 2. 浏览器原生支持的格式直接返回
|
|
|
if (format === 'browser') {
|
|
|
return new File([blob], filename, { type: mime })
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 3. 需要转换成 PNG 的格式
|
|
|
let pngBlob: Blob
|
|
|
if (format === 'tiff') {
|
|
|
@@ -767,7 +767,7 @@ export default () => {
|
|
|
// format === 'png' 的情况
|
|
|
pngBlob = blob
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// --- 新增:检测 PNG 是否已经包含透明背景 ---
|
|
|
let alreadyTransparent = false
|
|
|
// 无论原始格式是 PNG 还是转换后得到的 PNG,都进行检测
|
|
|
@@ -789,7 +789,7 @@ export default () => {
|
|
|
finally {
|
|
|
URL.revokeObjectURL(checkUrl)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
let transparentPngBlob: Blob
|
|
|
transparentPngBlob = pngBlob
|
|
|
/*
|
|
|
@@ -806,7 +806,7 @@ export default () => {
|
|
|
const finalFilename = format === 'png' ? filename : filename.replace(/\.[^.]*$/, '') + '.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
|
|
|
async function convertMetafileToPng(
|
|
|
arrayBuffer: ArrayBuffer,
|
|
|
- RendererClass: any // new (data: ArrayBuffer) => { render(settings: any): SVGElement }
|
|
|
+ RendererClass: any
|
|
|
): Promise<Blob> {
|
|
|
// 1. 创建 Renderer 实例
|
|
|
const renderer = new RendererClass(arrayBuffer)
|
|
|
|
|
|
- // 2. 先尝试获取图片的真实尺寸(通过临时渲染并解析 SVG 的 viewBox)
|
|
|
- let width = 800, height = 600 // 默认值
|
|
|
+ // 2. 获取真实尺寸(保持原逻辑)
|
|
|
+ let width = 800, height = 600
|
|
|
try {
|
|
|
- // 使用一个较大的临时尺寸进行第一次渲染,以获取 SVG 的 viewBox
|
|
|
const tempSettings = {
|
|
|
width: '100%',
|
|
|
height: '100%',
|
|
|
xExt: 1000,
|
|
|
yExt: 1000,
|
|
|
- mapMode: 8, // 保持宽高比
|
|
|
+ mapMode: 8,
|
|
|
}
|
|
|
const tempSvg = renderer.render(tempSettings)
|
|
|
const viewBox = tempSvg.getAttribute('viewBox')
|
|
|
@@ -878,9 +877,7 @@ export default () => {
|
|
|
width = parseFloat(parts[2])
|
|
|
height = parseFloat(parts[3])
|
|
|
}
|
|
|
- }
|
|
|
- else {
|
|
|
- // 尝试从 width/height 属性获取
|
|
|
+ } else {
|
|
|
const svgWidth = tempSvg.getAttribute('width')
|
|
|
const svgHeight = tempSvg.getAttribute('height')
|
|
|
if (svgWidth && svgHeight) {
|
|
|
@@ -888,28 +885,46 @@ export default () => {
|
|
|
height = parseFloat(svgHeight)
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- catch (e) {
|
|
|
+ } catch (e) {
|
|
|
console.warn('Failed to get dimensions from SVG, using default', e)
|
|
|
}
|
|
|
|
|
|
- // 3. 使用实际尺寸重新渲染
|
|
|
+ // 3. 使用实际尺寸渲染最终 SVG
|
|
|
const settings = {
|
|
|
width: width + 'px',
|
|
|
height: height + 'px',
|
|
|
xExt: width,
|
|
|
yExt: height,
|
|
|
- mapMode: 8, // 保持宽高比
|
|
|
+ mapMode: 8,
|
|
|
}
|
|
|
const svg = renderer.render(settings)
|
|
|
|
|
|
- // 4. 将 SVG 转为 data URL 并用 Image 加载
|
|
|
+ // 4. 序列化并修补 SVG 字符串
|
|
|
const serializer = new XMLSerializer()
|
|
|
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 url = URL.createObjectURL(blob)
|
|
|
try {
|
|
|
@@ -919,17 +934,20 @@ export default () => {
|
|
|
image.onerror = reject
|
|
|
image.src = url
|
|
|
})
|
|
|
- // 5. 绘制到 canvas
|
|
|
+
|
|
|
const canvas = document.createElement('canvas')
|
|
|
canvas.width = img.width
|
|
|
canvas.height = img.height
|
|
|
const ctx = canvas.getContext('2d')!
|
|
|
ctx.drawImage(img, 0, 0)
|
|
|
+
|
|
|
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)
|
|
|
}
|
|
|
}
|