Web2PDF 網頁轉檔PDF

將網頁轉檔PDF,支援閱讀模式、編輯和自定義樣式。

// ==UserScript==
// @name         Web2PDF 网页转PDF
// @name:zh-CN   Web2PDF 网页转PDF
// @name:zh-TW   Web2PDF 網頁轉檔PDF
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Convert web pages to PDF with support for reading mode, editing, and custom styles.将网页转换为PDF,支持阅读模式、编辑和自定义样式。
// @description:zh-CN  将网页转换为PDF,支持阅读模式、编辑和自定义样式。
// @description:zh-TW  將網頁轉檔PDF,支援閱讀模式、編輯和自定義樣式。
// @author       martjay
// @match        *://*/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZmlsbD0iIzRDQUY1MCIgZD0iTTIwIDJIOGMtMS4xIDAtMiAuOS0yIDJ2MTJjMCAxLjEuOSAyIDIgMmgxMmMxLjEgMCAyLS45IDItMlY0YzAtMS4xLS45LTItMi0yem0tOC41IDcuNWMwIC44My0uNjcgMS41LTEuNSAxLjVIOXYySDcuNVY3SDEwYy44MyAwIDEuNS42NyAxLjUgMS41djF6bTUgMmMwIC44My0uNjcgMS41LTEuNSAxLjVoLTIuNVY3SDE1Yy44MyAwIDEuNS42NyAxLjUgMS41djN6bTQtM0gxOXYxaDEuNVYxMUgxOHYyaC0xLjVWN2gzdjEuNXpNOSA5LjVoMXYtMUg5djF6TTQgNkgydjE0YzAgMS4xLjkgMiAyIDJoMTR2LTJINFY2em0xMCA1LjVoMXYtM2gtMXYzeiIvPjwvc3ZnPg==
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      GPL-3.0 License
// @require      https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// ==/UserScript==

(function() {
    'use strict';

    // i18n 配置
    const i18n = {
        zh: {
            readMode: '阅读模式',
            printPDF: '打印PDF',
            editMode: '编辑模式',
            switchLang: '中/En',
            loading: '正在加载页面内容和图片,请稍候...',
            loadingImages: '正在加载图片',
            noImages: '没有找到需要加载的图片',
            loadComplete: '加载完成,现在可以打印PDF了',
            loadFailed: '加载失败,请重试',
            deleteBlock: '删除此块',
            confirmDelete: '确定要删除这个内容块吗?',
            enterLink: '请输入链接地址:',
            bold: '加粗',
            italic: '斜体',
            underline: '下划线',
            heading1: '标题1',
            heading2: '标题2',
            paragraph: '段落',
            alignLeft: '左对齐',
            alignCenter: '居中',
            alignRight: '右对齐',
            bulletList: '无序列表',
            numberList: '有序列表',
            addLink: '添加链接',
            undo: '撤销',
            redo: '重做',
            pdfError: '生成PDF时出错,请重试',
            close: '关闭 (ESC)'
        },
        en: {
            readMode: 'Reading Mode',
            printPDF: 'Print PDF',
            editMode: 'Edit Mode',
            switchLang: '中/En',
            loading: 'Loading content and images, please wait...',
            loadingImages: 'Loading images',
            noImages: 'No images found to load',
            loadComplete: 'Loading complete, you can print PDF now',
            loadFailed: 'Loading failed, please try again',
            deleteBlock: 'Delete Block',
            confirmDelete: 'Are you sure you want to delete this block?',
            enterLink: 'Please enter the link URL:',
            bold: 'Bold',
            italic: 'Italic',
            underline: 'Underline',
            heading1: 'Heading 1',
            heading2: 'Heading 2',
            paragraph: 'Paragraph',
            alignLeft: 'Align Left',
            alignCenter: 'Center',
            alignRight: 'Align Right',
            bulletList: 'Bullet List',
            numberList: 'Number List',
            addLink: 'Add Link',
            undo: 'Undo',
            redo: 'Redo',
            pdfError: 'Error generating PDF, please try again',
            close: 'Close (ESC)'
        }
    };

    // 获取用户语言
    async function getUserLanguage() {
        const userLang = GM_getValue('userLanguage');
        if (userLang) {
            return userLang;
        }
        const lang = navigator.language.toLowerCase();
        const defaultLang = lang.startsWith('zh') ? 'zh' : 'en';
        return defaultLang;
    }

    // 设置用户语言
    async function setUserLanguage(lang) {
        GM_setValue('userLanguage', lang);
    }

    // 获取翻译文本
    async function t(key) {
        const lang = await getUserLanguage();
        return i18n[lang][key] || i18n.en[key];
    }

    // 添加样式
    GM_addStyle(`
        .web2pdf-floating-button {
            position: fixed;
            z-index: 2147483647;
            width: 48px;
            height: 48px;
            border-radius: 24px;
            background: white;
            border: none;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            cursor: move;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.2s;
            user-select: none;
            touch-action: none;
            color: #333;
            left: 20px;
            bottom: 20px;
        }

        .web2pdf-floating-button:hover {
            background: #f5f5f5;
        }

        .web2pdf-floating-button.dragging {
            opacity: 0.8;
            cursor: grabbing;
        }

        .web2pdf-floating-button svg {
            pointer-events: none;
            fill: currentColor;
        }

        .web2pdf-menu {
            position: fixed;
            background: white;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            display: none;
            z-index: 2147483646;
            min-width: 180px;
            max-width: 250px;
        }

        .web2pdf-menu.show {
            display: block;
        }

        .menu-item {
            padding: 10px 15px;
            display: flex;
            align-items: center;
            gap: 8px;
            cursor: pointer;
            color: #333;
            transition: background-color 0.2s;
            white-space: nowrap;
        }

        .menu-item:hover {
            background-color: #f5f5f5;
        }

        .menu-item svg {
            flex-shrink: 0;
            fill: currentColor;
        }

        .web2pdf-reader-mode {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: white;
            z-index: 2147483645;
            padding: 40px;
            overflow-y: auto;
            line-height: 1.6;
            font-family: Arial, sans-serif;
        }

        .web2pdf-reader-mode .content {
            max-width: 800px;
            margin: 0 auto;
        }

        .reader-close-button {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            border-radius: 20px;
            border: none;
            background: #f0f0f0;
            cursor: pointer;
            font-size: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 2147483646;
        }

        .reader-close-button:hover {
            background: #e0e0e0;
        }

        .web2pdf-context-menu {
            position: fixed;
            background: white;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            display: none;
            z-index: 2147483645;
            min-width: 150px;
        }

        .context-menu-item {
            padding: 8px 12px;
            display: flex;
            align-items: center;
            gap: 8px;
            cursor: pointer;
            transition: background-color 0.2s;
        }

        .context-menu-item:hover {
            background-color: #f5f5f5;
        }

        .web2pdf-reader-mode .web2pdf-floating-button {
            z-index: 2147483647;
        }

        .web2pdf-content.editing {
            outline: 2px solid #4CAF50;
            padding: 10px;
        }

        .editing-toolbar {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: white;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 5px;
            z-index: 2147483646;
            display: flex;
            gap: 5px;
        }

        .editing-toolbar button {
            width: 30px;
            height: 30px;
            border: none;
            background: #f0f0f0;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.2s;
        }

        .editing-toolbar button:hover {
            background: #e0e0e0;
        }

        [contenteditable=true]:focus {
            outline: none;
        }

        .element-controls {
            position: absolute;
            top: 0;
            right: 0;
            display: none;
            background: rgba(0, 0, 0, 0.1);
            border-radius: 4px;
        }

        [contenteditable=true] *:hover > .element-controls {
            display: block;
        }

        .element-controls button {
            padding: 2px 6px;
            background: none;
            border: none;
            color: #ff4444;
            cursor: pointer;
            font-size: 16px;
        }

        .element-controls button:hover {
            background: rgba(255, 0, 0, 0.1);
        }

        .image-wrapper {
            position: relative;
            display: inline-block;
        }

        .image-resizer {
            position: absolute;
            right: -5px;
            bottom: -5px;
            width: 10px;
            height: 10px;
            background: #4CAF50;
            border-radius: 50%;
            cursor: se-resize;
            display: none;
        }

        [contenteditable=true] .image-wrapper:hover .image-resizer {
            display: block;
        }

        #switchLang {
            border-top: 1px solid #eee;
            margin-top: 5px;
            padding-top: 10px;
        }

        #switchLang svg {
            transform: scale(0.9);
        }
    `);

    // 原有的功能函数
    // 将 content.js 的代码粘贴到这里
    // 创建浮动按钮和菜单
    function createFloatingButton() {
        // 检查是否已存在按钮
        let button = document.querySelector('.web2pdf-floating-button');
        if (button) {
            return button;
        }

        button = document.createElement('button');
        button.className = 'web2pdf-floating-button';
        button.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
                <path d="M0 0h24v24H0z" fill="none"/>
                <path fill="currentColor" d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 7.5c0 .83-.67 1.5-1.5 1.5H9v2H7.5V7H10c.83 0 1.5.67 1.5 1.5v1zm5 2c0 .83-.67 1.5-1.5 1.5h-2.5V7H15c.83 0 1.5.67 1.5 1.5v3zm4-3H19v1h1.5V11H19v2h-1.5V7h3v1.5zM9 9.5h1v-1H9v1zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm10 5.5h1v-3h-1v3z"/>
            </svg>
        `;

        // 设置初始位置
        const savedPosition = GM_getValue('buttonPosition', { left: '20px', bottom: '20px' });
        button.style.left = savedPosition.left;
        button.style.bottom = savedPosition.bottom;

        // 添加拖动功能
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        button.addEventListener('mousedown', function(e) {
            if (e.target.closest('.web2pdf-menu')) return;
            
            isDragging = true;
            button.style.transition = 'none';
            
            const rect = button.getBoundingClientRect();
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;
            
            button.classList.add('dragging');
        });

        document.addEventListener('mousemove', function(e) {
            if (!isDragging) return;

            e.preventDefault();
            
            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;
            
            const buttonRect = button.getBoundingClientRect();
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            
            currentX = Math.max(0, Math.min(currentX, viewportWidth - buttonRect.width));
            currentY = Math.max(0, Math.min(currentY, viewportHeight - buttonRect.height));
            
            button.style.left = currentX + 'px';
            button.style.top = currentY + 'px';
            button.style.bottom = 'auto';
        });

        document.addEventListener('mouseup', function() {
            if (!isDragging) return;
            
            isDragging = false;
            button.style.transition = 'background-color 0.2s';
            button.classList.remove('dragging');
            
            GM_setValue('buttonPosition', {
                left: button.style.left,
                bottom: button.style.bottom
            });
        });

        button.addEventListener('click', showMenu);
        
        // 确保按钮被添加到页面
        document.body.appendChild(button);
        console.log('Floating button created and added to page');

        return button;
    }

    // 创建菜单
    async function createMenu() {
        const menu = document.createElement('div');
        menu.className = 'web2pdf-menu';
        menu.innerHTML = `
            <div class="menu-item" id="readMode">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
                </svg>
                ${await t('readMode')}
            </div>
            <div class="menu-item" id="printPDF">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
                </svg>
                ${await t('printPDF')}
            </div>
            <div class="menu-item" id="switchLang">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/>
                </svg>
                ${await t('switchLang')}
            </div>
        `;
        document.body.appendChild(menu);

        // 添加事件监听
        document.getElementById('readMode').addEventListener('click', toggleReadMode);
        document.getElementById('printPDF').addEventListener('click', printToPDF);
        document.getElementById('switchLang').addEventListener('click', async function() {
            const currentLang = await getUserLanguage();
            const newLang = currentLang === 'zh' ? 'en' : 'zh';
            await setUserLanguage(newLang);
            
            // 重新创建菜单以更新语言
            const oldMenu = document.querySelector('.web2pdf-menu');
            if (oldMenu) oldMenu.remove();
            createMenu();
        });
    }

    // 显示/隐藏菜单
    function showMenu(event) {
        event.stopPropagation();
        const button = event.currentTarget;
        const menu = document.querySelector('.web2pdf-menu');
        const buttonRect = button.getBoundingClientRect();
        
        // 根据按钮位置调整菜单位置
        if (buttonRect.left < window.innerWidth / 2) {
            // 按钮在左半边,菜单显示在按钮右边
            menu.style.left = (buttonRect.right + 10) + 'px';
            menu.style.right = 'auto';
        } else {
            // 按钮在右半边,菜单显示在按钮左边
            menu.style.right = (window.innerWidth - buttonRect.left + 10) + 'px';
            menu.style.left = 'auto';
        }

        // 垂直位置调整
        const menuHeight = menu.offsetHeight || 100; // 预估高度
        if (buttonRect.top + menuHeight > window.innerHeight) {
            // 如果菜单会超出底部,就显示在按钮上方
            menu.style.bottom = (window.innerHeight - buttonRect.top + 10) + 'px';
            menu.style.top = 'auto';
        } else {
            // 否则显示在按钮下方
            menu.style.top = buttonRect.top + 'px';
            menu.style.bottom = 'auto';
        }

        menu.classList.toggle('show');
        
        // 点击其他地方关闭菜单
        document.addEventListener('click', function closeMenu(e) {
            if (!menu.contains(e.target) && !e.target.closest('.web2pdf-floating-button')) {
                menu.classList.remove('show');
                document.removeEventListener('click', closeMenu);
            }
        });
    }

    // 切换阅读模式
    async function toggleReadMode() {
        const existingReader = document.querySelector('.web2pdf-reader-mode');
        if (existingReader) {
            existingReader.remove();
            document.body.style.overflow = '';
            return;
        }

        // 添加加载提示
        const loadingTip = document.createElement('div');
        loadingTip.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 10px;
            z-index: 100000;
        `;
        loadingTip.textContent = await t('loading');
        document.body.appendChild(loadingTip);

        try {
            const content = extractMainContent();
            const tempContainer = document.createElement('div');
            tempContainer.innerHTML = content;

            // 预加载图片,传入 loadingTip 参数
            await preloadImages(tempContainer, loadingTip);

            // 创建阅读模式容器
            const readerMode = document.createElement('div');
            readerMode.className = 'web2pdf-reader-mode';
            readerMode.innerHTML = tempContainer.innerHTML;

            // 添加关闭按钮
            const closeButton = document.createElement('button');
            closeButton.className = 'web2pdf-close-button';
            closeButton.innerHTML = `
                <svg viewBox="0 0 24 24" width="24" height="24">
                    <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
                </svg>
                <span style="font-size: 12px;">ESC</span>
            `;
            closeButton.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                width: 40px;
                height: 40px;
                border-radius: 50%;
                border: none;
                background: #f0f0f0;
                cursor: pointer;
                font-size: 24px;
                display: flex;
                align-items: center;
                justify-content: center;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
                transition: background-color 0.3s, transform 0.3s;
                z-index: 2147483647;
            `;
            closeButton.addEventListener('mouseover', () => {
                closeButton.style.backgroundColor = '#e0e0e0';
                closeButton.style.transform = 'scale(1.1)';
            });
            closeButton.addEventListener('mouseout', () => {
                closeButton.style.backgroundColor = '#f0f0f0';
                closeButton.style.transform = 'scale(1)';
            });
            closeButton.addEventListener('click', () => {
                readerMode.remove();
                document.body.style.overflow = '';
                // 重新创建浮动按钮和菜单
                createFloatingButton();
                createMenu();
            });
            readerMode.appendChild(closeButton);

            // 添加 ESC 快捷键支持
            document.addEventListener('keydown', function escKeyHandler(e) {
                if (e.key === 'Escape') {
                    readerMode.remove();
                    document.body.style.overflow = '';
                    // 重新创建浮动按钮和菜单
                    createFloatingButton();
                    createMenu();
                    document.removeEventListener('keydown', escKeyHandler);
                }
            });

            document.body.appendChild(readerMode);
            document.body.style.overflow = 'hidden';
            document.querySelector('.web2pdf-menu')?.classList.remove('show');

            // 获取原始按钮的位置
            const originalButton = document.querySelector('.web2pdf-floating-button');
            const originalMenu = document.querySelector('.web2pdf-menu');
            const buttonPosition = originalButton ? {
                left: originalButton.style.left,
                top: originalButton.style.top,
                bottom: originalButton.style.bottom
            } : null;

            if (originalButton) originalButton.remove();
            if (originalMenu) originalMenu.remove();

            // 在阅读模式中创建新的浮动按钮,使用相同的位置
            const button = document.createElement('button');
            button.className = 'web2pdf-floating-button';
            button.innerHTML = `
                <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
                    <path d="M0 0h24v24H0z" fill="none"/>
                    <path fill="currentColor" d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 7.5c0 .83-.67 1.5-1.5 1.5H9v2H7.5V7H10c.83 0 1.5.67 1.5 1.5v1zm5 2c0 .83-.67 1.5-1.5 1.5h-2.5V7H15c.83 0 1.5.67 1.5 1.5v3zm4-3H19v1h1.5V11H19v2h-1.5V7h3v1.5zM9 9.5h1v-1H9v1zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm10 5.5h1v-3h-1v3z"/>
                </svg>
            `;

            // 应用保存的位置或使用默认位置
            const savedPosition = GM_getValue('buttonPosition', { left: '20px', top: '20px' });
            button.style.left = savedPosition.left;
            button.style.top = savedPosition.top;

            // 添加拖动功能
            let isDragging = false;
            let currentX;
            let currentY;
            let initialX;
            let initialY;

            button.addEventListener('mousedown', function(e) {
                if (e.target.closest('.web2pdf-menu')) return;
                
                isDragging = true;
                button.style.transition = 'none';
                
                const rect = button.getBoundingClientRect();
                initialX = e.clientX - rect.left;
                initialY = e.clientY - rect.top;
                
                button.classList.add('dragging');
            });

            document.addEventListener('mousemove', function(e) {
                if (!isDragging) return;

                e.preventDefault();
                
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                
                const buttonRect = button.getBoundingClientRect();
                const viewportWidth = window.innerWidth;
                const viewportHeight = window.innerHeight;
                
                currentX = Math.max(0, Math.min(currentX, viewportWidth - buttonRect.width));
                currentY = Math.max(0, Math.min(currentY, viewportHeight - buttonRect.height));
                
                button.style.left = currentX + 'px';
                button.style.top = currentY + 'px';
                button.style.bottom = 'auto';
            });

            document.addEventListener('mouseup', function() {
                if (!isDragging) return;
                
                isDragging = false;
                button.style.transition = 'background-color 0.2s';
                button.classList.remove('dragging');
                
                // 保存新位置到 storage,这样主页面的按钮也会使用这个位置
                GM_setValue('buttonPosition', {
                    left: button.style.left,
                    top: button.style.top
                });
            });

            button.addEventListener('click', showMenu);
            readerMode.appendChild(button);

            // 在阅读模式中创建新的菜单
            const menu = document.createElement('div');
            menu.className = 'web2pdf-menu';
            menu.innerHTML = `
                <div class="menu-item" id="toggleEdit">
                    <svg viewBox="0 0 24 24" width="20" height="20">
                        <path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
                    </svg>
                    ${await t('editMode')}
                </div>
                <div class="menu-item" id="printPDF">
                    <svg viewBox="0 0 24 24" width="20" height="20">
                        <path fill="currentColor" d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
                    </svg>
                    ${await t('printPDF')}
                </div>
                <div class="menu-item" id="switchLang">
                    <svg viewBox="0 0 24 24" width="20" height="20">
                        <path fill="currentColor" d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/>
                    </svg>
                    ${await t('switchLang')}
                </div>
            `;
            readerMode.appendChild(menu);

            // 为菜单项添加事件监听
            menu.querySelector('#toggleEdit').addEventListener('click', () => {
                toggleEditMode(readerMode);
                menu.classList.remove('show');
            });

            menu.querySelector('#printPDF').addEventListener('click', () => {
                printToPDF();
                menu.classList.remove('show');
            });

            menu.querySelector('#switchLang').addEventListener('click', async function() {
                const currentLang = await getUserLanguage();
                const newLang = currentLang === 'zh' ? 'en' : 'zh';
                await setUserLanguage(newLang);
                
                // 重新创建菜单以更新语言
                const oldMenu = readerMode.querySelector('.web2pdf-menu');
                if (oldMenu) oldMenu.remove();
                await createReaderModeMenu(readerMode);
            });

            // 显示成功提示
            loadingTip.style.background = 'rgba(76, 175, 80, 0.9)';
            loadingTip.textContent = await t('loadComplete');
            setTimeout(() => {
                loadingTip.remove();
                // 自动开启编辑模式
                toggleEditMode(readerMode);
            }, 2000);

        } catch (error) {
            console.error('Error loading content:', error);
            loadingTip.style.background = 'rgba(244, 67, 54, 0.9)';
            loadingTip.textContent = await t('loadFailed');
            setTimeout(() => {
                loadingTip.remove();
            }, 2000);
        }
    }

    // 添加一个新函数来创建阅读模式的菜单
    async function createReaderModeMenu(readerMode) {
        const menu = document.createElement('div');
        menu.className = 'web2pdf-menu';
        menu.innerHTML = `
            <div class="menu-item" id="toggleEdit">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
                </svg>
                ${await t('editMode')}
            </div>
            <div class="menu-item" id="printPDF">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
                </svg>
                ${await t('printPDF')}
            </div>
            <div class="menu-item" id="switchLang">
                <svg viewBox="0 0 24 24" width="20" height="20">
                    <path fill="currentColor" d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/>
                </svg>
                ${await t('switchLang')}
            </div>
        `;
        readerMode.appendChild(menu);

        // 添加事件监听
        menu.querySelector('#toggleEdit').addEventListener('click', () => {
            toggleEditMode(readerMode);
            menu.classList.remove('show');
        });

        menu.querySelector('#printPDF').addEventListener('click', () => {
            printToPDF();
            menu.classList.remove('show');
        });

        menu.querySelector('#switchLang').addEventListener('click', async function() {
            const currentLang = await getUserLanguage();
            const newLang = currentLang === 'zh' ? 'en' : 'zh';
            await setUserLanguage(newLang);
            
            // 重新创建菜单以更新语言
            const oldMenu = readerMode.querySelector('.web2pdf-menu');
            if (oldMenu) oldMenu.remove();
            await createReaderModeMenu(readerMode);
        });

        return menu;
    }

    // 添加预加载图片的函数
    async function preloadImages(container, loadingTip) {
        const images = container.getElementsByTagName('img');
        const totalImages = images.length;
        const imageLoadPromises = [];
        let loadedCount = 0;

        // 如果没有图片需要加载
        if (totalImages === 0) {
            if (loadingTip) {
                loadingTip.innerHTML = '没有找到需要加载的图片';
                await new Promise(resolve => setTimeout(resolve, 1000));
            }
            return;
        }

        // 创建进度条容器
        if (loadingTip) {
            loadingTip.innerHTML = `
                <div style="text-align: center;">
                    <div>正在加载图片 (0/${totalImages})</div>
                    <div class="progress-bar" style="
                        width: 200px;
                        height: 6px;
                        background: rgba(255, 255, 255, 0.2);
                        border-radius: 3px;
                        margin: 10px auto;
                        overflow: hidden;
                    ">
                        <div class="progress" style="
                            height: 100%;
                            width: 0%;
                            background: #4CAF50;
                            transition: width 0.3s ease;
                        "></div>
                    </div>
                    <div class="loading-details" style="
                        font-size: 12px;
                        color: rgba(255, 255, 255, 0.8);
                        margin-top: 5px;
                    ">准备加载...</div>
                </div>
            `;
        }

        // 处理所有图片
        Array.from(images).forEach((img, index) => {
            // 处理懒加载图片
            const originalSrc = img.getAttribute('data-src') || 
                               img.getAttribute('data-original') || 
                               img.getAttribute('data-lazy-src') || 
                               img.getAttribute('data-lazy-loaded') ||
                               img.getAttribute('data-url') ||
                               img.src;
            
            if (originalSrc) {
                // 创建一个加载Promise
                const loadPromise = new Promise((resolve) => {
                    const tempImg = new Image();
                    
                    tempImg.onload = () => {
                        loadedCount++;
                        if (loadingTip) {
                            // 更新加载进度
                            const progress = Math.round((loadedCount / totalImages) * 100);
                            const progressBar = loadingTip.querySelector('.progress');
                            const loadingDetails = loadingTip.querySelector('.loading-details');
                            
                            loadingTip.querySelector('div').textContent = 
                                `正在加载图片 (${loadedCount}/${totalImages})`;
                            
                            if (progressBar) {
                                progressBar.style.width = `${progress}%`;
                            }
                            
                            if (loadingDetails) {
                                loadingDetails.textContent = `正在加载: ${originalSrc.substring(0, 50)}...`;
                            }
                        }

                        img.src = originalSrc;  // 设置实际图片源
                        img.removeAttribute('data-src');
                        img.removeAttribute('data-original');
                        img.removeAttribute('data-lazy-src');
                        img.removeAttribute('data-lazy-loaded');
                        img.removeAttribute('data-url');
                        img.removeAttribute('loading');  // 移除懒加载属性
                        img.classList.remove('lazyload', 'lazy');  // 移除懒加载类
                        resolve();
                    };

                    tempImg.onerror = () => {
                        loadedCount++;
                        if (loadingTip) {
                            // 更新加载进度,即使加载失败
                            const progress = Math.round((loadedCount / totalImages) * 100);
                            const progressBar = loadingTip.querySelector('.progress');
                            const loadingDetails = loadingTip.querySelector('.loading-details');
                            
                            loadingTip.querySelector('div').textContent = 
                                `正在加载图片 (${loadedCount}/${totalImages})`;
                            
                            if (progressBar) {
                                progressBar.style.width = `${progress}%`;
                            }
                            
                            if (loadingDetails) {
                                loadingDetails.textContent = `加载失败: ${originalSrc.substring(0, 50)}...`;
                            }
                        }
                        console.warn('Failed to load image:', originalSrc);
                        resolve();
                    };

                    // 开始加载图片
                    tempImg.src = originalSrc;
                });

                imageLoadPromises.push(loadPromise);
            }
        });

        // 等待所有图片加载完成
        await Promise.all(imageLoadPromises);

        // 显示加载完成信息
        if (loadingTip) {
            loadingTip.innerHTML = `
                <div style="text-align: center;">
                    <div>图片加载完成 (${loadedCount}/${totalImages})</div>
                    <div class="progress-bar" style="
                        width: 200px;
                        height: 6px;
                        background: rgba(255, 255, 255, 0.2);
                        border-radius: 3px;
                        margin: 10px auto;
                        overflow: hidden;
                    ">
                        <div class="progress" style="
                            height: 100%;
                            width: 100%;
                            background: #4CAF50;
                        "></div>
                    </div>
                    <div class="loading-details" style="
                        font-size: 12px;
                        color: rgba(255, 255, 255, 0.8);
                        margin-top: 5px;
                    ">所有图片加载完成</div>
                </div>
            `;
            // 等待一会儿再消失,让用户看到完成状态
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
    }

    // 添加 A4 尺寸计算和内容分页函数
    function calculateA4Pages(container) {
        // A4 尺寸(以像素为单位,假设 96 DPI)
        // A4 纸张尺寸为 210mm x 297mm
        const A4_WIDTH_PX = 794;  // 210mm = 794px
        const A4_HEIGHT_PX = 1123; // 297mm = 1123px
        const MARGIN = 40; // 页边距
        const EFFECTIVE_HEIGHT = A4_HEIGHT_PX - (MARGIN * 2);
        
        // 创建一个临时容器来计算布局
        const tempDiv = document.createElement('div');
        tempDiv.style.cssText = `
            position: absolute;
            top: -9999px;
            left: -9999px;
            width: ${A4_WIDTH_PX - (MARGIN * 2)}px;
            visibility: hidden;
        `;
        document.body.appendChild(tempDiv);

        // 克隆内容到临时容器
        const contentClone = container.cloneNode(true);
        tempDiv.appendChild(contentClone);

        // 处理所有图片,使其等宽
        const images = tempDiv.getElementsByTagName('img');
        Array.from(images).forEach(img => {
            img.style.width = '100%';
            img.style.height = 'auto';
            // 确保图片容器也是等宽的
            if (img.parentElement.classList.contains('img-container')) {
                img.parentElement.style.width = '100%';
            }
        });

        // 存储分页后的内容
        const pages = [];
        let currentPage = document.createElement('div');
        let currentHeight = 0;
        
        // 遍历所有子元素
        Array.from(contentClone.children).forEach(element => {
            const elementHeight = element.offsetHeight;
            const isImage = element.tagName.toLowerCase() === 'img' || 
                           element.querySelector('img') !== null;

            // 如果当前元素是图片或包含图片,且会导致超出页面高度
            if (isImage && currentHeight + elementHeight > EFFECTIVE_HEIGHT) {
                // 将当前页添加到页面集合中
                if (currentPage.children.length > 0) {
                    pages.push(currentPage);
                }
                // 创建新页面,并将图片放在新页面的开始
                currentPage = document.createElement('div');
                currentPage.appendChild(element.cloneNode(true));
                currentHeight = elementHeight;
            }
            // 如果是普通元素,且会导致超出页面高度
            else if (currentHeight + elementHeight > EFFECTIVE_HEIGHT) {
                // 将当前页添加到页面集合中
                pages.push(currentPage);
                // 创建新页面
                currentPage = document.createElement('div');
                currentPage.appendChild(element.cloneNode(true));
                currentHeight = elementHeight;
            }
            // 如果不会超出页面高度
            else {
                currentPage.appendChild(element.cloneNode(true));
                currentHeight += elementHeight;
            }
        });

        // 添加最后一页
        if (currentPage.children.length > 0) {
            pages.push(currentPage);
        }

        // 清理临时元素
        document.body.removeChild(tempDiv);

        return pages;
    }

    // 修改 printToPDF 函数
    async function printToPDF() {
        // 创建一个临时容器来存放打印内容
        const tempContainer = document.createElement('div');
        
        if (document.querySelector('.web2pdf-reader-mode')) {
            // 如果在阅读模式下,复制阅读模式的内容
            const readerContent = document.querySelector('.web2pdf-reader-mode').cloneNode(true);
            
            // 清理所有UI元素
            const elementsToRemove = [
                '.web2pdf-floating-button',
                '.web2pdf-menu',
                '.reader-close-button',
                '.editing-toolbar',
                '.element-controls',
                '.image-resizer',
                'button'
            ];
            
            elementsToRemove.forEach(selector => {
                readerContent.querySelectorAll(selector).forEach(el => el.remove());
            });

            // 只保留主要内容
            const mainContent = readerContent.querySelector('.web2pdf-content');
            if (mainContent) {
                // 移除所有编辑相关的属性和类
                mainContent.querySelectorAll('*').forEach(el => {
                    el.removeAttribute('contenteditable');
                    el.classList.remove('editing');
                    if (el.style.position === 'relative') {
                        el.style.position = '';
                    }
                });

                // 处理图片包装器
                mainContent.querySelectorAll('.image-wrapper').forEach(wrapper => {
                    const img = wrapper.querySelector('img');
                    if (img) {
                        const imgContainer = document.createElement('div');
                        imgContainer.className = 'img-container';
                        wrapper.parentNode.insertBefore(imgContainer, wrapper);
                        imgContainer.appendChild(img);
                        wrapper.remove();
                    }
                });

                tempContainer.appendChild(mainContent);
            }
        } else {
            // 如果不在阅读模式下,使用提取的内容
            tempContainer.innerHTML = extractMainContent();
            
            // 为所有图片添加包装容器
            tempContainer.querySelectorAll('img').forEach(img => {
                const imgContainer = document.createElement('div');
                imgContainer.className = 'img-container';
                img.parentNode.insertBefore(imgContainer, img);
                imgContainer.appendChild(img);
            });
        }

        // 添加加载提示
        const loadingTip = document.createElement('div');
        loadingTip.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 10px;
            z-index: 100000;
        `;
        loadingTip.textContent = '正在加载图片,请稍候...';
        document.body.appendChild(loadingTip);

        try {
            // 预加载所有图片
            await preloadImages(tempContainer);
            
            // 计算分页
            const pages = calculateA4Pages(tempContainer);

            const printWindow = window.open('', '_blank');
            printWindow.document.write(`
                <html>
                    <head>
                        <title>${document.title}</title>
                        <style>
                            @page {
                                size: A4;
                                margin: 40px;
                            }
                            body {
                                font-family: Arial, sans-serif;
                                line-height: 1.6;
                                margin: 0;
                                padding: 0;
                            }
                            .page {
                                width: 21cm;
                                height: 29.7cm;
                                padding: 40px;
                                box-sizing: border-box;
                                margin: 0 auto;
                                background: white;
                            }
                            img {
                                max-width: 100%;
                                height: auto;
                                display: block;
                                margin: 10px auto;
                            }
                            .img-container {
                                width: 100%;
                                margin: 20px 0;
                            }
                            h1, h2, h3, h4, h5, h6 {
                                margin: 1em 0 0.5em;
                            }
                            p {
                                margin: 0.5em 0;
                            }
                            @media print {
                                .page {
                                    page-break-after: always;
                                    margin: 0;
                                    height: auto;
                                }
                                .page:last-child {
                                    page-break-after: avoid;
                                }
                            }
                        </style>
                    </head>
                    <body>
                        ${pages.map(page => `
                            <div class="page">
                                ${page.innerHTML}
                            </div>
                        `).join('')}
                    </body>
                </html>
            `);
            printWindow.document.close();
            
            // 等待一段时间确保图片完全加载和布局完成
            setTimeout(() => {
                printWindow.print();
                loadingTip.remove();
            }, 2000);
        } catch (error) {
            console.error('Error during PDF generation:', error);
            alert('生成PDF时出错,请重试');
        } finally {
            loadingTip.remove();
        }
    }

    // 添加清理打印内容的函数
    function cleanupForPrint(element) {
        // 移除所有UI相关元素
        const removeSelectors = [
            '.web2pdf-floating-button',
            '.web2pdf-menu',
            '.reader-close-button',
            '.editing-toolbar',
            'button',
            '[contenteditable]'
        ];

        removeSelectors.forEach(selector => {
            element.querySelectorAll(selector).forEach(el => el.remove());
        });

        // 移除编辑相关的属性和类
        element.querySelectorAll('*').forEach(el => {
            el.removeAttribute('contenteditable');
            el.classList.remove('editing');
        });

        // 返回清理后的HTML
        return element.innerHTML;
    }

    // 提取主要内容
    function extractMainContent() {
        // 针对32r.com的特定选择器
        const specificSelectors = [
            '.details_content', // 详情内容区
            'article',          // 文章区
            '[role="article"]', // 文章角色
            '.article-content', // 文章内容
            '#article-content', // 文章内容ID
            '.content',        // 内容区
            '.main-content',   // 主要内容
            'main',           // 主要区域
            '[role="main"]'   // 主要区域角色
        ];

        let mainContent = null;
        
        // 首先尝试使用特定选择器
        for (const selector of specificSelectors) {
            const element = document.querySelector(selector);
            if (element && element.textContent.trim().length > 100) {
                // 检查内容是否包含导航等不相关内容
                const navigationText = element.textContent.toLowerCase();
                if (!navigationText.includes('网站导航') && 
                    !navigationText.includes('手机版') &&
                    !navigationText.includes('关于我们')) {
                    mainContent = element;
                    break;
                }
            }
        }

        // 如果没找到合适的容器,尝试通过内容密度分析
        if (!mainContent) {
            const allElements = document.querySelectorAll('div, article, section');
            let maxTextDensity = 0;
            let bestElement = null;

            allElements.forEach(element => {
                // 计算文本密度(文本长度/HTML长度的比率)
                const textLength = element.textContent.trim().length;
                const htmlLength = element.innerHTML.length;
                const density = textLength / htmlLength;

                // 检查是否包含标题和正文特征
                const hasTitle = element.querySelector('h1, h2, h3');
                const hasContent = textLength > 500; // 假设正文至少500字符

                if (density > maxTextDensity && hasTitle && hasContent) {
                    maxTextDensity = density;
                    bestElement = element;
                }
            });

            if (bestElement) {
                mainContent = bestElement;
            }
        }

        // 如果仍然没找到,使用body作为后备方案
        if (!mainContent) {
            mainContent = document.body;
        }

        // 创建一个新的容器
        const container = document.createElement('div');
        container.className = 'web2pdf-content';
        
        // 添加标题
        const title = document.createElement('h1');
        title.style.marginBottom = '20px';
        title.textContent = document.title.split('_')[0].split('-')[0].trim(); // 清理标题中的额外文本
        container.appendChild(title);

        // 克隆内容
        const contentClone = mainContent.cloneNode(true);

        // 清理不需要的元素
        const removeSelectors = [
            'script', 'style', 'iframe', 'nav', 'header', 'footer',
            '.advertisement', '.ads', '.social-share', '.comments',
            '#comments', '.sidebar', '.related-posts', '.nav',
            '.navigation', '.menu', '.share', '.social',
            'button', 'input', 'form', '.web2pdf-floating-button',
            '.web2pdf-menu', '.pagination', '.breadcrumb',
            '.category-list', '.tag-list', '.footer', '.header',
            '.site-nav', '.site-header', '.site-footer',
            '[role="complementary"]', '[role="navigation"]'
        ];

        removeSelectors.forEach(selector => {
            contentClone.querySelectorAll(selector).forEach(el => {
                if (el.parentNode) {
                    el.parentNode.removeChild(el);
                }
            });
        });

        // 处理图片
        contentClone.querySelectorAll('img').forEach(img => {
            if (img.src) {
                img.src = img.src; // 确保使用完整URL
                img.style.maxWidth = '100%';
                img.style.height = 'auto';
                img.removeAttribute('srcset'); // 移除响应式图片源
            }
        });

        // 添加内容
        container.appendChild(contentClone);

        return container.outerHTML;
    }

    // 添加编辑模式相关的函数
    async function toggleEditMode(readerMode) {
        const content = readerMode.querySelector('.web2pdf-content') || readerMode;
        const isEditing = content.getAttribute('contenteditable') === 'true';
        
        if (isEditing) {
            // 退出编辑模式
            content.setAttribute('contenteditable', 'false');
            content.classList.remove('editing');
            removeEditingToolbar();
            removeElementControls();
        } else {
            // 进入编辑模式
            content.setAttribute('contenteditable', 'true');
            content.classList.add('editing');
            await createEditingToolbar(readerMode);
            await addElementControls(content);
        }
    }

    // 修改创建编辑工具栏函数
    async function createEditingToolbar(readerMode) {
        const existingToolbar = document.querySelector('.editing-toolbar');
        if (existingToolbar) {
            existingToolbar.remove();
        }

        const toolbar = document.createElement('div');
        toolbar.className = 'editing-toolbar';
        toolbar.innerHTML = `
            <button data-command="bold" title="${await t('bold')}">B</button>
            <button data-command="italic" title="${await t('italic')}">I</button>
            <button data-command="underline" title="${await t('underline')}">U</button>
            <button data-command="formatBlock" data-value="h1" title="${await t('heading1')}">H1</button>
            <button data-command="formatBlock" data-value="h2" title="${await t('heading2')}">H2</button>
            <button data-command="formatBlock" data-value="p" title="${await t('paragraph')}">P</button>
            <button data-command="justifyLeft" title="${await t('alignLeft')}">⇤</button>
            <button data-command="justifyCenter" title="${await t('alignCenter')}">⇔</button>
            <button data-command="justifyRight" title="${await t('alignRight')}">⇥</button>
            <button data-command="insertUnorderedList" title="${await t('bulletList')}">•</button>
            <button data-command="insertOrderedList" title="${await t('numberList')}">1.</button>
            <button data-command="createLink" title="${await t('addLink')}">🔗</button>
            <button data-command="undo" title="${await t('undo')}">↩</button>
            <button data-command="redo" title="${await t('redo')}">↪</button>
        `;

        toolbar.addEventListener('click', async (e) => {
            const button = e.target.closest('button');
            if (!button) return;

            e.preventDefault();
            const command = button.dataset.command;
            const value = button.dataset.value;

            if (command === 'createLink') {
                const url = prompt(await t('enterLink'), 'http://');
                if (url) document.execCommand(command, false, url);
            } else {
                document.execCommand(command, false, value);
            }
        });

        readerMode.appendChild(toolbar);
    }

    // 移除编辑工具栏
    function removeEditingToolbar() {
        const toolbar = document.querySelector('.editing-toolbar');
        if (toolbar) {
            toolbar.remove();
        }
    }

    // 修改添加元素控制的函数为异步
    async function addElementControls(container) {
        const blockElements = container.querySelectorAll('p, div, section, article, aside, h1, h2, h3, h4, h5, h6');
        for (const element of blockElements) {
            if (!element.classList.contains('web2pdf-content')) {
                await addElementControl(element);
            }
        }

        // 为图片添加调整大小的功能
        const images = container.querySelectorAll('img');
        images.forEach(img => addImageResize(img));
    }

    // 修改为单个元素添加控制按钮的函数
    async function addElementControl(element) {
        const controls = document.createElement('div');
        controls.className = 'element-controls';
        controls.innerHTML = `
            <button class="delete-btn" title="${await t('deleteBlock')}">×</button>
        `;

        element.style.position = 'relative';
        element.appendChild(controls);

        controls.querySelector('.delete-btn').addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            // 直接删除元素,不需要确认
            element.remove();
        });
    }

    // 为图片添加缩放功能
    function addImageResize(img) {
        const wrapper = document.createElement('div');
        wrapper.className = 'image-wrapper';
        img.parentNode.insertBefore(wrapper, img);
        wrapper.appendChild(img);

        const resizer = document.createElement('div');
        resizer.className = 'image-resizer';
        wrapper.appendChild(resizer);

        let startX, startY, startWidth, startHeight;

        resizer.addEventListener('mousedown', initResize);

        function initResize(e) {
            startX = e.clientX;
            startY = e.clientY;
            startWidth = img.offsetWidth;
            startHeight = img.offsetHeight;
            
            document.addEventListener('mousemove', resize);
            document.addEventListener('mouseup', stopResize);
        }

        function resize(e) {
            const width = startWidth + (e.clientX - startX);
            const height = startHeight * (width / startWidth); // 保持宽高比
            
            img.style.width = width + 'px';
            img.style.height = height + 'px';
        }

        function stopResize() {
            document.removeEventListener('mousemove', resize);
            document.removeEventListener('mouseup', stopResize);
        }
    }

    // 移除元素控制
    function removeElementControls() {
        document.querySelectorAll('.element-controls').forEach(control => control.remove());
        document.querySelectorAll('.image-resizer').forEach(resizer => resizer.remove());
        document.querySelectorAll('.image-wrapper').forEach(wrapper => {
            const img = wrapper.querySelector('img');
            if (img) {
                wrapper.parentNode.insertBefore(img, wrapper);
                wrapper.remove();
            }
        });
    }

    // 修改初始化函数
    function initializeExtension() {
        try {
            createFloatingButton();
            createMenu();
            console.log('Web2PDF initialized successfully');
        } catch (error) {
            console.error('Failed to initialize Web2PDF:', error);
        }
    }

    // 等待页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeExtension);
    } else {
        initializeExtension();
    }

    // 添加错误处理
    window.addEventListener('error', function(e) {
        console.error('Web2PDF error:', e.error);
    });
})(); 

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址