起因
前阵子我的亲人经常发我好几张图让我帮他转成PDF,因为他要拿去打印,起初我是手动帮他用ps转的。
后来我转了几次之后嫌太麻烦,我就想能不能写个工具一键转换,刚开始时我用python写了个命令行的工具,后来觉得还不够方便,便认真写了个浏览器扩展程序(因为我想,我都有这个需求,想必还有其他人也有这样的需求)
开始
图片转pdf 主要依赖使用了 [jspdf](jspdf - npm)
核心代码如下:
<template>
<input
type="file"
accept="image/*"
multiple
@change="handleFileSelect"
class="file-input"
ref="fileInput"
>
<n-button
type="primary"
class="convert-btn"
:disabled="selectedFiles.length == 0"
@click="convertToPdf"
:loading="isLoading"
>
转换为PDF
</n-button>
</template>
<script setup lang="ts">
import { jsPDF } from 'jspdf';
import {NButton, useMessage} from 'naive-ui'
const message = useMessage()
const isLoading = ref(false)
const paperFormat = ref('a4'); //纸张类型
const horizontalMargin = ref(0) // 横向边距,默认0px
const verticalMargin = ref(0) // 纵向边距,默认0px
//选择的图片列表
const selectedFiles = ref<{url: string, file: File}[]>([])
const marginOptions = [
{ label: '40px', value: 40 },
{ label: '30px', value: 30 },
{ label: '20px', value: 20 },
{ label: '10px', value: 10 },
{ label: '0px', value: 0 },
]
const paperOptions = [
{ label: 'A5', value: 'a5' },
{ label: 'A3', value: 'a3' },
{ label: 'A4', value: 'a4' },
]
const handleFileSelect = (event) => {
const files = Array.from(event.target.files).map((file: File) => {
return {
url: URL.createObjectURL(file),
file
}
})
selectedFiles.value = [...selectedFiles.value, ...files]
event.target.value = '' // 清空input以支持选择相同文件
}
const getPdfName = () => {
// 生成文件名:年-月-日
const date = new Date()
const fileName = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}.pdf`
return fileName
}
//开始转换
const convertToPdf = async () => {
if (selectedFiles.value.length === 0) return
isLoading.value = true
progress.value = 0
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'px',
format: paperFormat.value
})
const totalCount = selectedFiles.value.length;
const nextFn = requestAnimationFrame || setTimeout
try {
for (let i = 0; i < selectedFiles.value.length; i++) {
const img = new Image()
img.src = selectedFiles.value[i].url
await new Promise((resolve) => {
img.onload = async () => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const scale = 2
const pdfWidth = pdf.internal.pageSize.getWidth()
const pdfHeight = pdf.internal.pageSize.getHeight()
// 计算可用空间(考虑边距)
const availableWidth = pdfWidth - (horizontalMargin.value * 2)
const availableHeight = pdfHeight - (verticalMargin.value * 2)
let imgWidth = img.width
let imgHeight = img.height
// 计算适合页面的尺寸,保持宽高比
const ratio = Math.min(
availableWidth / imgWidth,
availableHeight / imgHeight
)
imgWidth = imgWidth * ratio
imgHeight = imgHeight * ratio
canvas.width = imgWidth * scale
canvas.height = imgHeight * scale
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
const imgData = canvas.toDataURL('image/jpeg', 1.0)
if (i > 0) {
pdf.addPage()
}
// 居中显示图片(考虑边距)
const x = horizontalMargin.value + (availableWidth - imgWidth) / 2
const y = verticalMargin.value + (availableHeight - imgHeight) / 2
pdf.addImage(imgData, 'JPEG', x, y, imgWidth, imgHeight)
const completeCount = i + 1;
progress.value = completeCount / totalCount * 100
nextFn(() => resolve(1));
}
})
}
pdf.save(getPdfName())
message.success('转换成功')
} catch (error) {
console.error('PDF conversion failed:', error)
message.error('转换失败')
} finally {
isLoading.value = false
}
}
成品
-
请选择图片
-
选择后预览
怎么样? 它还支持拖拽排序
现它这个扩展已上架到Edge扩展商店和chrome扩展商店
另外给没上架过扩展的同学提示一下:
edge扩展商店上架扩展是免费的
chrome扩展商店上架需要交纳$5.00 费用