HTML内容转图片或PDF下载(html2canvas)

HTML转Canvas

虽然OpenCV可以用来绘图 但是制作答题卡的时候还是建议使用HTML来实现,并用html2canvas转为图片。

http://html2canvas.hertzen.com/

https://www.bootcdn.cn/html2canvas/

添加引用

1
<script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>

或者

1
2
npm install --save html2canvas
import html2canvas from "html2canvas";

示例

1
2
3
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});

注意

部分样式该组件转换的图片和原样式不同。

如table转换的时候border并不会合并,所以计算坐标的时候要加上间隔的像素,如果是三行那么就要加2像素。

图片无法显示

如果图片没有允许跨域访问,虽然在页面上我们能看到,但是生成Canvas会提示跨域,所以图片必须允许跨域访问,才能正常生成Canvas。

1
2
3
4
5
6
7
8
9
10
11
12
13
function downQrClick() {
let dom = document.querySelector('.qr_page') as HTMLElement
if (dom) {
html2canvas(dom, { useCORS: true })
.then((canvas) => {
let href = canvas.toDataURL() // 获取canvas对应的base64编码
let a = document.createElement('a') // 创建a标签
a.download = '商品.png' // 设置图片名字
a.href = href
a.dispatchEvent(new MouseEvent('click'))
})
}
}

阿里云OSS配置

image-20240921012700036

Canvas合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
get_all_page: async function () {
let cols = this.cols;
let pages = document.querySelectorAll(".page");
var canvas_arr = [];
let temp_canvas_arr = [];
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
let canvas = await html2canvas(page);
temp_canvas_arr.push(canvas);
if (temp_canvas_arr.length === cols) {
var canvas_all = document.createElement("canvas");
let page_width = temp_canvas_arr[0].width;
let page_height = temp_canvas_arr[0].height;
canvas_all.width = page_width * cols;
canvas_all.height = page_height;
var context = canvas_all.getContext("2d");
for (let j = 0; j < temp_canvas_arr.length; j++) {
const citem = temp_canvas_arr[j];
context.drawImage(citem, j * page_width, 0, page_width, page_height);
}
canvas_arr.push(canvas_all);
temp_canvas_arr = [];
}
}
return canvas_arr;
},

Canvas下载为图片

1
2
3
4
5
6
7
html2canvas(document.querySelector(".page")).then(canvas => {
let href = canvas.toDataURL() // 获取canvas对应的base64编码
let a = document.createElement('a') // 创建a标签
a.download = "答题卡.png" // 设置图片名字
a.href = href
a.dispatchEvent(new MouseEvent('click'))
});

图片清晰度设置

html2canvas 提供了一个 scale 选项,通过调整这个比例因子,你可以增加导出图像的分辨率。

默认情况下,scalewindow.devicePixelRatio,通常等于 1。

你可以将其设置为一个更大的值来增加清晰度。

1
2
3
4
5
6
7
8
9
10
11
12
13
function downQrClick() {
let goodsCode = props.goodsDetail?.goodsCode || "";
let dom = document.querySelector('.qr_page_outer') as HTMLElement
if (dom) {
html2canvas(dom, { useCORS: true,scale: 2 }).then((canvas) => {
let href = canvas.toDataURL() // 获取canvas对应的base64编码
let a = document.createElement('a') // 创建a标签
a.download = `商品_${goodsCode}.png` // 设置图片名字
a.href = href
a.dispatchEvent(new MouseEvent('click'))
})
}
}

Canvas下载为PDF

https://artskydj.github.io/jsPDF/docs/index.html

图片生成PDF

添加引用

1
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js" integrity="sha384-NaWTHo/8YCBYJ59830LTz/P4aQZK1sS0SneOgAvhsIl3zBu8r9RevNg5lHCHAuQ/" crossorigin="anonymous"></script>

或者

1
2
npm install jspdf --save
import jsPDF from 'jspdf';

单页下载

示例代码:

1
2
3
4
5
6
7
8
9
html2canvas(document.querySelector(".page")).then(canvas => {
// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('portrait', 'mm', 'a4');
doc.addImage(canvas, 'PNG', 0, 0, 210, 297);
//添加第二页
doc.addPage('a4', 'portrait');
doc.addImage(canvas, 'PNG', 0, 0, 210, 297);
doc.save('a4.pdf');
});

多页下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
download_page: async function () {
let pages = document.querySelectorAll(".page");
// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('portrait', 'mm', 'a4');
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
let canvas = await html2canvas(page);
// 默认有一页 所以第一页不用添加
if (i !== 0) {
doc.addPage('a4', 'portrait');
}
doc.addImage(canvas, 'PNG', 0, 0, 210, 297);
}
doc.autoPrint({ variant: 'non-conform' });
doc.save('autoprint.pdf');
},

合并下载

两张A4合并为A3下载

