乐于分享
好东西不私藏

零基础的普通人,利用AI手搓图片转PDF工具

零基础的普通人,利用AI手搓图片转PDF工具

    现在的ai真的是造福我们这些零基础的小白,不到30分钟就能用自然语言手搓出某些软件需要付费的工具。
    昨天因工作需要把图片转成PDF格式发给客户,使用某办公软件转,竟然这么简单的功能都要开会员。在网上找半天没找到合适的工具,突发奇想问deekseep能不能自己做一个工具,竟然真的能成。
    随后与dp展开多轮对话,最终30分钟完成。功能:图片转PDF格式文件。图片可自由旋转,排序,批量删除、预览大图、支持图片的格式JPG/PNG/WEBP等常见格式。 优点:简单轻巧、无需安装、电脑有浏览器就可以使用(HTML文件)。  缺点:简单功能单一。
代码如下:
<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <metaname="viewport"content="width=device-width, initial-scale=1.0">    <title>图片转PDF工具 - 高级版</title>    <scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>    <style>        * {            margin0;            padding0;            box-sizing: border-box;        }        body {            font-family'Segoe UI''Microsoft YaHei', sans-serif;            backgroundlinear-gradient(135deg#667eea 0%#764ba2 100%);            min-height100vh;            padding20px;        }        .container {            max-width900px;            margin0 auto;            background: white;            border-radius20px;            padding40px;            box-shadow0 20px 60px rgba(0,0,0,0.3);        }        h1 {            text-align: center;            color#333;            margin-bottom10px;            font-size28px;        }        .subtitle {            text-align: center;            color#666;            margin-bottom30px;            font-size14px;        }        /* 上传区域 */        .upload-area {            border3px dashed #667eea;            border-radius15px;            padding60px 20px;            text-align: center;            background#f8f9ff;            transition: all 0.3s;            cursor: pointer;        }        .upload-area:hover {            background#eef1ff;            border-color#764ba2;        }        .upload-area.active {            background#e8ecff;            border-color#764ba2;        }        .upload-icon {            font-size60px;            margin-bottom15px;        }        .upload-text {            color#555;            font-size18px;            margin-bottom10px;        }        .upload-hint {            color#999;            font-size13px;        }        #fileInput {            display: none;        }        /* 工具栏 */        .toolbar {            margin-bottom15px;            padding12px 15px;            background#f0f2ff;            border-radius10px;            display: none;            align-items: center;            justify-content: space-between;            flex-wrap: wrap;            gap10px;        }        .toolbar.show {            display: flex;        }        .toolbar-left {            display: flex;            align-items: center;            gap10px;            flex-wrap: wrap;        }        .checkbox-wrapper {            display: flex;            align-items: center;            gap6px;            cursor: pointer;            font-size14px;            color#555;            user-select: none;        }        .checkbox-wrapper input[type="checkbox"] {            width18px;            height18px;            cursor: pointer;        }        .batch-btn {            padding6px 14px;            border: none;            border-radius6px;            font-size13px;            cursor: pointer;            transition: all 0.3s;            display: flex;            align-items: center;            gap5px;        }        .batch-delete {            background#ff4757;            color: white;        }        .batch-delete:hover:not(:disabled) {            background#ff3344;        }        .batch-delete:disabled {            opacity0.5;            cursor: not-allowed;        }        .selected-count {            color#667eea;            font-weight600;            font-size14px;        }        /* 图片预览区 */        .preview-section {            margin-top30px;            display: none;        }        .preview-section.show {            display: block;            animation: fadeIn 0.5s;        }        @keyframes fadeIn {            from { opacity0transformtranslateY(20px); }            to { opacity1transformtranslateY(0); }        }        .section-title {            font-size18px;            color#333;            margin-bottom15px;            display: flex;            align-items: center;            gap8px;        }        .sort-hint {            color#667eea;            font-size13px;            font-weight: normal;            margin-left: auto;            background#f0f2ff;            padding5px 12px;            border-radius20px;        }        .image-list {            display: flex;            flex-direction: column;            gap10px;        }        .image-item {            background#f8f9ff;            border-radius12px;            padding12px;            display: flex;            align-items: center;            gap12px;            transition: all 0.3s;            border2px solid transparent;            cursor: grab;            position: relative;        }        .image-item:hover {            background#eef1ff;            transformtranslateX(5px);            box-shadow0 4px 12px rgba(1021262340.15);        }        .image-item.selected {            background#e8ecff;            border-color#667eea;        }        .image-item.dragging {            opacity0.5;            cursor: grabbing;            border2px dashed #667eea;        }        .image-item.drag-over {            border2px dashed #764ba2;            background#f0e6ff;            transformscale(1.02);        }        .item-checkbox {            width20px;            height20px;            cursor: pointer;            flex-shrink0;        }        .drag-handle {            color#999;            font-size20px;            cursor: grab;            padding5px;            user-select: none;            flex-shrink0;        }        .drag-handle:active {            cursor: grabbing;        }        .order-number {            width30px;            height30px;            backgroundlinear-gradient(135deg#667eea 0%#764ba2 100%);            color: white;            border-radius50%;            display: flex;            align-items: center;            justify-content: center;            font-weight: bold;            font-size14px;            flex-shrink0;        }        .image-thumb-wrapper {            position: relative;            flex-shrink0;            cursor: zoom-in;        }        .image-thumb {            width70px;            height70px;            object-fit: cover;            border-radius8px;            border2px solid #ddd;            transition: transform 0.3s;        }        .image-thumb-wrapper:hover .image-thumb {            transformscale(1.05);        }        .zoom-icon {            position: absolute;            bottom5px;            right5px;            backgroundrgba(0,0,0,0.6);            color: white;            width24px;            height24px;            border-radius50%;            display: flex;            align-items: center;            justify-content: center;            font-size12px;            opacity0;            transition: opacity 0.3s;        }        .image-thumb-wrapper:hover .zoom-icon {            opacity1;        }        .image-info {            flex1;            min-width0;        }        .image-name {            font-weight600;            color#333;            margin-bottom5px;            word-break: break-all;            font-size14px;        }        .image-size {            color#888;            font-size12px;        }        .image-actions {            display: flex;            gap6px;            flex-shrink0;            flex-wrap: wrap;        }        .btn-small {            padding6px 10px;            border: none;            border-radius6px;            cursor: pointer;            font-size12px;            transition: all 0.3s;            display: flex;            align-items: center;            justify-content: center;            min-width32px;            height32px;        }        .btn-up.btn-down {            background#e0e0e0;            color#666;        }        .btn-up:hover:not(:disabled), .btn-down:hover:not(:disabled) {            background#667eea;            color: white;        }        .btn-up:disabled.btn-down:disabled {            opacity0.3;            cursor: not-allowed;        }        .btn-rotate {            background#4CAF50;            color: white;        }        .btn-rotate:hover {            background#45a049;            transformscale(1.1);        }        .btn-delete {            background#ff4757;            color: white;        }        .btn-delete:hover {            background#ff3344;            transformscale(1.05);        }        /* 大图预览模态框 */        .modal-overlay {            position: fixed;            top0;            left0;            width100%;            height100%;            backgroundrgba(0,0,0,0.9);            display: none;            align-items: center;            justify-content: center;            z-index1000;            padding20px;        }        .modal-overlay.show {            display: flex;            animation: fadeIn 0.3s;        }        .modal-content {            position: relative;            max-width90%;            max-height90%;            display: flex;            flex-direction: column;            align-items: center;        }        .modal-image {            max-width100%;            max-height80vh;            object-fit: contain;            border-radius8px;            box-shadow0 10px 40px rgba(0,0,0,0.5);        }        .modal-info {            color: white;            margin-top15px;            text-align: center;            font-size14px;        }        .modal-close {            position: absolute;            top: -40px;            right0;            backgroundrgba(255,255,255,0.2);            color: white;            border: none;            width40px;            height40px;            border-radius50%;            font-size24px;            cursor: pointer;            display: flex;            align-items: center;            justify-content: center;            transition: all 0.3s;        }        .modal-close:hover {            backgroundrgba(255,255,255,0.4);            transformrotate(90deg);        }        .modal-nav {            position: absolute;            top50%;            transformtranslateY(-50%);            backgroundrgba(255,255,255,0.2);            color: white;            border: none;            width50px;            height50px;            border-radius50%;            font-size20px;            cursor: pointer;            display: flex;            align-items: center;            justify-content: center;            transition: all 0.3s;        }        .modal-nav:hover {            backgroundrgba(255,255,255,0.4);        }        .modal-prev {            left: -70px;        }        .modal-next {            right: -70px;        }        /* 设置区域 */        .settings {            margin-top25px;            padding20px;            background#f8f9ff;            border-radius12px;        }        .setting-item {            margin-bottom15px;        }        .setting-item:last-child {            margin-bottom0;        }        .setting-label {            display: block;            margin-bottom8px;            color#555;            font-size14px;            font-weight500;        }        selectinput[type="text"] {            width100%;            padding10px 15px;            border2px solid #ddd;            border-radius8px;            font-size14px;            transition: border-color 0.3s;        }        select:focusinput[type="text"]:focus {            outline: none;            border-color#667eea;        }        /* 按钮组 */        .btn-group {            margin-top30px;            display: flex;            gap15px;            flex-wrap: wrap;        }        .btn {            flex1;            min-width120px;            padding15px 30px;            border: none;            border-radius10px;            font-size16px;            font-weight600;            cursor: pointer;            transition: all 0.3s;            display: flex;            align-items: center;            justify-content: center;            gap8px;        }        .btn-primary {            backgroundlinear-gradient(135deg#667eea 0%#764ba2 100%);            color: white;        }        .btn-primary:hover:not(:disabled) {            transformtranslateY(-2px);            box-shadow0 10px 25px rgba(1021262340.4);        }        .btn-secondary {            background#f1f1f1;            color#666;        }        .btn-secondary:hover {            background#e1e1e1;        }        .btn:disabled {            opacity0.6;            cursor: not-allowed;        }        /* 进度提示 */        .progress {            margin-top20px;            padding15px;            background#e8f5e9;            border-radius10px;            color#2e7d32;            text-align: center;            display: none;        }        .progress.show {            display: block;            animation: pulse 1.5s infinite;        }        @keyframes pulse {            0%100% { opacity1; }            50% { opacity0.7; }        }        /* 页脚 */        .footer {            text-align: center;            margin-top30px;            padding-top20px;            border-top1px solid #eee;        }        .footer-hint {            color#999;            font-size12px;            margin-bottom8px;        }        .signature {            display: inline-flex;            align-items: center;            gap8px;            backgroundlinear-gradient(135deg#667eea 0%#764ba2 100%);            color: white;            padding8px 20px;            border-radius20px;            font-size14px;            font-weight600;            box-shadow0 4px 15px rgba(1021262340.3);        }        .signature::before {            content"🍃";        }        /* 响应式 */        @media (max-width768px) {            .container {                padding20px;            }            .image-item {                flex-wrap: wrap;            }            .image-actions {                width100%;                justify-content: flex-end;                margin-top10px;            }            .modal-nav {                width40px;                height40px;                font-size16px;            }            .modal-prev {                left10px;            }            .modal-next {                right10px;            }        }    </style></head><body>    <divclass="container">        <h1>🖼️ 图片转PDF工具</h1>        <pclass="subtitle">拖拽排序、旋转、批量删除、预览大图</p>        <!-- 上传区域 -->        <divclass="upload-area"onclick="document.getElementById('fileInput').click()">            <divclass="upload-icon">📁</div>            <divclass="upload-text">点击或拖拽图片到此处</div>            <divclass="upload-hint">支持 JPG、PNG、GIF、WEBP 格式,可批量上传</div>        </div>        <inputtype="file"id="fileInput"accept="image/*"multipleonchange="handleFiles(this.files)">        <!-- 图片预览 -->        <divclass="preview-section"id="previewSection">            <!-- 工具栏 -->            <divclass="toolbar"id="toolbar">                <divclass="toolbar-left">                    <labelclass="checkbox-wrapper">                        <inputtype="checkbox"id="selectAll"onchange="toggleSelectAll()">                        <span>全选</span>                    </label>                    <spanclass="selected-count"id="selectedCount"></span>                </div>                <buttonclass="batch-btn batch-delete"id="batchDeleteBtn"onclick="batchDelete()"disabled>                    🗑️ 删除选中 (<spanid="deleteCount">0</span>)                </button>            </div>            <divclass="section-title">                📋 已选择的图片                 <spanid="imageCount">0</span>                <spanclass="sort-hint">👆 拖拽排序 | 点击缩略图预览 | 🔄 旋转图片</span>            </div>            <divclass="image-list"id="imageList"></div>            <!-- 设置 -->            <divclass="settings">                <divclass="setting-item">                    <labelclass="setting-label">📄 页面方向</label>                    <selectid="pageOrientation">                        <optionvalue="portrait">纵向(适合手机查看)</option>                        <optionvalue="landscape">横向(适合电脑查看)</option>                        <optionvalue="auto">自动适应图片</option>                    </select>                </div>                <divclass="setting-item">                    <labelclass="setting-label">💾 文件名</label>                    <inputtype="text"id="fileName"placeholder="输入文件名(默认:图片合集)"value="图片合集">                </div>            </div>            <!-- 按钮组 -->            <divclass="btn-group">                <buttonclass="btn btn-secondary"onclick="clearAll()">                    🗑️ 清空全部                </button>                <buttonclass="btn btn-primary"id="convertBtn"onclick="convertToPDF()">                    🚀 开始转换                </button>            </div>            <!-- 进度提示 -->            <divclass="progress"id="progress">                ⏳ 正在生成PDF,请稍候...            </div>        </div>        <!-- 页脚署名 -->        <divclass="footer">            <divclass="footer-hint">💡 提示:旋转后的图片会自动保存状态,生成的PDF将使用最终效果</div>            <divclass="signature">公众号:小叔自记</div>        </div>    </div>    <!-- 大图预览模态框 -->    <divclass="modal-overlay"id="imageModal"onclick="closeModal(event)">        <divclass="modal-content"onclick="event.stopPropagation()">            <buttonclass="modal-close"onclick="closeModal()">×</button>            <buttonclass="modal-nav modal-prev"onclick="navigateModal(-1)"></button>            <buttonclass="modal-nav modal-next"onclick="navigateModal(1)"></button>            <imgclass="modal-image"id="modalImage"src=""alt="">            <divclass="modal-info"id="modalInfo"></div>        </div>    </div>    <script>        // 存储选中的图片        let selectedImages = [];        let draggedIndex = null;        let currentModalIndex = 0;        // 拖拽上传        const uploadArea = document.querySelector('.upload-area');        uploadArea.addEventListener('dragover'(e) => {            e.preventDefault();            uploadArea.classList.add('active');        });        uploadArea.addEventListener('dragleave'() => {            uploadArea.classList.remove('active');        });        uploadArea.addEventListener('drop'(e) => {            e.preventDefault();            uploadArea.classList.remove('active');            handleFiles(e.dataTransfer.files);        });        // 处理文件        function handleFiles(files) {            const validFiles = Array.from(files).filter(file => file.type.startsWith('image/'));            if (validFiles.length === 0) {                alert('请选择图片文件!');                return;            }            validFiles.forEach(file => {                const reader = new FileReader();                reader.onload = (e) => {                    selectedImages.push({                        name: file.name,                        sizeformatFileSize(file.size),                        data: e.target.result,                        idDate.now() + Math.random(),                        rotation0,                        selectedfalse                    });                    updatePreview();                };                reader.readAsDataURL(file);            });        }        // 格式化文件大小        function formatFileSize(bytes) {            if (bytes === 0return '0 Bytes';            const k = 1024;            const sizes = ['Bytes''KB''MB''GB'];            const i = Math.floor(Math.log(bytes) / Math.log(k));            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];        }        // 更新预览        function updatePreview() {            const previewSection = document.getElementById('previewSection');            const toolbar = document.getElementById('toolbar');            const imageList = document.getElementById('imageList');            const imageCount = document.getElementById('imageCount');            if (selectedImages.length > 0) {                previewSection.classList.add('show');                toolbar.classList.add('show');                imageCount.textContent = selectedImages.length;                updateToolbar();                imageList.innerHTML = selectedImages.map((img, index) => `                    <div class="image-item ${img.selected ? 'selected' : ''}                         draggable="true"                          data-index="${index}"                         ondragstart="handleDragStart(event, ${index})"                         ondragend="handleDragEnd(event)"                         ondragover="handleDragOver(event, ${index})"                         ondrop="handleDrop(event, ${index})"                         ondragenter="handleDragEnter(event)"                         ondragleave="handleDragLeave(event)">                        <input type="checkbox"                                class="item-checkbox"                                ${img.selected ? 'checked' : ''}                                onchange="toggleItemSelect(${index})"                               onclick="event.stopPropagation()">                        <div class="drag-handle">☰</div>                        <div class="order-number">${index + 1}</div>                        <div class="image-thumb-wrapper" onclick="openModal(${index})">                            <img src="${img.data}                                 class="image-thumb"                                  alt="${img.name}"                                 style="transform: rotate(${img.rotation}deg);">                            <div class="zoom-icon">🔍</div>                        </div>                        <div class="image-info">                            <div class="image-name">${img.name}</div>                            <div class="image-size">${img.size} ${img.rotation > 0 ? '| 已旋转' + img.rotation + '°' : ''}</div>                        </div>                        <div class="image-actions">                            <button class="btn-small btn-up"                                     onclick="moveUp(${index})"                                     ${index === 0 ? 'disabled' : ''}                                    title="上移">↑</button>                            <button class="btn-small btn-down"                                     onclick="moveDown(${index})"                                     ${index === selectedImages.length - 1 ? 'disabled' : ''}                                    title="下移">↓</button>                            <button class="btn-small btn-rotate"                                     onclick="rotateImage(${index})"                                    title="顺时针旋转90°">🔄</button>                            <button class="btn-small btn-delete"                                     onclick="removeImage(${index})"                                    title="删除">🗑️</button>                        </div>                    </div>                `).join('');            } else {                previewSection.classList.remove('show');                toolbar.classList.remove('show');            }        }        // 更新工具栏状态        function updateToolbar() {            const selectedCount = selectedImages.filter(img => img.selected).length;            const selectAllCheckbox = document.getElementById('selectAll');            const selectedCountEl = document.getElementById('selectedCount');            const batchDeleteBtn = document.getElementById('batchDeleteBtn');            const deleteCount = document.getElementById('deleteCount');            selectAllCheckbox.checked = selectedCount === selectedImages.length && selectedCount > 0;            selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < selectedImages.length;            if (selectedCount > 0) {                selectedCountEl.textContent = `已选择 ${selectedCount} 张`;                batchDeleteBtn.disabled = false;                deleteCount.textContent = selectedCount;            } else {                selectedCountEl.textContent = '';                batchDeleteBtn.disabled = true;                deleteCount.textContent = 0;            }        }        // 全选/取消全选        function toggleSelectAll() {            const selectAll = document.getElementById('selectAll').checked;            selectedImages.forEach(img => img.selected = selectAll);            updatePreview();        }        // 单选        function toggleItemSelect(index) {            selectedImages[index].selected = !selectedImages[index].selected;            updatePreview();        }        // 批量删除        function batchDelete() {            const selectedCount = selectedImages.filter(img => img.selected).length;            if (selectedCount === 0return;            if (confirm(`确定要删除选中的 ${selectedCount} 张图片吗?`)) {                selectedImages = selectedImages.filter(img => !img.selected);                updatePreview();            }        }        // 旋转图片        function rotateImage(index) {            selectedImages[index].rotation = (selectedImages[index].rotation + 90) % 360;            updatePreview();        }        // 打开大图预览        function openModal(index) {            currentModalIndex = index;            updateModal();            document.getElementById('imageModal').classList.add('show');            document.body.style.overflow = 'hidden';        }        // 关闭模态框        function closeModal(event) {            if (!event || event.target.id === 'imageModal' || event.target.classList.contains('modal-close')) {                document.getElementById('imageModal').classList.remove('show');                document.body.style.overflow = '';            }        }        // 更新模态框内容        function updateModal() {            const img = selectedImages[currentModalIndex];            const modalImage = document.getElementById('modalImage');            const modalInfo = document.getElementById('modalInfo');            modalImage.src = img.data;            modalImage.style.transform = `rotate(${img.rotation}deg)`;            modalInfo.innerHTML = `                <strong>${img.name}</strong><br>                第 ${currentModalIndex + 1} / ${selectedImages.length} 张 | ${img.size}                ${img.rotation > 0 ? ` | 已旋转 ${img.rotation}°` : ''}            `;            document.querySelector('.modal-prev').style.display =                 currentModalIndex > 0 ? 'flex' : 'none';            document.querySelector('.modal-next').style.display =                 currentModalIndex < selectedImages.length - 1 ? 'flex' : 'none';        }        // 模态框导航        function navigateModal(direction) {            const newIndex = currentModalIndex + direction;            if (newIndex >= 0 && newIndex < selectedImages.length) {                currentModalIndex = newIndex;                updateModal();            }        }        // 键盘导航        document.addEventListener('keydown'(e) => {            if (!document.getElementById('imageModal').classList.contains('show')) return;            if (e.key === 'Escape'closeModal();            if (e.key === 'ArrowLeft'navigateModal(-1);            if (e.key === 'ArrowRight'navigateModal(1);        });        // 拖拽排序功能        function handleDragStart(event, index) {            draggedIndex = index;            event.target.classList.add('dragging');            event.dataTransfer.effectAllowed = 'move';        }        function handleDragEnd(event) {            event.target.classList.remove('dragging');            document.querySelectorAll('.image-item').forEach(item => {                item.classList.remove('drag-over');            });        }        function handleDragEnter(event) {            event.preventDefault();            const item = event.currentTarget;            if (!item.classList.contains('dragging')) {                item.classList.add('drag-over');            }        }        function handleDragLeave(event) {            const item = event.currentTarget;            if (!item.contains(event.relatedTarget)) {                item.classList.remove('drag-over');            }        }        function handleDragOver(event, index) {            event.preventDefault();            event.dataTransfer.dropEffect = 'move';        }        function handleDrop(event, dropIndex) {            event.preventDefault();            const item = event.currentTarget;            item.classList.remove('drag-over');            if (draggedIndex === null || draggedIndex === dropIndex) return;            const draggedItem = selectedImages[draggedIndex];            selectedImages.splice(draggedIndex, 1);            selectedImages.splice(dropIndex, 0, draggedItem);            draggedIndex = null;            updatePreview();        }        // 按钮移动功能        function moveUp(index) {            if (index <= 0return;            const temp = selectedImages[index];            selectedImages[index] = selectedImages[index - 1];            selectedImages[index - 1] = temp;            updatePreview();        }        function moveDown(index) {            if (index >= selectedImages.length - 1return;            const temp = selectedImages[index];            selectedImages[index] = selectedImages[index + 1];            selectedImages[index + 1] = temp;            updatePreview();        }        // 删除单张图片        function removeImage(index) {            selectedImages.splice(index, 1);            updatePreview();        }        // 清空全部        function clearAll() {            if (selectedImages.length === 0return;            if (confirm('确定要清空所有图片吗?')) {                selectedImages = [];                updatePreview();                document.getElementById('fileName').value = '图片合集';            }        }        // 转换为PDF        async function convertToPDF() {            if (selectedImages.length === 0) {                alert('请先选择图片!');                return;            }            const btn = document.getElementById('convertBtn');            const progress = document.getElementById('progress');            btn.disabled = true;            progress.classList.add('show');            try {                const { jsPDF } = window.jspdf;                const orientation = document.getElementById('pageOrientation').value;                const fileName = document.getElementById('fileName').value || '图片合集';                const pdf = new jsPDF({                    unit'mm',                    compresstrue                });                for (let i = 0; i < selectedImages.length; i++) {                    const img = selectedImages[i];                    const processedData = await processImageWithRotation(img);                    const imageObj = await loadImage(processedData);                    const imgWidth = imageObj.width;                    const imgHeight = imageObj.height;                    const ratio = imgWidth / imgHeight;                    let pageWidth, pageHeight;                    if (orientation === 'auto') {                        if (ratio > 1) {                            pageWidth = 297;                            pageHeight = 210;                            pdf.addPage([pageWidth, pageHeight], 'landscape');                        } else {                            pageWidth = 210;                            pageHeight = 297;                            pdf.addPage([pageWidth, pageHeight], 'portrait');                        }                        if (i === 0) pdf.deletePage(1);                    } else {                        pageWidth = pdf.internal.pageSize.getWidth();                        pageHeight = pdf.internal.pageSize.getHeight();                        if (i > 0) pdf.addPage();                    }                    const margin = 10;                    const maxWidth = pageWidth - 2 * margin;                    const maxHeight = pageHeight - 2 * margin;                    let pdfImgWidth, pdfImgHeight;                    if (ratio > maxWidth / maxHeight) {                        pdfImgWidth = maxWidth;                        pdfImgHeight = maxWidth / ratio;                    } else {                        pdfImgHeight = maxHeight;                        pdfImgWidth = maxHeight * ratio;                    }                    const x = (pageWidth - pdfImgWidth) / 2;                    const y = (pageHeight - pdfImgHeight) / 2;                    pdf.addImage(processedData, 'JPEG', x, y, pdfImgWidth, pdfImgHeight, undefined'FAST');                }                pdf.save(`${fileName}.pdf`);                progress.innerHTML = '✅ 转换成功!PDF已下载';                progress.style.background = '#e8f5e9';                progress.style.color = '#2e7d32';                setTimeout(() => {                    progress.classList.remove('show');                    progress.innerHTML = '⏳ 正在生成PDF,请稍候...';                    btn.disabled = false;                }, 3000);            } catch (error) {                console.error(error);                alert('转换失败:' + error.message);                progress.classList.remove('show');                btn.disabled = false;            }        }        // 处理带旋转的图片        async function processImageWithRotation(imgData) {            if (imgData.rotation === 0return imgData.data;            return new Promise((resolve) => {                const img = new Image();                img.onload = () => {                    const canvas = document.createElement('canvas');                    const ctx = canvas.getContext('2d');                    if (imgData.rotation === 90 || imgData.rotation === 270) {                        canvas.width = img.height;                        canvas.height = img.width;                    } else {                        canvas.width = img.width;                        canvas.height = img.height;                    }                    ctx.translate(canvas.width / 2, canvas.height / 2);                    ctx.rotate(imgData.rotation * Math.PI / 180);                    ctx.drawImage(img, -img.width / 2, -img.height / 2);                    resolve(canvas.toDataURL('image/jpeg'0.95));                };                img.src = imgData.data;            });        }        // 加载图片获取尺寸        function loadImage(src) {            return new Promise((resolve, reject) => {                const img = new Image();                img.onload = () => resolve(img);                img.onerror = reject;                img.src = src;            });        }    </script></body></html>
使用方法更是简单,新建文本-复制代码到文本-修改文件名后缀为HTML。效果如下: