Greasy Fork

来自缓存

Greasy Fork is available in English.

云展网下载PDF

云展网下载高清PDF!支持多种模式!

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         云展网下载PDF
// @namespace    http://tampermonkey.net/yunzhanpDF
// @version      1.21
// @description  云展网下载高清PDF!支持多种模式!
// @author       ZouYS
// @match        https://*.yunzhan365.com/*
// @icon         https://book.yunzhan365.com/web/images/nav_logo_big.jpg
// @require      https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require      https://fastly.jsdelivr.net/npm/sweetalert2@11
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/pdf-lib.min.js
// @grant        GM_addStyle
// @grant        GM_addElement
// @grant        unsafeWindow
// @run-at       document-end
// ==/UserScript==

(function () {
    'use strict';
    window.onload = function () {
        /*if(!window.htmlConfig){
            console.error('error!')
            return 1;
        }*/
        /*const zip = new JSZip();
        if (!zip) {
            console.error('error loading zip!')
            return
        }*/
        let css = `.mydiv1{
            position: absolute;
            z-index: 9999999999;
            left: 100px;
            top: 300px;
            display: none;
        }
        .mybtn1 {
            z-index: 9999999999;
            position: absolute;
            top: 300px;
            font-size: inherit;
            font-family: inherit;
            color: white;
            padding: 0.5em 1em;
            outline: none;
            border: none;
            background-color: hsl(236, 32%, 26%);
            overflow: hidden;
            transition: color 0.4s ease-in-out;
        }
        .mybtn2 {
            z-index: 9999999999;
            position: absolute;
            top: 350px;
            font-size: inherit;
            font-family: inherit;
            color: white;
            padding: 0.5em 1em;
            outline: none;
            border: none;
            background-color: hsl(236, 32%, 26%);
            overflow: hidden;
            transition: color 0.4s ease-in-out;
        }

        .mybtn2::before,.mybtn1::before {
            content: '';
            z-index: -1;
            position: absolute;
            top: 100%;
            right: 100%;
            width: 1em;
            height: 1em;
            border-radius: 50%;
            background-color: #3cefff;
            transform-origin: center;
            transform: translate3d(50%, -50%, 0) scale3d(0, 0, 0);
            transition: transform 0.45s ease-in-out;
        }

        .mybtn2:hover,.mybtn1:hover {
            cursor: pointer;
            color: #161616;
        }

        .mybtn2:hover::before,.mybtn1:hover::before {
            transform: translate3d(50%, -50%, 0) scale3d(15, 15, 15);
        }`;
        GM_addStyle(css);
        const rules=new Map()
        const rulesClass=['cover_shadow side','flip-shadowB','flip-topshadow','HandPainted','midShadow','grayShadow','ViewModule','leftShadow book','rightShadow book','cover_shadow side flip_x']
        rulesClass.forEach(item=>{
            rules.set(item,true)
        })
        const button = document.createElement('button')
        button.classList.add('mybtn1')
        button.innerText = '高清下载PDF'
        document.body.append(button)
        button.onclick = () => {
            // console.log(unsafeWindow)
            try {
                let newUrl = document.getElementsByTagName('iframe')[0] && document.getElementsByTagName('iframe')[0].src
                if (newUrl) {
                    unsafeWindow.location.href = newUrl
                }
                if (Swal) {
                    Swal.fire({
                        position: 'top-end', //定位 左上角
                        icon: 'success',
                        title: '↑↑↑下载中,请留意浏览器右上角↑↑↑弹窗~',
                        showConfirmButton: false,
                        timer: 1500
                    })
                }
                let meta = unsafeWindow.htmlConfig || console.error('no meta data!')
                let imgUrls = meta.fliphtml5_pages
                let basicUrl = meta.meta.url
                basicUrl = basicUrl.slice(0, basicUrl.lastIndexOf('/') + 1)

                addImageToPDF(meta, imgUrls, basicUrl).then(pdf => {
                    pdf.save(`${meta.meta.title}.pdf`);
                });
            } catch (e) {
                if (Swal) {
                    Swal.fire({
                        position: 'center', //定位 左上角
                        icon: 'error',
                        title: `下载失败,请尝试兼容模式!~错误信息:${e}`,
                        // showConfirmButton: false,
                        // timer: 1500
                    })
                }
                console.error(e)
            }
        }
        const button_01 = document.createElement('button')
        button_01.classList.add('mybtn2')
        button_01.innerText = '下载-兼容模式'
        document.body.append(button_01)
        button_01.onclick = async () => {
            if(button_01.innerText==='下载中...'){
                return
            }
            const start = new Date();
            try {
                button_01.innerText='下载中...'
                let oriPDF = unsafeWindow['pdfjs-dist/build/pdf']

                let htmlConfig = unsafeWindow.htmlConfig || console.error('no meta data!')
                let fliphtml5_pages = htmlConfig.fliphtml5_pages || ''
                let meta = htmlConfig.meta;
                let pos = 0
                const pageCount = meta.pageCount;
                let pdf;
                //zip
                for (let i = 0; i < fliphtml5_pages.length; i++) {
                    if (fliphtml5_pages[i].n[0] && fliphtml5_pages[i].n[0].includes("zip")) {
                        //zip的情况
                        pos = 1
                    }
                }
                // console.log('fliphtml5_pages:',fliphtml5_pages)
                console.log('pos:', pos)
                //处理各种情况
                switch (pos) {
                    /**
                     * 情況一:html拼接
                     * 图片分解后写入为html拼接
                     */
                    case 0:
                        let scale=parseInt(prompt('case 0:html拼接,选择像素倍数(渲染高像素耗时较长,基数为页面展示的长宽,倍数默认5倍时,平均每页大约60s)','5'))
                        for (let i = 0; i < pageCount; i++) {
                            let page;
                            do{
                                page = document.getElementById(`page${i + 1}`)
                                if(!page){
                                    unsafeWindow.nextPageFun("flip shot bar");
                                    await sleep(3000);
                                    unsafeWindow.nextPageFun("flip shot bar");
                                    await sleep(3000);
                                }
                            }while (!page)
                            // 记录开始时间
                            const startTime = new Date();
                            // page = page.childNodes[0].childNodes[1]
                            console.log(`---------------------------------------------------------------------------------`)
                            console.log(`page${i + 1}: `, page)
                            const width = page.style.width;
                            const height = page.style.height;
                            if (page) {
                                const dom = page
                                //图片加像素
                                const startTimeDiv = new Date();
                                await replaceBackgroundImage(dom);
                                console.log(`page${i+1}:loop div using ${((new Date()-startTimeDiv)/1000).toFixed(2)}s`)
                                // 解决转换出来的图片的清晰度问题
                                // 手动创建一个 canvas 标签
                                let root = document.documentElement
                                root.style.overflow = 'auto'
                                const canvas = document.createElement('canvas')
                                // 获取父级的宽高
                                const width = parseInt(window.getComputedStyle(dom).width)
                                const height = parseInt(window.getComputedStyle(dom).height)
                                // 定义放大倍数,可支持小数
                                canvas.width = width * scale
                                canvas.height = height * scale
                                canvas.style.width = width + 'px'
                                canvas.style.height = height + 'px'

                                // 拿到目标dom调用一下html2canvas方法就能生成canvas对象了
                                // 获取要转换的元素
                                await html2canvas(dom, {
                                    canvas: canvas,
                                    scale: scale,
                                    dpi:96*2,
                                    useCORS: true,// 开启跨域设置,需要后台设置cors
                                    ignoreElements: (element) => {
                                        // console.log(element)
                                        if(rules.has(element.className)){
                                            return true;
                                        }
                                        /*if(element.tagName==='DIV' &&element.getAttribute('style')&& element.getAttribute('style').includes('background-image: url("blob:https://www.gaoding.com/')){
                                            return true;
                                        }*/
                                    }
                                }).then((canvas) => {
                                    if (!pdf) {
                                        pdf = new jspdf.jsPDF({
                                            orientation: canvas.width / canvas.height > 1 ? 'l' : 'p',
                                            unit: 'px',
                                            format: [canvas.width, canvas.height],
                                            compressPdf: true
                                        });
                                    }
                                    if (i !== 0) {
                                        pdf.addPage({
                                            format: [canvas.width, canvas.height],
                                        });
                                    }
                                    let dataURL = canvas.toDataURL('image/png')
                                    // 计算居中位置
                                    const x = 0;
                                    const y = 0;
                                    // 确保坐标和尺寸有效
                                    pdf.addImage(dataURL, 'PNG', x, y, canvas.width, canvas.height);
                                    console.log(`page ${i + 1}: loaded in ${((new Date()-startTime)/1000).toFixed(2)}s`)
                                    console.log(`---------------------------------------------------------------------------------`)
                                })
                            }

                        }
                        pdf.save(`${meta.title}.pdf`);
                        break
                    /**
                     * 情況二:zip的pdf文件
                     * 图片为PDF单页文件
                     */
                    case 1:
                        //https://book.yunzhan365.com/eurha/wafe/files/content-page/04f0837c365de291805215b7b97a5bdf.zip
                        let baseUrl = meta.url && meta.url.slice(0, meta.url.lastIndexOf('/') + 1) + 'files/content-page/'

                        for (let i = 0; i < fliphtml5_pages.length; i++) {
                            if (fliphtml5_pages[i].n[0] && fliphtml5_pages[i].n[0].includes("zip")) {
                                let url = `${baseUrl}${fliphtml5_pages[i].n[0]}`
                                console.log('url:', url)

                                let blobRes =await getBlob(url)
                                console.log('blobRes:', blobRes)
                                this.loadingTask = oriPDF.getDocument({url: blobRes.url, password: blobRes.password});
                                const l = await this.loadingTask.promise;
                                const page = await l.getPage(1);
                                console.log(`Page ${i} loaded`);

                                const scale = 2; // 缩放比例
                                const viewport = page.getViewport({scale});

                                if (!pdf) {
                                    pdf = new jspdf.jsPDF({
                                        orientation: viewport.width / viewport.height > 1 ? 'l' : 'p',
                                        unit: 'px',
                                        format: [viewport.width, viewport.height],
                                    });
                                }

                                if (i !== 0) {
                                    pdf.addPage({
                                        format: [viewport.width, viewport.height],
                                    });
                                }

                                // 创建用于渲染的 canvas
                                const canvas = document.createElement('canvas');
                                const context = canvas.getContext('2d');
                                canvas.height = viewport.height;
                                canvas.width = viewport.width;

                                // 渲染 PDF 页面到 canvas
                                const renderContext = {
                                    canvasContext: context,
                                    viewport: viewport,
                                };
                                await page.render(renderContext).promise;

                                // 获取图像数据
                                const imgData = canvas.toDataURL('image/png');

                                // 将图像数据添加到 jsPDF 中
                                pdf.addImage(imgData, 'PNG', 0, 0, viewport.width, viewport.height);
                                canvas.remove();

                            }
                        }
                        pdf.save(`${meta.title}.pdf`);

                        break
                    default:

                }
                button_01.innerText = '下载-兼容模式'
                console.log('下载成功!')
                if (Swal) {
                    Swal.fire({
                        position: 'center', //定位 左上角
                        icon: 'success',
                        title: `下载成功!~用时:${((new Date()-start)/1000).toFixed(2)}s`,
                        // showConfirmButton: false,
                        // timer: 3000
                    })
                }
            } catch (e) {
                if (Swal) {
                    Swal.fire({
                        position: 'center', //定位 左上角
                        icon: 'error',
                        title: `下载失败,请刷新重试!~${e}`,
                        // showConfirmButton: false,
                        // timer: 1500
                    })
                }
                button_01.innerText = '下载-兼容模式'
                console.error(e)
            }
        }

        // 定义一个递归函数来遍历所有 div
        async function replaceBackgroundImage(div) {
            // 获取当前 div 的背景图像样式
            const style = div.style.backgroundImage;

            // 检查是否包含 background-image
            if (style) {
                // 提取 URL 部分
                const urlMatch = style.match(/url\(["']?(.*?)["']?\)/);
                if (urlMatch) {
                    let url = urlMatch[1];

                    /**
                     * 解决 图片模糊
                     * 生成img标签
                     */
                    await new Promise(resolve => {
                        const imgElement = document.createElement('img');

                        // 替换掉查询字符串部分
                        url = url.split('?')[0]; // 删除 '?' 及其后面内容
                        // 更新背景图像的样式
                        div.style.backgroundImage = ``;
                        Array.from(div.attributes).forEach(attr => {
                            imgElement.setAttribute(attr.name, attr.value);
                        });
                        imgElement.src = url;
                        imgElement.onload=()=>{
                            div.parentNode.replaceChild(imgElement, div);
                            console.log(`img replaced url:${url}`)
                            resolve();
                        }
                    });
                }
            }

            // 遍历当前 div 的所有子元素
            const children = div.children;
            for (let i = 0; i < children.length; i++) {
                if (rules.has(children[i].className)) {
                    console.log(`${children[i].className} has been remove! `);
                    children[i].remove()
                    break
                }
                await replaceBackgroundImage(children[i]); // 递归调用
            }
        }

        /**
         * 解密PDF文件,返回密码和文件url
         * @param b
         * @returns {*}
         */
        function getBlob(b) {
            return new Promise(function (c, d) {
                d = new XMLHttpRequest;
                d.open("get", b, !0);
                d.responseType = "blob";
                d.onload = function () {
                    if (4 == this.readyState && 200 == this.status) {
                        (new Date).getTime();
                        window.response = this.response;
                        let e = new FileReader;
                        e.onload = function () {
                            window.arrayBuffer = new Uint8Array(this.result)
                        };
                        e.readAsArrayBuffer(response);
                        let f = response.slice(1083, response.size - 1003, "application/pdf"), g = "",
                            h = response.slice(1080, 1083),
                            k = response.slice(response.size - 1003, response.size - 1E3);
                        e = new FileReader;
                        e.onload = function () {
                            g = this.result + g;
                            let l = new FileReader;
                            l.onload = function () {
                                g += this.result;
                                var n = f.slice(0, 4E3), p = new FileReader;
                                p.onload = function () {
                                    var v = new Uint8Array(this.result);
                                    for (let i = 0; i < this.result.byteLength; ++i) v[i] = 255 - v[i];
                                    v = new Blob([new Blob([v.buffer]), f.slice(4E3, f.size)], {type: "application/pdf"});
                                    v = window.URL.createObjectURL(v);
                                    c({url: v, password: g})
                                };
                                p.readAsArrayBuffer(n)
                            };
                            l.readAsText(k)
                        };
                        e.readAsText(h)
                    }
                };
                d.send()
            }.bind(this))
        }

        async function addImageToPDF(meta, imageUrls, basicUrl) {
            let pdf;
            for (let i = 0; i < imageUrls.length; i++) {
                try {
                    let url = imageUrls[i].n[0].split('?')[0]
                    url += `?x-oss-process=image/sharpen,100`
                    if (url.includes('files/large/')) {
                        url = url.replace('../', '')
                    } else {
                        url = 'files/large/' + url
                    }
                    url = basicUrl + url;
                    console.log(`第${i + 1}页  url:`, url)
                    const img = await loadImage(url);
                    const imgWidth = img.width;
                    const imgHeight = img.height;
                    if (!pdf) {
                        pdf = new jspdf.jsPDF({
                            orientation: imgWidth / imgHeight > 1 ? 'l' : 'p',
                            unit: 'px',
                            format: [imgWidth, imgHeight],
                            compress: true
                        });
                    }
                    if (i !== 0) {
                        pdf.addPage({
                            format: [imgWidth, imgHeight],
                        });
                    }
                    /*const pageWidth = pdf.internal.pageSize.width;
                    const pageHeight = pdf.internal.pageSize.height;

                    // 计算缩放比例
                    const ratio = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
                    const newWidth = imgWidth * ratio;
                    const newHeight = imgHeight * ratio;*/

                    // 计算居中位置
                    const x = 0;
                    const y = 0;
                    // 确保坐标和尺寸有效
                    pdf.addImage(img, 'WEBP', x, y, imgWidth, imgHeight);
                    /*if (newWidth > 0 && newHeight > 0 && x >= 0 && y >= 0) {
                        pdf.addImage(img, 'WEBP', x, y, newWidth, newHeight);
                    } else {
                        console.error('Invalid dimensions or coordinates:', { newWidth, newHeight, x, y });
                    }*/
                    /*if(i!==imageUrls.length-1){
                        pdf.addPage()
                    }*/
                    if (Swal && i > 10) {
                        Swal.fire({
                            position: 'top-end', //定位 左上角
                            icon: 'success',
                            title: `加载中,第${i + 1}页`,
                            showConfirmButton: false,
                        })
                    }
                } catch (e) {
                    if (Swal) {
                        Swal.fire({
                            icon: 'error',
                            title: `下载失败,${e.message}`,
                            showConfirmButton: true,
                        })
                    }
                }

            }
            return pdf;
        }

        function loadImage(url) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.crossOrigin = 'anonymous'; // 处理跨域问题
                img.src = url;
                img.onload = () => {
                    /*const canvas = document.createElement('canvas');
                    canvas.width = img.width;
                    canvas.height = img.height;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0);*/
                    // resolve(canvas.toDataURL('image/webp'));
                    resolve(img);
                };
                img.onerror = reject;
            });
        }
        function sleep(ms){
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    }
    // Your code here...
})();