diff --git a/README.md b/README.md index d0b8373..595e819 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ https://github.com/027xiguapi/code-box 2. [plasmo](https://github.com/PlasmoHQ/plasmo) 3. [Chrome 应用商店一站式支持](https://support.google.com/chrome_webstore/contact/one_stop_support) 4. [开箱即用的web打印和下载](https://juejin.cn/post/7412672713376497727) +5. [导出微信公众号文章为PDF](https://greasyfork.org/en/scripts/510683-%E5%AF%BC%E5%87%BA%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E6%96%87%E7%AB%A0%E4%B8%BApdf/code) ## 隐私政策 diff --git a/component/items/downloadPdf.tsx b/component/items/downloadPdf.tsx new file mode 100644 index 0000000..5b184ea --- /dev/null +++ b/component/items/downloadPdf.tsx @@ -0,0 +1,25 @@ +import { DownloadOutlined, StarTwoTone } from "@ant-design/icons" + +import { sendToContentScript } from "@plasmohq/messaging" + +import { i18n } from "~tools" + +export default function DownloadPdf(props) { + let { name } = props + + async function downloadPdf() { + sendToContentScript({ + name: `${name}-downloadPdf` + }) + } + + return ( +
+ + + {i18n("downloadPdf")} + + +
+ ) +} diff --git a/component/options/weixin.tsx b/component/options/weixin.tsx index 9137c6f..a325770 100644 --- a/component/options/weixin.tsx +++ b/component/options/weixin.tsx @@ -1,6 +1,7 @@ import CssCode from "~component/items/cssCode" import DownloadHtml from "~component/items/downloadHtml" import DownloadMarkdown from "~component/items/downloadMarkdown" +import DownloadPdf from "~component/items/downloadPdf" import EditMarkdown from "~component/items/editMarkdown" import ShowTag from "~component/items/showTag" import { i18n } from "~tools" @@ -14,6 +15,7 @@ export default function Weixin() { + ) } diff --git a/contents/weixin.tsx b/contents/weixin.tsx index 35a3435..6e696b4 100644 --- a/contents/weixin.tsx +++ b/contents/weixin.tsx @@ -1,3 +1,4 @@ +import html2pdf from "html2pdf.js" import type { PlasmoCSConfig, PlasmoCSUIProps, @@ -6,7 +7,6 @@ import type { PlasmoGetStyle } from "plasmo" import { useEffect, type FC } from "react" -import { v4 as uuidv4 } from "uuid" import { useMessage } from "@plasmohq/messaging/hook" import { useStorage } from "@plasmohq/storage/dist/hook" @@ -17,7 +17,7 @@ import { useContent } from "~utils/editMarkdownHook" import Turndown from "~utils/turndown" export const config: PlasmoCSConfig = { - matches: ["https://mp.weixin.qq.com/*"] + matches: ["https://mp.weixin.qq.com/s/*"] } const turndownService = Turndown() @@ -69,8 +69,152 @@ const PlasmoOverlay: FC = ({ anchor }) => { if (req.name == "weixin-downloadHtml") { downloadHtml() } + if (req.name == "weixin-downloadPdf") { + downloadPdf() + } }) + console.log(777, html2pdf) + + function downloadPdf() { + // 获取文章内容和标题、作者、时间等元信息 + var article = document.querySelector(".rich_media_content") + var title = document.querySelector(".rich_media_title") + var author = document.querySelector(".weui-wa-hotarea") // 文章作者 + var publishTime = document.querySelector("#publish_time") // 文章时间 + + if (article) { + // 创建一个容器用于添加元信息 + var metaInfoDiv = document.createElement("div") + metaInfoDiv.style.marginBottom = "20px" + metaInfoDiv.style.borderBottom = "1px solid #eee" + metaInfoDiv.style.paddingBottom = "15px" + + // 标题 + var titleElement = document.createElement("h1") + titleElement.innerText = title ? title?.innerText.trim() : "未命名文章" + titleElement.style.fontSize = "24px" + titleElement.style.marginBottom = "10px" + metaInfoDiv.appendChild(titleElement) + + // 作者 + if (author) { + var authorElement = document.createElement("p") + authorElement.innerText = "作者: " + author.innerText.trim() + authorElement.style.fontSize = "14px" + authorElement.style.margin = "5px 0" + metaInfoDiv.appendChild(authorElement) + } + + // 时间 + if (publishTime) { + var timeElement = document.createElement("p") + timeElement.innerText = "发布时间: " + publishTime.innerText.trim() + timeElement.style.fontSize = "14px" + timeElement.style.margin = "5px 0" + metaInfoDiv.appendChild(timeElement) + } + + // 将元信息插入到文章内容的顶部 + article.insertBefore(metaInfoDiv, article.firstChild) + + // 添加防止图片分页的CSS样式 + var style = document.createElement("style") + style.innerHTML = ` + .rich_media_content img { + page-break-inside: avoid; + break-inside: avoid; + max-width: 100%; + height: auto; + } + .rich_media_content p, .rich_media_content div { + page-break-inside: avoid; + break-inside: avoid; + } + ` + document.head.appendChild(style) + + // 确保所有图片加载完成 + let images = article.querySelectorAll("img") + let imagePromises = [] + + images.forEach(function (img) { + // 处理懒加载的图片,确保图片的真实 URL 被加载 + if (img.dataset && img.dataset.src) { + img.src = img.dataset.src + } + + // 通过跨域获取图片,并将图片转换为 base64 格式 + imagePromises.push( + new Promise(function (resolve) { + var imgElement = new Image() + imgElement.crossOrigin = "Anonymous" + imgElement.src = img.src + imgElement.onload = function () { + var canvas = document.createElement("canvas") + canvas.width = imgElement.width + canvas.height = imgElement.height + var ctx = canvas.getContext("2d") + ctx.drawImage(imgElement, 0, 0) + img.src = canvas.toDataURL("image/jpeg") // 使用JPEG格式并压缩质量到70% + resolve() + } + imgElement.onerror = resolve // 即使图片加载失败,继续处理 + }) + ) + }) + + // 确保图片加载完成后再导出PDF + Promise.all(imagePromises) + .then(function () { + // 使用文章标题作为文件名 + var fileName = title + ? title.innerText.trim() + ".pdf" + : "WeChat_Article.pdf" + + var opt = { + margin: 0.5, + filename: fileName, + image: { + type: "jpeg", + quality: 1 // 降低图片质量以减小PDF体积 + }, + html2canvas: { + scale: 1.5, // 降低渲染比例以减小PDF体积 + useCORS: true, // 允许跨域图片 + logging: false // 关闭日志 + // 可以根据需要添加其他html2canvas选项 + }, + jsPDF: { + unit: "in", + format: "a4", // 使用A4格式,比letter更常用且体积可能更小 + orientation: "portrait" + }, + pagebreak: { + mode: ["avoid-all", "css", "legacy"] + } // 遵循CSS中的page-break规则 + } + + // 使用 html2pdf 将文章内容导出为 PDF + html2pdf() + .from(article) + .set(opt) + .save() + .then(function () {}) + .catch(function (error) { + alert("导出过程中出现问题: " + error.message) + console.log(error) + }) + }) + .catch(function (error) { + alert("处理图片时出现问题: " + error.message) + console.log(error) + }) + } else { + alert("未找到文章内容") + } + } + function downloadMarkdown() { const html = document.querySelector("#img-content") const markdown = turndownService.turndown(html) diff --git a/package.json b/package.json index 02acfda..5ba0418 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-box", "displayName": "__MSG_extensionName__", - "version": "0.9.14", + "version": "0.9.15", "description": "__MSG_extensionDescription__", "author": "027xiguapi. <458813868@qq.com>", "scripts": { @@ -33,6 +33,7 @@ "dayjs": "^1.11.12", "file-saver": "^2.0.5", "html2canvas": "^1.4.1", + "html2pdf.js": "^0.10.2", "jspdf": "^2.5.2", "jssha": "^3.3.1", "plasmo": "0.89.4",