合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
get_all_page: async function () {
let cols = this.cols;
let pages = document.querySelectorAll(".page");
var canvas_arr = [];
let temp_canvas_arr = [];
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
let canvas = await html2canvas(page);
temp_canvas_arr.push(canvas);
if (temp_canvas_arr.length === cols) {
var canvas_all = document.createElement("canvas");
let page_width = temp_canvas_arr[0].width;
let page_height = temp_canvas_arr[0].height;
canvas_all.width = page_width * cols;
canvas_all.height = page_height;
var context = canvas_all.getContext("2d");
for (let j = 0; j < temp_canvas_arr.length; j++) {
const citem = temp_canvas_arr[j];
context.drawImage(citem, j * page_width, 0, page_width, page_height);
}
canvas_arr.push(canvas_all);
temp_canvas_arr = [];
}
}
return canvas_arr;
},

下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
download_page: async function () {
// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('landscape', 'mm', 'a3');
let canvas_arr = await this.get_all_page();
for (let i = 0; i < canvas_arr.length; i++) {
const canvas = canvas_arr[i];
// 默认有一页 所以第一页不用添加
if (i !== 0) {
doc.addPage('a3', 'landscape');
}
doc.addImage(canvas, 'PNG', 0, 0, 420, 297);
}
doc.save('autoprint.pdf');
},

方法及参数

jsPDF()

Name Type Default Description
orientation string portrait 方向 “portrait” or “landscape” (or shortcuts “p” or “l”).
unit string mm 单位 “pt” (points), “mm”, “cm”, “m”, “in” or “px”.
format string/Array a4 首页的大小 可以使用:a0 - a10 b0 - b10 c0 - c10 默认为”a4”.
也可以使用具体的大小数组 如: [595.28, 841.89]

添加图片

注意添加图片前一定要先添加页面。

addImage(imageData, format, x, y, width, height, alias, compression, rotation)

Parameters:

Name Type Description
imageData string \ HTMLImageElement \ HTMLCanvasElement \ Uint8Array imageData as base64 encoded DataUrl or Image-HTMLElement or Canvas-HTMLElement
format string format of file if filetype-recognition fails, e.g. ‘JPEG’
x number x Coordinate (in units declared at inception of PDF document) against left edge of the page
y number y Coordinate (in units declared at inception of PDF document) against upper edge of the page
width number width of the image (in units declared at inception of PDF document)
height number height of the Image (in units declared at inception of PDF document)
alias string alias of the image (if used multiple times)
compression string compression of the generated JPEG, can have the values ‘NONE’, ‘FAST’, ‘MEDIUM’ and ‘SLOW’
rotation number rotation of the image in degrees (0-359)

下载后自动打印

下载后的文件打开时自动调用打印

1
2
3
4
5
6
7
8
9
10
html2canvas(document.querySelector(".page")).then(canvas => {
// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('portrait', 'mm', 'a4');
doc.addImage(canvas, 'PNG', 0, 0, 210, 297);
//添加第二页
doc.addPage('a4', 'portrait');
doc.addImage(canvas, 'PNG', 0, 0, 210, 297);
doc.autoPrint({ variant: 'non-conform' });
doc.save('autoprint.pdf');
});

注意这样并不会在下载后自动打印,只是下载的文件被打开时触发打印。

Canvas打印

打印单张

1
2
3
4
5
6
7
8
9
10
11
12
html2canvas(document.querySelector(".page")).then(canvas => {
var dataURL = canvas.toDataURL("image/png");
var printWindow = window.open();
var style = document.createElement('style');
style.innerHTML = "@media print {@page{margin:0;size:210mm 297mm;}}";
printWindow.document.head.appendChild(style);
printWindow.document.write('<img src="' + dataURL + '" width="100%" />');
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 0);
});

打印多张

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
print_page: async function () {
let pages = document.querySelectorAll(".page");
var printWindow = window.open();
var style = document.createElement('style');
style.innerHTML = "@media print {@page{margin:0;size:420mm 297mm;}}";
printWindow.document.head.appendChild(style);
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
let canvas = await html2canvas(page);
var dataURL = canvas.toDataURL("image/png");
printWindow.document.write('<img src="' + dataURL + '" width="100%" />');
}
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 0);
}

打印样式

网页上使用图片打印A3的时候要注意设置以下项,特别是纸张大小和边距,否则跟实际的效果不符合。

页面添加样式

1
2
3
4
5
@media print {
@page{
margin:0;size:420mm 297mm;
}
}

1
2
3
var style = document.createElement('style');
style.innerHTML = "@media print {@page{margin:0;size:420mm 297mm;}}";
printWindow.document.head.appendChild(style);

下划线问题

下划线不显示

我们可以使用u标签设置下划线

1
<u>&nbsp;&nbsp;</u>

但是这样在转成Canvas后就会不显示

是因为

html2canvas 在渲染的时候,会把只含空格的元素当作是空元素来处理。

所以我们可以插入不可见元素

1
<u>&nbsp;&#8203;&nbsp;&#8203;</u>

&#8203;是一个 HTML 实体编码,表示 Unicode 中的 零宽空格(Zero Width Space, ZWSP),编码为 U+200B。它是一种不可见的字符,主要用于控制文本的换行或连字行为,而不会在显示时占用实际空间。

下划线预览不一样

默认转成Canvas和HTML中的长度不一样。

设置字体和字体大小即可。

1
2
3
4
5
6
#content u {
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
min-height: 30px;
line-height: 30px;
}