云展网下载PDF

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

  1. // ==UserScript==
  2. // @name 云展网下载PDF
  3. // @namespace http://tampermonkey.net/yunzhanpDF
  4. // @version 1.21
  5. // @description 云展网下载高清PDF!支持多种模式!
  6. // @author ZouYS
  7. // @match https://*.yunzhan365.com/*
  8. // @icon https://book.yunzhan365.com/web/images/nav_logo_big.jpg
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
  10. // @require https://cdn.jsdelivr.net/npm/sweetalert2@11
  11. // @require https://fastly.jsdelivr.net/npm/sweetalert2@11
  12. // @require https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js
  13. // @require https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js
  14. // @grant GM_addStyle
  15. // @grant GM_addElement
  16. // @grant unsafeWindow
  17. // @run-at document-end
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. 'use strict';
  22. window.onload = function () {
  23. /*if(!window.htmlConfig){
  24. console.error('error!')
  25. return 1;
  26. }*/
  27. /*const zip = new JSZip();
  28. if (!zip) {
  29. console.error('error loading zip!')
  30. return
  31. }*/
  32. let css = `.mydiv1{
  33. position: absolute;
  34. z-index: 9999999999;
  35. left: 100px;
  36. top: 300px;
  37. display: none;
  38. }
  39. .mybtn1 {
  40. z-index: 9999999999;
  41. position: absolute;
  42. top: 300px;
  43. font-size: inherit;
  44. font-family: inherit;
  45. color: white;
  46. padding: 0.5em 1em;
  47. outline: none;
  48. border: none;
  49. background-color: hsl(236, 32%, 26%);
  50. overflow: hidden;
  51. transition: color 0.4s ease-in-out;
  52. }
  53. .mybtn2 {
  54. z-index: 9999999999;
  55. position: absolute;
  56. top: 350px;
  57. font-size: inherit;
  58. font-family: inherit;
  59. color: white;
  60. padding: 0.5em 1em;
  61. outline: none;
  62. border: none;
  63. background-color: hsl(236, 32%, 26%);
  64. overflow: hidden;
  65. transition: color 0.4s ease-in-out;
  66. }
  67.  
  68. .mybtn2::before,.mybtn1::before {
  69. content: '';
  70. z-index: -1;
  71. position: absolute;
  72. top: 100%;
  73. right: 100%;
  74. width: 1em;
  75. height: 1em;
  76. border-radius: 50%;
  77. background-color: #3cefff;
  78. transform-origin: center;
  79. transform: translate3d(50%, -50%, 0) scale3d(0, 0, 0);
  80. transition: transform 0.45s ease-in-out;
  81. }
  82.  
  83. .mybtn2:hover,.mybtn1:hover {
  84. cursor: pointer;
  85. color: #161616;
  86. }
  87.  
  88. .mybtn2:hover::before,.mybtn1:hover::before {
  89. transform: translate3d(50%, -50%, 0) scale3d(15, 15, 15);
  90. }`;
  91. GM_addStyle(css);
  92. const rules=new Map()
  93. const rulesClass=['cover_shadow side','flip-shadowB','flip-topshadow','HandPainted','midShadow','grayShadow','ViewModule','leftShadow book','rightShadow book','cover_shadow side flip_x']
  94. rulesClass.forEach(item=>{
  95. rules.set(item,true)
  96. })
  97. const button = document.createElement('button')
  98. button.classList.add('mybtn1')
  99. button.innerText = '高清下载PDF'
  100. document.body.append(button)
  101. button.onclick = () => {
  102. // console.log(unsafeWindow)
  103. try {
  104. let newUrl = document.getElementsByTagName('iframe')[0] && document.getElementsByTagName('iframe')[0].src
  105. if (newUrl) {
  106. unsafeWindow.location.href = newUrl
  107. }
  108. if (Swal) {
  109. Swal.fire({
  110. position: 'top-end', //定位 左上角
  111. icon: 'success',
  112. title: '↑↑↑下载中,请留意浏览器右上角↑↑↑弹窗~',
  113. showConfirmButton: false,
  114. timer: 1500
  115. })
  116. }
  117. let meta = unsafeWindow.htmlConfig || console.error('no meta data!')
  118. let imgUrls = meta.fliphtml5_pages
  119. let basicUrl = meta.meta.url
  120. basicUrl = basicUrl.slice(0, basicUrl.lastIndexOf('/') + 1)
  121.  
  122. addImageToPDF(meta, imgUrls, basicUrl).then(pdf => {
  123. pdf.save(`${meta.meta.title}.pdf`);
  124. });
  125. } catch (e) {
  126. if (Swal) {
  127. Swal.fire({
  128. position: 'center', //定位 左上角
  129. icon: 'error',
  130. title: `下载失败,请尝试兼容模式!~错误信息:${e}`,
  131. // showConfirmButton: false,
  132. // timer: 1500
  133. })
  134. }
  135. console.error(e)
  136. }
  137. }
  138. const button_01 = document.createElement('button')
  139. button_01.classList.add('mybtn2')
  140. button_01.innerText = '下载-兼容模式'
  141. document.body.append(button_01)
  142. button_01.onclick = async () => {
  143. if(button_01.innerText==='下载中...'){
  144. return
  145. }
  146. const start = new Date();
  147. try {
  148. button_01.innerText='下载中...'
  149. let oriPDF = unsafeWindow['pdfjs-dist/build/pdf']
  150.  
  151. let htmlConfig = unsafeWindow.htmlConfig || console.error('no meta data!')
  152. let fliphtml5_pages = htmlConfig.fliphtml5_pages || ''
  153. let meta = htmlConfig.meta;
  154. let pos = 0
  155. const pageCount = meta.pageCount;
  156. let pdf;
  157. //zip
  158. for (let i = 0; i < fliphtml5_pages.length; i++) {
  159. if (fliphtml5_pages[i].n[0] && fliphtml5_pages[i].n[0].includes("zip")) {
  160. //zip的情况
  161. pos = 1
  162. }
  163. }
  164. // console.log('fliphtml5_pages:',fliphtml5_pages)
  165. console.log('pos:', pos)
  166. //处理各种情况
  167. switch (pos) {
  168. /**
  169. * 情況一:html拼接
  170. * 图片分解后写入为html拼接
  171. */
  172. case 0:
  173. let scale=parseInt(prompt('case 0:html拼接,选择像素倍数(渲染高像素耗时较长,基数为页面展示的长宽,倍数默认5倍时,平均每页大约60s)','5'))
  174. for (let i = 0; i < pageCount; i++) {
  175. let page;
  176. do{
  177. page = document.getElementById(`page${i + 1}`)
  178. if(!page){
  179. unsafeWindow.nextPageFun("flip shot bar");
  180. await sleep(3000);
  181. unsafeWindow.nextPageFun("flip shot bar");
  182. await sleep(3000);
  183. }
  184. }while (!page)
  185. // 记录开始时间
  186. const startTime = new Date();
  187. // page = page.childNodes[0].childNodes[1]
  188. console.log(`---------------------------------------------------------------------------------`)
  189. console.log(`page${i + 1}: `, page)
  190. const width = page.style.width;
  191. const height = page.style.height;
  192. if (page) {
  193. const dom = page
  194. //图片加像素
  195. const startTimeDiv = new Date();
  196. await replaceBackgroundImage(dom);
  197. console.log(`page${i+1}:loop div using ${((new Date()-startTimeDiv)/1000).toFixed(2)}s`)
  198. // 解决转换出来的图片的清晰度问题
  199. // 手动创建一个 canvas 标签
  200. let root = document.documentElement
  201. root.style.overflow = 'auto'
  202. const canvas = document.createElement('canvas')
  203. // 获取父级的宽高
  204. const width = parseInt(window.getComputedStyle(dom).width)
  205. const height = parseInt(window.getComputedStyle(dom).height)
  206. // 定义放大倍数,可支持小数
  207. canvas.width = width * scale
  208. canvas.height = height * scale
  209. canvas.style.width = width + 'px'
  210. canvas.style.height = height + 'px'
  211.  
  212. // 拿到目标dom调用一下html2canvas方法就能生成canvas对象了
  213. // 获取要转换的元素
  214. await html2canvas(dom, {
  215. canvas: canvas,
  216. scale: scale,
  217. dpi:96*2,
  218. useCORS: true,// 开启跨域设置,需要后台设置cors
  219. ignoreElements: (element) => {
  220. // console.log(element)
  221. if(rules.has(element.className)){
  222. return true;
  223. }
  224. /*if(element.tagName==='DIV' &&element.getAttribute('style')&& element.getAttribute('style').includes('background-image: url("blob:https://www.gaoding.com/')){
  225. return true;
  226. }*/
  227. }
  228. }).then((canvas) => {
  229. if (!pdf) {
  230. pdf = new jspdf.jsPDF({
  231. orientation: canvas.width / canvas.height > 1 ? 'l' : 'p',
  232. unit: 'px',
  233. format: [canvas.width, canvas.height],
  234. compressPdf: true
  235. });
  236. }
  237. if (i !== 0) {
  238. pdf.addPage({
  239. format: [canvas.width, canvas.height],
  240. });
  241. }
  242. let dataURL = canvas.toDataURL('image/png')
  243. // 计算居中位置
  244. const x = 0;
  245. const y = 0;
  246. // 确保坐标和尺寸有效
  247. pdf.addImage(dataURL, 'PNG', x, y, canvas.width, canvas.height);
  248. console.log(`page ${i + 1}: loaded in ${((new Date()-startTime)/1000).toFixed(2)}s`)
  249. console.log(`---------------------------------------------------------------------------------`)
  250. })
  251. }
  252.  
  253. }
  254. pdf.save(`${meta.title}.pdf`);
  255. break
  256. /**
  257. * 情況二:zip的pdf文件
  258. * 图片为PDF单页文件
  259. */
  260. case 1:
  261. //https://book.yunzhan365.com/eurha/wafe/files/content-page/04f0837c365de291805215b7b97a5bdf.zip
  262. let baseUrl = meta.url && meta.url.slice(0, meta.url.lastIndexOf('/') + 1) + 'files/content-page/'
  263.  
  264. for (let i = 0; i < fliphtml5_pages.length; i++) {
  265. if (fliphtml5_pages[i].n[0] && fliphtml5_pages[i].n[0].includes("zip")) {
  266. let url = `${baseUrl}${fliphtml5_pages[i].n[0]}`
  267. console.log('url:', url)
  268.  
  269. let blobRes =await getBlob(url)
  270. console.log('blobRes:', blobRes)
  271. this.loadingTask = oriPDF.getDocument({url: blobRes.url, password: blobRes.password});
  272. const l = await this.loadingTask.promise;
  273. const page = await l.getPage(1);
  274. console.log(`Page ${i} loaded`);
  275.  
  276. const scale = 2; // 缩放比例
  277. const viewport = page.getViewport({scale});
  278.  
  279. if (!pdf) {
  280. pdf = new jspdf.jsPDF({
  281. orientation: viewport.width / viewport.height > 1 ? 'l' : 'p',
  282. unit: 'px',
  283. format: [viewport.width, viewport.height],
  284. });
  285. }
  286.  
  287. if (i !== 0) {
  288. pdf.addPage({
  289. format: [viewport.width, viewport.height],
  290. });
  291. }
  292.  
  293. // 创建用于渲染的 canvas
  294. const canvas = document.createElement('canvas');
  295. const context = canvas.getContext('2d');
  296. canvas.height = viewport.height;
  297. canvas.width = viewport.width;
  298.  
  299. // 渲染 PDF 页面到 canvas
  300. const renderContext = {
  301. canvasContext: context,
  302. viewport: viewport,
  303. };
  304. await page.render(renderContext).promise;
  305.  
  306. // 获取图像数据
  307. const imgData = canvas.toDataURL('image/png');
  308.  
  309. // 将图像数据添加到 jsPDF 中
  310. pdf.addImage(imgData, 'PNG', 0, 0, viewport.width, viewport.height);
  311. canvas.remove();
  312.  
  313. }
  314. }
  315. pdf.save(`${meta.title}.pdf`);
  316.  
  317. break
  318. default:
  319.  
  320. }
  321. button_01.innerText = '下载-兼容模式'
  322. console.log('下载成功!')
  323. if (Swal) {
  324. Swal.fire({
  325. position: 'center', //定位 左上角
  326. icon: 'success',
  327. title: `下载成功!~用时:${((new Date()-start)/1000).toFixed(2)}s`,
  328. // showConfirmButton: false,
  329. // timer: 3000
  330. })
  331. }
  332. } catch (e) {
  333. if (Swal) {
  334. Swal.fire({
  335. position: 'center', //定位 左上角
  336. icon: 'error',
  337. title: `下载失败,请刷新重试!~${e}`,
  338. // showConfirmButton: false,
  339. // timer: 1500
  340. })
  341. }
  342. button_01.innerText = '下载-兼容模式'
  343. console.error(e)
  344. }
  345. }
  346.  
  347. // 定义一个递归函数来遍历所有 div
  348. async function replaceBackgroundImage(div) {
  349. // 获取当前 div 的背景图像样式
  350. const style = div.style.backgroundImage;
  351.  
  352. // 检查是否包含 background-image
  353. if (style) {
  354. // 提取 URL 部分
  355. const urlMatch = style.match(/url\(["']?(.*?)["']?\)/);
  356. if (urlMatch) {
  357. let url = urlMatch[1];
  358.  
  359. /**
  360. * 解决 图片模糊
  361. * 生成img标签
  362. */
  363. await new Promise(resolve => {
  364. const imgElement = document.createElement('img');
  365.  
  366. // 替换掉查询字符串部分
  367. url = url.split('?')[0]; // 删除 '?' 及其后面内容
  368. // 更新背景图像的样式
  369. div.style.backgroundImage = ``;
  370. Array.from(div.attributes).forEach(attr => {
  371. imgElement.setAttribute(attr.name, attr.value);
  372. });
  373. imgElement.src = url;
  374. imgElement.onload=()=>{
  375. div.parentNode.replaceChild(imgElement, div);
  376. console.log(`img replaced url:${url}`)
  377. resolve();
  378. }
  379. });
  380. }
  381. }
  382.  
  383. // 遍历当前 div 的所有子元素
  384. const children = div.children;
  385. for (let i = 0; i < children.length; i++) {
  386. if (rules.has(children[i].className)) {
  387. console.log(`${children[i].className} has been remove! `);
  388. children[i].remove()
  389. break
  390. }
  391. await replaceBackgroundImage(children[i]); // 递归调用
  392. }
  393. }
  394.  
  395. /**
  396. * 解密PDF文件,返回密码和文件url
  397. * @param b
  398. * @returns {*}
  399. */
  400. function getBlob(b) {
  401. return new Promise(function (c, d) {
  402. d = new XMLHttpRequest;
  403. d.open("get", b, !0);
  404. d.responseType = "blob";
  405. d.onload = function () {
  406. if (4 == this.readyState && 200 == this.status) {
  407. (new Date).getTime();
  408. window.response = this.response;
  409. let e = new FileReader;
  410. e.onload = function () {
  411. window.arrayBuffer = new Uint8Array(this.result)
  412. };
  413. e.readAsArrayBuffer(response);
  414. let f = response.slice(1083, response.size - 1003, "application/pdf"), g = "",
  415. h = response.slice(1080, 1083),
  416. k = response.slice(response.size - 1003, response.size - 1E3);
  417. e = new FileReader;
  418. e.onload = function () {
  419. g = this.result + g;
  420. let l = new FileReader;
  421. l.onload = function () {
  422. g += this.result;
  423. var n = f.slice(0, 4E3), p = new FileReader;
  424. p.onload = function () {
  425. var v = new Uint8Array(this.result);
  426. for (let i = 0; i < this.result.byteLength; ++i) v[i] = 255 - v[i];
  427. v = new Blob([new Blob([v.buffer]), f.slice(4E3, f.size)], {type: "application/pdf"});
  428. v = window.URL.createObjectURL(v);
  429. c({url: v, password: g})
  430. };
  431. p.readAsArrayBuffer(n)
  432. };
  433. l.readAsText(k)
  434. };
  435. e.readAsText(h)
  436. }
  437. };
  438. d.send()
  439. }.bind(this))
  440. }
  441.  
  442. async function addImageToPDF(meta, imageUrls, basicUrl) {
  443. let pdf;
  444. for (let i = 0; i < imageUrls.length; i++) {
  445. try {
  446. let url = imageUrls[i].n[0].split('?')[0]
  447. url += `?x-oss-process=image/sharpen,100`
  448. if (url.includes('files/large/')) {
  449. url = url.replace('../', '')
  450. } else {
  451. url = 'files/large/' + url
  452. }
  453. url = basicUrl + url;
  454. console.log(`第${i + 1}页 url:`, url)
  455. const img = await loadImage(url);
  456. const imgWidth = img.width;
  457. const imgHeight = img.height;
  458. if (!pdf) {
  459. pdf = new jspdf.jsPDF({
  460. orientation: imgWidth / imgHeight > 1 ? 'l' : 'p',
  461. unit: 'px',
  462. format: [imgWidth, imgHeight],
  463. compress: true
  464. });
  465. }
  466. if (i !== 0) {
  467. pdf.addPage({
  468. format: [imgWidth, imgHeight],
  469. });
  470. }
  471. /*const pageWidth = pdf.internal.pageSize.width;
  472. const pageHeight = pdf.internal.pageSize.height;
  473.  
  474. // 计算缩放比例
  475. const ratio = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
  476. const newWidth = imgWidth * ratio;
  477. const newHeight = imgHeight * ratio;*/
  478.  
  479. // 计算居中位置
  480. const x = 0;
  481. const y = 0;
  482. // 确保坐标和尺寸有效
  483. pdf.addImage(img, 'WEBP', x, y, imgWidth, imgHeight);
  484. /*if (newWidth > 0 && newHeight > 0 && x >= 0 && y >= 0) {
  485. pdf.addImage(img, 'WEBP', x, y, newWidth, newHeight);
  486. } else {
  487. console.error('Invalid dimensions or coordinates:', { newWidth, newHeight, x, y });
  488. }*/
  489. /*if(i!==imageUrls.length-1){
  490. pdf.addPage()
  491. }*/
  492. if (Swal && i > 10) {
  493. Swal.fire({
  494. position: 'top-end', //定位 左上角
  495. icon: 'success',
  496. title: `加载中,第${i + 1}页`,
  497. showConfirmButton: false,
  498. })
  499. }
  500. } catch (e) {
  501. if (Swal) {
  502. Swal.fire({
  503. icon: 'error',
  504. title: `下载失败,${e.message}`,
  505. showConfirmButton: true,
  506. })
  507. }
  508. }
  509.  
  510. }
  511. return pdf;
  512. }
  513.  
  514. function loadImage(url) {
  515. return new Promise((resolve, reject) => {
  516. const img = new Image();
  517. img.crossOrigin = 'anonymous'; // 处理跨域问题
  518. img.src = url;
  519. img.onload = () => {
  520. /*const canvas = document.createElement('canvas');
  521. canvas.width = img.width;
  522. canvas.height = img.height;
  523. const ctx = canvas.getContext('2d');
  524. ctx.drawImage(img, 0, 0);*/
  525. // resolve(canvas.toDataURL('image/webp'));
  526. resolve(img);
  527. };
  528. img.onerror = reject;
  529. });
  530. }
  531. function sleep(ms){
  532. return new Promise(resolve => setTimeout(resolve, ms));
  533. }
  534. }
  535. // Your code here...
  536. })();

QingJ © 2025

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