Skip to content

Commit

Permalink
Feat:重构pdf的导出逻辑,导出的pdf尺寸不再是固定的a4,而是思维导图的尺寸,同时取出分页导出的配置
Browse files Browse the repository at this point in the history
  • Loading branch information
wanglin2 committed Dec 25, 2023
1 parent 5bea260 commit 29c5075
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 169 deletions.
6 changes: 0 additions & 6 deletions simple-mind-map/src/constants/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,6 @@ export const ERROR_TYPES = {
EXPORT_LOAD_IMAGE_ERROR: 'export_load_image_error'
}

// a4纸的宽高
export const a4Size = {
width: 592.28,
height: 841.89
}

// css
export const cssContent = `
/* 鼠标hover和激活时渲染的矩形 */
Expand Down
76 changes: 19 additions & 57 deletions simple-mind-map/src/plugins/Export.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { SVG } from '@svgdotjs/svg.js'
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
import { transformToMarkdown } from '../parse/toMarkdown'
import { a4Size, ERROR_TYPES } from '../constants/constant'
import { ERROR_TYPES } from '../constants/constant'

// 导出插件
class Export {
Expand Down Expand Up @@ -92,57 +92,28 @@ class Export {
}

// svg转png
svgToPng(
svgSrc,
transparent,
checkRotate = () => {
return false
},
compress
) {
svgToPng(svgSrc, transparent, ignoreDpr = false) {
return new Promise((resolve, reject) => {
const img = new Image()
// 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
img.setAttribute('crossOrigin', 'anonymous')
img.onload = async () => {
try {
const canvas = document.createElement('canvas')
const dpr = Math.max(
window.devicePixelRatio,
this.mindMap.opt.minExportImgCanvasScale
)
const dpr = ignoreDpr
? 1
: Math.max(
window.devicePixelRatio,
this.mindMap.opt.minExportImgCanvasScale
)
let imgWidth = img.width
let imgHeight = img.height
// 压缩图片
if (compress) {
const compressedSize = resizeImgSize(
imgWidth,
imgHeight,
compress.width,
compress.height
)
imgWidth = compressedSize[0]
imgHeight = compressedSize[1]
}
// 如果宽比高长,那么旋转90度
const needRotate = checkRotate(imgWidth, imgHeight)
if (needRotate) {
canvas.width = imgHeight * dpr
canvas.height = imgWidth * dpr
canvas.style.width = imgHeight + 'px'
canvas.style.height = imgWidth + 'px'
} else {
canvas.width = imgWidth * dpr
canvas.height = imgHeight * dpr
canvas.style.width = imgWidth + 'px'
canvas.style.height = imgHeight + 'px'
}
canvas.width = imgWidth * dpr
canvas.height = imgHeight * dpr
canvas.style.width = imgWidth + 'px'
canvas.style.height = imgHeight + 'px'
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)
if (needRotate) {
ctx.rotate(0.5 * Math.PI)
ctx.translate(0, -imgHeight)
}
// 绘制背景
if (!transparent) {
await this.drawBackgroundToCanvas(ctx, imgWidth, imgHeight)
Expand Down Expand Up @@ -232,31 +203,22 @@ class Export {
* 方法1.把svg的图片都转化成data:url格式,再转换
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
*/
async png(name, transparent = false, checkRotate, compress) {
async png(name, transparent = false) {
const { str } = await this.getSvgData()
const svgUrl = await this.fixSvgStrAndToBlob(str)
// 绘制到canvas上
const res = await this.svgToPng(svgUrl, transparent, checkRotate, compress)
const res = await this.svgToPng(svgUrl, transparent)
return res
}

// 导出为pdf
async pdf(name, useMultiPageExport, maxImageWidth) {
async pdf(name, transparent = false) {
if (!this.mindMap.doExportPDF) {
throw new Error('请注册ExportPDF插件')
}
const img = await this.png(
'',
false,
(width, height) => {
if (width <= a4Size.width && height && a4Size.height) return false
return width / height > 1
},
{
width: maxImageWidth || a4Size.width * 2
}
)
await this.mindMap.doExportPDF.pdf(name, img, useMultiPageExport)
const { str } = await this.getSvgData()
const svgUrl = await this.fixSvgStrAndToBlob(str)
const img = await this.svgToPng(svgUrl, transparent, true)
await this.mindMap.doExportPDF.pdf(name, img)
}

// 导出为xmind
Expand Down
107 changes: 11 additions & 96 deletions simple-mind-map/src/plugins/ExportPDF.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import JsPDF from '../utils/jspdf'
import { a4Size } from '../constants/constant'

// 导出PDF插件,需要通过Export插件使用
class ExportPDF {
Expand All @@ -9,104 +8,20 @@ class ExportPDF {
}

// 导出为pdf
async pdf(name, img, useMultiPageExport = false) {
if (useMultiPageExport) {
await this.multiPageExport(name, img)
} else {
await this.onePageExport(name, img)
}
}

// 单页导出
onePageExport(name, img) {
return new Promise((resolve, reject) => {
let pdf = new JsPDF({
unit: 'pt',
format: 'a4',
compress: true
})
let a4Ratio = a4Size.width / a4Size.height
let image = new Image()
image.onload = () => {
let imageWidth = image.width
let imageHeight = image.height
let imageRatio = imageWidth / imageHeight
let w, h
if (imageWidth <= a4Size.width && imageHeight <= a4Size.height) {
// 使用图片原始宽高
w = imageWidth
h = imageHeight
} else if (a4Ratio > imageRatio) {
// 以a4Height为高度,缩放图片宽度
w = imageRatio * a4Size.height
h = a4Size.height
} else {
// 以a4Width为宽度,缩放图片高度
w = a4Size.width
h = a4Size.width / imageRatio
}
pdf.addImage(
img,
'PNG',
(a4Size.width - w) / 2,
(a4Size.height - h) / 2,
w,
h
)
pdf.save(name)
resolve()
}
image.onerror = e => {
reject(e)
}
image.src = img
})
}

// 多页导出
multiPageExport(name, img) {
async pdf(name, img) {
return new Promise((resolve, reject) => {
let image = new Image()
const image = new Image()
image.onload = () => {
let imageWidth = image.width
let imageHeight = image.height
// 一页pdf显示高度
let pageHeight = (imageWidth / a4Size.width) * a4Size.height
// 未生成pdf的高度
let leftHeight = imageHeight
// 偏移
let position = 0
// a4纸的尺寸[595.28,841.89],图片在pdf中图片的宽高
let imgWidth = a4Size.width
let imgHeight = (a4Size.width / imageWidth) * imageHeight
let pdf = new JsPDF({
unit: 'pt',
format: 'a4',
compress: true
const imageWidth = image.width
const imageHeight = image.height
const pdf = new JsPDF({
unit: 'px',
format: [imageWidth, imageHeight],
compress: true,
hotfixes: ['px_scaling'],
orientation: imageWidth > imageHeight ? 'landscape' : 'portrait'
})
// 有两个高度需要区分,一个是图片的实际高度,和生成pdf的页面高度(841.89)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(
img,
'PNG',
(a4Size.width - imgWidth) / 2,
(a4Size.height - imgHeight) / 2,
imgWidth,
imgHeight
)
} else {
// 分页
while (leftHeight > 0) {
pdf.addImage(img, 'PNG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= a4Size.height
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
pdf.addImage(img, 'PNG', 0, 0, imageWidth, imageHeight)
pdf.save(name)
resolve()
}
Expand Down
13 changes: 3 additions & 10 deletions web/src/pages/Edit/components/Export.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,11 @@
@keydown.native.stop
></el-input>
<el-checkbox
v-show="['png'].includes(exportType)"
v-show="['png', 'pdf'].includes(exportType)"
v-model="isTransparent"
style="margin-left: 12px"
>{{ $t('export.isTransparent') }}</el-checkbox
>
<el-checkbox
v-show="['pdf'].includes(exportType)"
v-model="useMultiPageExport"
style="margin-left: 12px"
>{{ $t('export.useMultiPageExport') }}</el-checkbox
>
</div>
<div class="downloadTypeList">
<div
Expand Down Expand Up @@ -107,8 +101,7 @@ export default {
loading: false,
loadingText: '',
paddingX: 10,
paddingY: 10,
useMultiPageExport: false
paddingY: 10
}
},
computed: {
Expand Down Expand Up @@ -183,7 +176,7 @@ export default {
this.isTransparent
)
} else if (this.exportType === 'pdf') {
this.$bus.$emit('export', this.exportType, true, this.fileName, this.useMultiPageExport)
this.$bus.$emit('export', this.exportType, true, this.fileName, this.isTransparent)
} else {
this.$bus.$emit('export', this.exportType, true, this.fileName)
}
Expand Down

0 comments on commit 29c5075

Please sign in to comment.