之前我读过不少站内关于 PDF 打印的文章,一开始为了格式清爽,而选择了剪藏到 Obsidian,再导出为 PDF 打印。我写成文分享后,还是觉得流程繁琐,况且需要借助 Obsidian,就能拦掉一批人,费时又费力。
今天我就思考,有没有更好的解决方法。一开始我折腾 Microsoft Edge 的阅读模式,又下载插件,换浏览器,最后逛 UTGD 时,看到了一个完美的解决思路:借助小书签(Bookmarklet)来调整文章格式!
我简要介绍一下小书签,它是一种特殊的浏览器书签,点击后就执行 JavaScript 代码,能直接对当前页面进行操作。它的优势是,支持几乎所有主流浏览器,无需下载,比插件更方便,还能自行修改,满足需求。
使用方法很简单:直接复制小书签,随后在浏览器随便添加一个网页书签,把网址替换成 javascript 代码即可。如果觉得不需要,刷新网页就能恢复原状。
理论说到这里,下面说解决方案。
1、把网页文章调整成纯净阅读模式。
javascript:(function()%7Bjavascript%3A(function()%7Bjavascript%3A(function()%7Bconsole.log('start')%3Bvar jsCode%3Ddocument.createElement('script')%3BjsCode.setAttribute('src'%2C'https%3A%2F%2Fgcore.jsdelivr.net%2Fgh%2Fmozilla%2Freadability%2FReadability.js')%3Bwindow.cleanHtml%3D(function()%7Bvar loc%3Ddocument.location%3Bvar uri%3D%7Bspec%3Aloc.href%2Chost%3Aloc.host%2CprePath%3Aloc.protocol%2B'%2F%2F'%2Bloc.host%2Cscheme%3Aloc.protocol.substr(0%2Cloc.protocol.indexOf('%3A'))%2CpathBase%3Aloc.protocol%2B'%2F%2F'%2Bloc.host%2Bloc.pathname.substr(0%2Cloc.pathname.lastIndexOf('%2F')%2B1)%7D%3Bvar article%3Dnew Readability(uri%2Cdocument).parse()%3Bvar titleElement%3Ddocument.createElement('h1')%3BtitleElement.textContent%3Darticle.title%3BtitleElement.style.textAlign%3D'center'%3BtitleElement.style.margin%3D'20px 0'%3BtitleElement.style.backgroundColor%3D'transparent'%3BtitleElement.style.border%3D'none'%3BtitleElement.style.padding%3D'0'%3Bvar metadataElement%3Ddocument.createElement('p')%3BmetadataElement.style.textAlign%3D'center'%3BmetadataElement.style.color%3D'%23555'%3BmetadataElement.style.fontSize%3D'12px'%3BmetadataElement.style.margin%3D'-15px 0 25px 0'%3BmetadataElement.style.lineHeight%3D'1.6'%3BmetadataElement.style.wordBreak%3D'break-word'%3Bvar metadataParts%3D%5B%5D%3Bif(article.byline)%7BmetadataParts.push('作者%3A '%2Barticle.byline)%3B%7Dvar linkHTML%3D'原文%3A <a href%3D"'%2Bloc.href%2B'" target%3D"_blank" style%3D"color%3A%23007bff%3Btext-decoration%3Aunderline%3B">'%2Bloc.href%2B'<%2Fa>'%3BmetadataParts.push(linkHTML)%3BmetadataElement.innerHTML%3DmetadataParts.join('<span style%3D"margin%3A0 1em%3B">%7C<%2Fspan>')%3Bvar contentDiv%3Ddocument.createElement('div')%3BcontentDiv.innerHTML%3Darticle.content%3Bdocument.head.innerHTML%3D''%3Bvar cleanStyle%3Ddocument.createElement('link')%3BcleanStyle.setAttribute('href'%2C'https%3A%2F%2Fgcore.jsdelivr.net%2Fgh%2Frunningcheese%2FAwesome-Bookmarklets%2Flibs%2Fclean.css')%3BcleanStyle.setAttribute('rel'%2C'stylesheet')%3Bdocument.head.appendChild(cleanStyle)%3Bvar customStyles%3Ddocument.createElement('style')%3BcustomStyles.type%3D'text%2Fcss'%3Bvar styleText%3D'blockquote %7B color%3A %23666%3B display%3A block%3B margin%3A 0 0 16px%3B padding%3A 0 20px%3B border-left%3A %23ddd solid 4px%3B %7D h1%2C h2%2C h3%2C h4%2C h5%2C h6 %7B color%3A %23000000 !important%3B %7D body %7B color%3A %23000000 !important%3B font-size%3A 18px !important%3B line-height%3A 1.7 !important%3B %7D'%3BcustomStyles.appendChild(document.createTextNode(styleText))%3Bdocument.head.appendChild(customStyles)%3Bdocument.body.innerHTML%3D''%3Bdocument.body.appendChild(titleElement)%3Bdocument.body.appendChild(metadataElement)%3Bdocument.body.appendChild(contentDiv)%3B%7D)%3BjsCode.onload%3DcleanHtml%3Bdocument.body.appendChild(jsCode)%3B%7D)()%7D)()%7D)()
作用:文章开启阅读模式,有标题和文章来源,字号为 18px,美化引用样式。
我以前保存了一个【阅读模式】小书签,但它开启后,不显示正文标题。我借助 AI 完善后,发现不少需要改善的地方:
- 标题下我希望显示作者和原文链接,方便打印后溯源;
- 文字颜色比较淡,导致打印出后来字迹不清;
- 文章字号较小,打印在 A5 小册子上阅读比较费眼;
- 引用样式只是两段缩进字符,但不够醒目;
- 小书签无法应用在公众号文章上(因其封闭、自成一体的设计)。
中间我跟 AI 对话了几十次,遇到许多问题,最后完美解决了上述问题(除了公众号文章),再导出为 PDF 即可。
现在你把上面 javascript 开头的小书签,添加到浏览器后,点开一篇文章,也就拥有了一个纯净的阅读模式,无需借助插件和 Obsidian。
应用前与应用后(但放弃了banner图)
2、公众号的解决方案:字体调大、颜色为黑。
javascript:(function()%7Bjavascript%3A(function()%7Bvar id%3D'font-styler-bookmarklet'%3Bif(document.getElementById(id))%7Balert('样式已调整!')%3Breturn%3B%7Dvar css%3D%60%23js_content%2C%23js_content p%2C%23js_content span%7Bfont-size%3A18px !important%3Bcolor%3A%23000000 !important%3B%7D%60%3Bvar style%3Ddocument.createElement('style')%3Bstyle.id%3Did%3Bstyle.appendChild(document.createTextNode(css))%3Bdocument.head.appendChild(style)%3Balert('正文样式已调整!')%3B%7D)()%7D)()
作用:网页文字全部变成黑色,字号为 18px。
上述提到,由于公众号的封闭,小书签无法应用。我最后想到,许多时候公众号文章需要调整,是因为原文排版字体不是纯黑色,导致打印出来字迹不清,而且字号太小,无法调整。
那问题就简单了,我只需要小书签能帮我把所有文字改为黑色,字号设置为 18px 就够了。应用之后,文章格式就会自动调整。
应用前与应用后
下图为打印对比:
如此一来,公众号文章打印后更适合纸质阅读,也不劳烦其他工具来帮忙了。
3、点击就能缩小网页图片。
javascript:(function()%7Bjavascript%3A(function() %7B if (window.imgShrinkActive) %7B alert('图片缩小模式已激活!')%3B return%3B %7D window.imgShrinkActive %3D true%3B function shrinkImg(e) %7B e.preventDefault()%3B e.stopPropagation()%3B const img %3D e.target%3B const minWidth %3D 50%3B let curWidth %3D img.offsetWidth%3B let newWidth %3D curWidth * 0.9%3B if (newWidth < minWidth) %7B newWidth %3D minWidth%3B %7D img.style.setProperty('width'%2C newWidth %2B 'px'%2C 'important')%3B img.style.setProperty('height'%2C 'auto'%2C 'important')%3B if (newWidth %3D%3D%3D minWidth) %7B alert('图片已缩小到最小尺寸')%3B %7D %7D document.querySelectorAll('img').forEach(img %3D> %7B img.style.cursor %3D 'pointer'%3B img.addEventListener('click'%2C shrinkImg%2C true)%3B %7D)%3B alert('图片缩小模式已开启,点击任意图片即可逐步缩小!')%3B %7D)()%7D)()
作用:启用后,每点击一次图片,就会缩小 10%,直到 50px 最小尺寸。
目前,格式问题解决了,但图片还是个难题。因为不少文章图片比较大,打印出来很占地方,之前就有朋友问有没有方法。
今天我想到也能用小书签来解决,于是让 AI 来协助完成,中间也遇到点击图片后,因为原有设计,自动铺满全屏的情况,接着优化几次,总算解决了。
4、字体调整为「思源宋体 CN」。
javascript:(function()%7Bjavascript%3A(function()%7Bvar style %3D document.createElement('style')%3Bstyle.innerHTML %3D %60%40import url('https%3A%2F%2Ffonts.googleapis.com%2Fcss2%3Ffamily%3DNoto%2BSerif%2BSC%3Awght%40500%3B900%26display%3Dswap')%3Bbody%2C p%2C div%2C span%2C a%2C li%2C td%2C th %7Bfont-family%3A 'Noto Serif SC'%2C serif !important%3Bfont-weight%3A 500 !important%3B%7Dh1%2C h2%2C h3%2C h4%2C h5%2C h6%2C b%2C strong %7Bfont-family%3A 'Noto Serif SC'%2C serif !important%3Bfont-weight%3A 900 !important%3B%7D%60%3Bdocument.head.appendChild(style)%3B%7D)()%7D)()
作用:将网页正文字体设置为「思源宋体 CN Medium」,标题及粗体为「思源宋体 CN Heavy」,需要提前安装字体。
这是个人偏好,我觉得思源宋体比较耐看,以及我之前打印 UTGD 文章时,就发现粗体和标题的字重都没有变化,因此额外增添一条,诸君自取。
5、删除多余元素。
javascript: (function%20()%20%7B%20%20%20var%20isIe%20%3D%20false%3B%20%20%2F*%40cc_on%20isIe%3Dtrue%3B%20%40*%2F%20%20function%20fe(a%2C%20fn)%20%7B%20%20%20%20%20var%20i%2C%20l%20%3D%20a.length%3B%20%20%20%20%20for%20(i%20%3D%200%3B%20i%20%3C%20l%3B%20i%2B%2B)%20%7B%20fn(a%5Bi%5D)%3B%20%7D%20%20%20%7D%3B%20%20%20function%20ae(el%2C%20n%2C%20fn%2C%20ix)%20%7B%20%20%20%20%20function%20wfn(ev)%20%7B%20%20%20%20%20%20%20var%20el%20%3D%20(isIe%20%3F%20window.event.srcElement%20%3A%20ev.target)%3B%20%20%20%20%20%20%20if%20(n%20%3D%3D%20%27click%27)%20%7B%20%20%20%20%20%20%20%20ev.stopPropagation()%3B%20%20%20%20%20%20%20%20ev.preventDefault()%3B%20%20%20%20%20%20%20%7D%20%20%20%20%20%20if%20(ix%20%7C%7C%20!el.xmt)%20%7Bfn(el)%3B%7D%20%20%20%20%20%7D%20%20%20%20%20if%20(isIe)%20%7B%20%20%20%20%20%20%20n%20%3D%20%27on%27%20%2B%20n%3B%20el.attachEvent(n%2C%20wfn)%3B%20%20%20%20%20%7D%20else%20%7B%20%20%20%20%20%20%20var%20isUseCapture%20%3D%20(n%20%3D%3D%20%27click%27)%3B%20%20%20%20%20%20el.addEventListener(n%2C%20wfn%2C%20isUseCapture)%3B%20%7D%20%20%20%20%20if%20(!el.es)%20el.es%20%3D%20%5B%5D%3B%20%20%20%20%20el.es.push(%20%20%20%20%20%20function%20()%20%7B%20%20%20%20%20%20%20%20%20if%20(isIe)%20%7B%20el.detachEvent(n%2C%20wfn)%3B%20%7D%20%20%20%20%20%20%20%20%20else%20%7B%20el.removeEventListener(n%2C%20wfn%2C%20isUseCapture)%3B%20%7D%20%20%20%20%20%20%20%7D)%3B%20%20%20%20%20el.re%20%3D%20function%20()%20%7B%20fe(el.es%2C%20function%20(f)%20%7B%20f()%20%7D)%3B%20%7D%3B%20%20%20%7D%3B%20%20function%20sce(el)%20%7B%20%20%20%20%20var%20oldclick%20%3D%20el.onclick%2C%20oldmu%20%3D%20el.onmouseup%2C%20oldmd%20%3D%20el.onmousedown%3B%20%20%20%20%20el.onclick%20%3D%20function%20()%20%7B%20return%20false%3B%20%7D%3B%20%20%20%20%20el.onmouseup%20%3D%20function%20()%20%7B%20return%20false%3B%20%7D%3B%20%20%20%20%20el.onmousedown%20%3D%20function%20()%20%7B%20return%20false%3B%20%7D%3B%20%20%20%20%20el.rce%20%3D%20function%20()%20%7B%20el.onclick%20%3D%20oldclick%3B%20el.onmouseup%20%3D%20oldmu%3B%20el.onmousedown%20%3D%20oldmd%3B%20%7D%3B%20%20%20%7D%20%20%20if%20(!window.r_)%20window.r_%20%3D%20%5B%5D%3B%20%20%20var%20r%20%3D%20window.r_%3B%20%20%20var%20D%20%3D%20document%3B%20%20%20ae(D.body%2C%20%27mouseover%27%2C%20function%20(el)%20%7B%20el.style.backgroundColor%20%3D%20%27%23ffff99%27%3B%20sce(el)%20%7D)%3B%20%20%20ae(D.body%2C%20%27mouseout%27%2C%20function%20(el)%20%7B%20el.style.backgroundColor%20%3D%20%27%27%3B%20if%20(el.rce)%20el.rce()%3B%20%7D)%3B%20%20%20ae(D.body%2C%20%27click%27%2C%20function%20(el)%20%7B%20%20%20%20el.style.display%20%3D%20%27none%27%3B%20%20%20%20%20r.push(el)%3B%20%20%20%7D)%3B%20%20%20function%20ac(p%2C%20tn%2C%20ih)%20%7B%20var%20e%20%3D%20D.createElement(tn)%3B%20%20%20%20%20if%20(ih)%20e.innerHTML%20%3D%20ih%3B%20%20%20%20%20p.appendChild(e)%3B%20%20%20%20%20return%20e%3B%20%20%20%7D%20%20%20var%20p%20%3D%200%3B%20%20%20var%20bx%20%3D%20ac(D.body%2C%20%27div%27)%3B%20%20%20bx.style.cssText%20%3D%20%27position%3A%27%20%2B%20(isIe%20%3F%20%27absolute%27%20%3A%20%27fixed%27)%20%2B%20%27%3Bpadding%3A2px%3Bbackground-color%3A%2399FF99%3Bborder%3A1px%20solid%20green%3Bz-index%3A9999%3Bfont-family%3Asans-serif%3Bfont-size%3A10px%27%3B%20%20%20function%20sp()%20%7B%20%20%20%20%20bx.style.top%20%3D%20(p%20%26%202)%20%3F%20%27%27%20%3A%20%2710px%27%3B%20%20%20%20%20bx.style.bottom%20%3D%20(p%20%26%202)%20%3F%20%2710px%27%20%3A%20%27%27%3B%20%20%20%20%20bx.style.left%20%3D%20(p%20%26%201)%20%3F%20%27%27%20%3A%20%2710px%27%3B%20%20%20%20%20bx.style.right%20%3D%20(p%20%26%201)%20%3F%20%2710px%27%20%3A%20%27%27%3B%20%20%20%7D%20sp()%3B%20%20%20var%20ul%20%3D%20ac(bx%2C%20%27a%27%2C%20%27%20Undo%20%7C%27)%3B%20%20%20ae(ul%2C%20%27dblclick%27%2C%20function%20()%20%7B%20var%20e%20%3D%20r.pop()%3B%20if%20(e)%20e.style.display%20%3D%20%27%27%3B%20%7D%2C%20true)%3B%20%20%20var%20ual%20%3D%20ac(bx%2C%20%27a%27%2C%20%27%20Undo%20All%20%7C%27)%3B%20%20%20ae(ual%2C%20%27dblclick%27%2C%20function%20()%20%7B%20var%20e%3B%20while%20(e%20%3D%20r.pop())%20e.style.display%20%3D%20%27%27%3B%20%7D%2C%20true)%3B%20%20%20var%20ml%20%3D%20ac(bx%2C%20%27a%27%2C%20%27%20Move%20%7C%27)%3B%20%20%20ae(ml%2C%20%27dblclick%27%2C%20function%20()%20%7B%20p%2B%2B%3B%20sp()%3B%20%7D%2C%20true)%3B%20%20%20var%20xl%20%3D%20ac(bx%2C%20%27a%27%2C%20%27%20Exit%20%27)%3B%20%20%20ae(xl%2C%20%27dblclick%27%2C%20function%20()%20%7B%20D.body.re()%3B%20bx.parentNode.removeChild(bx)%3B%20%7D%2C%20true)%3B%20%20%20fe(%5Bbx%2C%20ul%2C%20ml%2C%20xl%2C%20ual%5D%2C%20function%20(e)%20%7B%20e.style.cursor%20%3D%20%27pointer%27%3B%20e.xmt%20%3D%201%3B%20%7D)%3B%20%7D)()
源于 Minja 的文章《剪藏网页到本地的轻量化思路》,由网友 Flez 提供的优化版本。
作用:启用后,就能任意删除网页元素。
简单一用,鼠标点点,就能删掉文章里的无用元素,比如二维码、自我介绍、多余图片,最后留下干净清爽的页面。
最后
我迭代了十几个版本,最后形成了这套打印前的简化流程。这么操作下来,不管是阅读模式,还是调整图片,删改元素,都不需要任何插件。
而且你想修改哪里,随时能让 AI 来帮你,比如字号、颜色、字体等、总能调整到满意为止。
这时打印出来的文章,格式也跟我之前用 Obsidian 导出打印没什么两样,甚至更加快速简单。
至此,文章打印格式的折腾,也能暂且告一段落,希望这篇文章对你有用,少点折腾,一步到位。