多图转PDF扩展了解一下?#前端#扩展#jspdf#vue#pdf

210 阅读1分钟

起因

前阵子我的亲人经常发我好几张图让我帮他转成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
  }
}

成品

  • 请选择图片 image.png

  • 选择后预览 image.png

怎么样? 它还支持拖拽排序

现它这个扩展已上架到Edge扩展商店和chrome扩展商店

另外给没上架过扩展的同学提示一下:

edge扩展商店上架扩展是免费的

chrome扩展商店上架需要交纳$5.00 费用