如何使用JavaScript构建基于浏览器的PDF颜色反转工具
长时间阅读PDF文档会让人感到疲劳,尤其是当文档的背景颜色很鲜艳,或者你在光线较暗的环境中阅读时。 在某些情况下,设计师、开发人员和印刷专业人士可能希望查看文档在颜色反转后的效果,或者为便于访问或审查而准备不同的版本。 PDF颜色反转工具能够实现这一目标——它在保持文档结构不变的前提下,改变PDF页面的颜色。用户无需手动编辑每一张图片或图形,只需上传PDF文件,选择颜色反转模式,指定需要处理的页面范围,调整输出质量,然后点击几下即可生成新的PDF文件并下载。 在本教程中,你将使用JavaScript制作一个基于浏览器的PDF颜色反转工具。用户可以上传PDF文件,预览其内容,选择反转方式,指定处
长时间阅读PDF文档会让人感到疲劳,尤其是当文档的背景颜色很鲜艳,或者你在光线较暗的环境中阅读时。
在某些情况下,设计师、开发人员和印刷专业人士可能希望查看文档在颜色反转后的效果,或者为便于访问或审查而准备不同的版本。
PDF颜色反转工具能够实现这一目标——它在保持文档结构不变的前提下,改变PDF页面的颜色。用户无需手动编辑每一张图片或图形,只需上传PDF文件,选择颜色反转模式,指定需要处理的页面范围,调整输出质量,然后点击几下即可生成新的PDF文件并下载。
在本教程中,你将使用JavaScript制作一个基于浏览器的PDF颜色反转工具。用户可以上传PDF文件,预览其内容,选择反转方式,指定处理范围,调整输出质量,实时查看效果,生成反转后的PDF文件,检查结果,重命名输出文件并下载它——所有这些操作都不需要将文档上传到服务器上。
我们将使用PDF.js在浏览器中显示PDF页面,利用HTML Canvas API来修改像素颜色,再通过PDF-lib生成最终的PDF文件。
完成本教程后,你将会拥有一个功能完备的客户端PDF颜色反转工具,它的效果与我在“All In One Tools”网站上提供的工具类似。
目录
这个PDF颜色反转工具的作用及工作原理
PDF颜色反转工具通过反转PDF页面的颜色来改变文档的外观:浅色会变成深色,深色会变成浅色,所有像素都会被重新计算,从而生成原始文档的反转版本。这种功能在某些环境中可以提高可读性,帮助用户在暗模式下预览设计内容,或者为查看文档提供另一种方式。
在这个项目中,用户可以上传PDF文件,浏览其中的每一页,选择颜色反转的方式,指定需要处理的页面范围,选择输出文件的质量,开启实时预览功能,生成反转后的文档,重新命名输出文件,然后下载最终的PDF文件。由于所有的处理操作都是在浏览器内部进行的,因此原始文档始终不会离开用户的设备。
在后台,PDF.js会将每一页PDF内容渲染到HTML画布上。一旦某页被渲染完成,JavaScript就会通过Canvas API访问这些像素数据,然后重新计算每个像素的红色、绿色和蓝色数值,从而生成该页面的反转版本。
在处理完所有选中的页面后,PDF-lib会将这些修改后的页面合并成一个新的PDF文件,用户可以预览并下载这个新文件。
一个像素由四个数值来表示:
const pixel = {
red: 120,
green: 85,
blue: 200,
alpha: 255
};
在进行颜色反转操作时,每个颜色通道的值都会被减去255,从而实现颜色的反转。
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
对每一页上所有的像素都重复进行这样的计算,最终就能得到反转后的PDF文件,同时文档的布局、页面顺序和尺寸也会保持不变。
项目设置
在编写任何图像处理代码之前,我们先来为这个PDF颜色反转工具创建一个简单的项目结构吧。
我们将使用纯HTML、CSS和JavaScript,再加上两个第三方库来构建这个应用程序:
PDF.js——用于在浏览器中渲染PDF页面。
PDF-lib——用于生成最终的反转PDF文件。
我们的项目结构如下所示:
pdf-color-inverter/
│── index.html
│── style.css
│── script.js
│── pdf.worker.min.js
│── assets/
将各个组件分开存放,可以使应用程序更易于理解和维护。
在加载自己的JavaScript代码之前,请先引入所需的第三方库。
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.4.168/pdf.min.js"></script>
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js"></script>
<script src="script.js"></script>
需要配置PDF.js的工作进程。
pdfjsLib.GlobalWorkerOptions.workerSrc =
"pdf.worker.min.js";
在后台进程中运行PDF.js,可以在渲染大型PDF文件时保持浏览器的响应速度。
创建HTML布局
这个应用程序包含四个主要的组成部分:
上传区域
设置面板
PDF预览功能
结果展示区
首先来创建这些基本的结构吧。
<section id="uploadSection"></section>
<section id="settingsSection" hidden></section>
<section id="previewSection" hidden></section>
<section id="resultSection" hidden></section>
页面首次加载时,只有上传区域是可见的。
在选择了一个PDF文件后,其余的部分才会变得可用。
选择主要元素
在应用程序中保存对各种元素的引用。
const uploadSection =
document.getElementById(
"uploadSection"
);
const settingsSection =
document.getElementById(
"settingsSection"
);
const previewSection =
document.getElementById(
"previewSection"
);
const resultSection =
document.getElementById(
"resultSection"
);
const pdfCanvas =
document.getElementById(
"pdfCanvas"
);
这些引用使得在工作流程的不同阶段之间切换变得非常方便。
上传和预览PDF文件
上传区域支持拖放操作,也支持手动选择文件。
当选择了一个文件后,需要确认该文件确实是PDF格式。
async function handleUpload(
file
) {
if (
!file ||
file.type !==
"application/pdf"
) {
alert(
"请选择一个PDF文件。"
);
return;
}
await loadPdf(file);
}
如果文件通过了验证,浏览器会将其加载到内存中。
支持受密码保护的PDF文件
有些文档是受到密码保护的。
在开始处理文件之前,上传界面会提供一个可选的密码输入框。
系统会获取用户输入的密码。
const password =
document
.getElementById(
"pdfPassword"
)
.value
.trim();
在加载PDF文件时,需要将这个密码传递给PDF.js库。
const loadingTask =
pdfjsLib.getDocument、
data: pdfBytes,
password
);
pdfDocument =
await loadingTask.promise;
如果文档没有设置密码保护,那么密码输入框就可以保持为空状态。
加载PDF文件
需要将上传的文件转换成ArrayBuffer格式。
async function loadPdf(
file
) {
originalPdfBytes =
await file.arrayBuffer();
pdfDocument =
await pdfjsLib
.getDocument、
data:
originalPdfBytes
)
.promise;
currentPage = 1;
await renderPage(
currentPage
);
}
原始字节会被保留下来,因为后续会使用这些字节来生成反转后的PDF文件。
渲染PDF页面
PDF.js是一次只渲染一个页面。
获取用户请求的页面内容。
async function renderPage(
pageNumber
) {
const page =
await pdfDocument.getPage(
pageNumber
);
const viewport =
page.getViewport({
scale: 1.5
});
调整画布的大小。
pdfCanvas.width =
viewport.width;
pdfCanvas.height =
viewport.height;
渲染该页面。
await page.render、
canvasContext:
pdfCanvas.getContext(
"2d"
),
viewport
}).promise;
当渲染完成后,所选页面就会在预览区域中显示出来。
在页面之间导航
大多数文档都包含多个页面,因此预览界面会提供“上一页”和“下一页”按钮。
创建当前页面的状态信息。
let currentPage = 1;
let pdfDocument = null;
跳转到上一页。
previousButton
.addEventListener(
"click",
async () => {
if (
currentPage > 1
) {
currentPage--;
await renderPage(
currentPage
);
}
});
跳转到下一页。
nextButton
.addEventListener(
"click",
async () => {
if (
currentPage < pdfDocument.numPages
) {
currentPage++;
await renderPage(
currentPage
);
}
});
更新页面显示索引。
pageNumber.textContent =
`第${currentPage}页,共${pdfDocument.numPages}页`;
这样用户就可以在选择哪些页面需要反转颜色之前,先浏览整个文档内容。
设置颜色反转选项
在处理PDF文件之前,用户应该能够自行控制颜色的反转方式。设置面板允许用户选择反转模式、指定需要处理的页面范围、选择输出质量、开启实时预览功能,或者重置所有设置并重新开始操作。

选择反转模式
第一个选项允许用户指定文档颜色应如何被反转。
创建一个下拉列表。
<select id="invertMode">
读取所选择的模式。
const inversionMode = document.getElementById("invertMode").value;
所选的设置决定了在处理文档时应用哪种颜色变换方式。
选择页面范围
有时用户只需要反转部分页面,而不是整个文档。
创建两个输入字段用于指定页面范围。
<>input type="number" id="startPage" min="1">
获取用户选择的页面范围。
const startPage = Number(document.getElementById("startPage").value); const endPage = Number(document.getElementById("endPage").value);
在生成最终PDF时,只有这个范围内指定的页面才会被处理。
选择输出质量
该工具提供了多种质量等级,让用户能够根据需要平衡图像质量和文件大小。
创建一个质量选择器。
<>select id="outputQuality">
读取所选择的输出质量设置。
const quality = document.getElementById("outputQuality").value;
所选的输出质量将决定生成的新PDF的图像质量。
启用实时预览功能
实时预览功能允许用户在不生成新PDF的情况下立即查看颜色反转后的效果。
创建一个用于切换此功能的控件。
<>input type="checkbox" id="livePreview">
读取该控件的当前状态。
const livePreview = document.getElementById("livePreview").checked;
每当用户更改设置时,都需要重新刷新预览页面。
livePreviewToggle.addEventListener("change", updatePreview);
当实时预览功能被启用后,每当用户调整反转设置时,预览画面会自动更新。
重置设置
“重置”按钮会清除当前的配置,这样用户就可以在不重新加载页面的情况下开始操作。
创建这个按钮。
<button id="resetButton">
重置/清除
</button>
将设置恢复为默认值。
function resetSettings() {
invertMode.value =
"full";
outputQuality.value =
"high";
livePreview.checked =
false;
}
为这个按钮添加事件监听器。
resetButton
.addEventListener(
"click",
resetSettings
);
执行此操作后,设置面板会恢复到初始状态。
开始颜色反转功能
在确认设置无误后,用户就可以开始处理文档了。
创建这个操作按钮。
<button id="invertPdf">
反转PDF页面的颜色
<>/button>
启动颜色反转过程。
invertButton
.addEventListener(
"click",
async () => {
await invertPdf();
});
反转PDF页面的颜色
现在我们来实现这个应用程序的核心功能:反转每页PDF的颜色。
操作流程非常简单。首先,PDF.js会将页面内容渲染到HTML画布上;接着,JavaScript会读取画布上的每个像素,将其红色、绿色和蓝色的数值进行反转,然后再将这些更新后的像素写回画布中;最后,使用PDF-lib将处理后的页面生成为新的PDF文件。
读取画布上的像素数据
一旦页面内容被渲染出来,就可以获取其对应的像素信息了。
const imageData =
context.getImageData(
0,
0,
canvas.width,
canvas.height
);
每个像素由四个数值组成:
红色
绿色
蓝色
透明度
这些像素数据被存储在一个数组中。
const pixels =
imageData.data;
我们将直接修改这个数组来实现颜色反转的效果。
反转每个像素的颜色
要反转一种颜色的数值,只需将其RGB值从255中减去即可。
需要遍历画布上的每一个像素才能完成这个操作。
for (
let i = 0;
while (i < pixels.length) {
i += 4;
}
pixels[i] =
255 - pixels[i];
pixels[i + 1] =
255 - pixels[i + 1];
pixels[i + 2] =
255 - pixels[i + 2];
}
阿尔法通道保持不变,因此透明元素仍然能够正确显示。
在修改了每个像素之后,需要将修改后的图像重新绘制到画布上。
context.putImageData(
imageData,
0,
0
);
现在,页面预览会显示出颜色被反转后的效果。
更新实时预览效果
如果启用了实时预览功能,用户可以立即看到更改结果,而无需重新生成PDF文件。
请检查该功能是否处于激活状态。
if (
livePreview.checked
) {
await invertCurrentPage();
}
每当用户更改颜色反转模式、页面范围或质量设置时,都需要重新刷新预览效果。
async function updatePreview() {
await renderPage(
currentPage
);
await invertCurrentPage();
}
处理选定的页面
每次不需要处理整个文档,只需反转用户所选中的那些页面即可。
遍历用户选择的页面范围进行操作。
for (
let page = startPage;
page <= endPage;
page++;
) {
await processPage(
page
);
}
每处理完一个页面后,都会将其暂时存储起来,直到最终生成PDF文件时才会将其合并。
生成最终的PDF文件
创建一个新的PDF文档。
const outputPdf =
await PDFLib
.PDFDocument
.create();
将处理后的图像数据转换为图片格式。
const imageBytes =
await canvasToBytes(
pdfCanvas
);
将生成的图片嵌入到PDF文档中。
const image =
await outputPdf
.embedPng(
imageBytes
);
创建一个新的页面,并在该页面上放置图片。
const page =
outputPdf.addPage([
image.width,
image.height
]);
在新的页面上绘制处理后的图像。
page.drawImage(
image,
{
x: 0,
y: 0,
width: image.width,
height: image.height
}
);
对于每一个被选中的页面,都需要重复上述步骤。
保存最终的PDF文件
当所有页面都处理完成后,就需要将文档保存下来。
const pdfBytes =
await outputPdf.save();
将生成的PDF字节数据转换为可供下载的文件格式。
generatedPdfBlob =
new Blob(
[pdfBytes],
{
type:
"application/pdf"
}
);
生成的PDF文件现已可以用于预览和下载了。
预览结果
在下载处理后的文档之前,让用户先查看最终输出内容是非常有用的。这样他们就可以确认所选页面的颜色是否已经被正确反转,以及文档的显示效果是否符合预期。
将生成的PDF文件加载到PDF.js中。
async function showPreview() {
const bytes =
await generatedPdfBlob
.arrayBuffer();
finalPdf =
await pdfjsLib
.getDocument({
data: bytes
})
.promise;
renderFinalPage(1);
}
渲染所选页面。
async function renderFinalPage(
pageNumber
) {
const page =
await finalPdf.getPage(
pageNumber
);
const viewport =
page.getViewport({
scale: 1.5
});
finalCanvas.width =
viewport.width;
finalCanvas.height =
viewport.height;
await page.render、
canvasContext:
finalCanvas
.getContext("2d"),
viewport
}).promise;
}
用户可以在下载之前先浏览处理后的文档内容。
重命名与下载
在保存PDF文件之前,用户可以为其指定自定义文件名。
创建用于输入文件名的字段。
<input
type="text"
id="outputFilename"
value="inverted-document.pdf">
获取用户输入的文件名。
function getFilename() {
let filename =
outputFilename
.value
.trim();
if (!filename) {
filename =
"inverted-document.pdf";
}
if (
!filename
.endsWith(".pdf")
) {
filename += ".pdf";
}
return filename;
}
显示额外的文件信息。
pageCount.textContent =
`${finalPdf.numPages} 页`;
fileSize.textContent =
formatFileSize、
generatedPdfBlob.size
);
下载生成的PDF文件。
downloadButton
.addEventListener(
"click",
() => {
const url =
URL.createObjectURL(
generatedPdfBlob
);
const link =
document.createElement(
"a"
);
link.href =
url;
link.download =
getFilename();
link.click();
URL.revokeObjectURL(
url
);
});
所有操作都在浏览器内部完成,因此用户可以在查看处理后的PDF文件后立即将其下载下来。
演示:PDF颜色反转工具的工作原理
让我们一起来了解整个操作流程。
步骤1:上传PDF文件
用户只需将PDF文件拖放到上传区域,或点击“选择PDF文件”即可。如果文档设置了密码保护,可以在上传前输入密码。
步骤2:预览文档
上传的PDF文件会逐页显示,用户可以在进行任何修改之前先浏览整个文档内容。
步骤3:配置设置选项
用户可以选择需要处理的页面范围、输出文件的质量,以及是否启用实时预览功能。
步骤4:预览反转后的颜色效果
如果启用了实时预览功能,当前页面会立即显示反转后的效果,用户可以提前查看修改后的文档外观。
步骤5:生成PDF文件
点击“反转PDF颜色”按钮后,系统会处理选中的页面,并生成新的PDF文件,其中包含所有经过颜色反转处理的页面。
步骤6:审核并下载文件
生成的PDF文件会在最终预览界面中显示。用户可以重新命名输出文件,查看页数和文件大小,然后下载完成的文档。

性能优化技巧
较大的PDF文件可能需要更长的处理时间。仅渲染当前页面、更新预览内容而非重新加载整个文档,以及仅处理选中的页面范围,这些方法都能显著提升处理效率。
for (
let page = startPage;
page <= endPage;
page++;
) {
await processPage(
page
);
}
下载完成后,请释放临时使用的资源。
URL.revokeObjectURL(
downloadUrl
);
这些小小的优化措施能够帮助应用程序在处理大型PDF文件时依然保持良好的响应速度。
常见错误
一个常见的错误是在没有先渲染PDF页面的新副本的情况下,反复修改原始画布内容。这种操作会导致颜色多次被反转。
在进行任何颜色调整之前,务必先渲染原始页面。
await renderPage(
currentPage
);
另一个容易犯的错误是尝试处理超出有效范围的页码。
if (
pageNumber < 1 ||
pageNumber > pdfDocument.numPages
) {
return;
}
最后需要记住的是,较高的输出质量通常会导致PDF文件体积增大。用户应根据自己的实际需求,选择能够兼顾图像清晰度与文件大小的设置。
总结
通过本教程,您使用JavaScript开发了一个基于浏览器的PDF颜色反转工具。
您学会了如何上传PDF文件、支持加密文件、利用PDF.js渲染页面、通过HTML Canvas API修改像素颜色、使用PDF-lib生成新的PDF文件、预览处理完成的文档、重命名输出文件,并直接从浏览器中下载它。
由于所有处理操作都是在本机进行的,因此用户可以在不将敏感文件上传到外部服务器的情况下完成PDF颜色的反转操作。
您可以通过这个PDF颜色反转工具来体验整个工作流程。
这个项目还可以进一步扩展:添加自定义颜色滤镜、选择性页面预览功能、亮度与对比度调节选项、灰度转换效果、棕褐色处理效果、批量处理功能,或是其他文档优化工具。
相关文章
如何使用JavaScript开发一款基于浏览器的PDF模糊处理工具
许多PDF文件包含不应公开分享的信息。在将文件发送给他人之前,个人资料、财务数据、签名、地址、账号信息、员工资料或机密商业数据通常都需要被隐藏起来。 一款PDF模糊处理工具可以让这一过程变得简单。它并不会永久删除这些内容,而是会在PDF文件的指定区域添加模糊效果,这样一来,敏感信息就会变得难以阅读,而文件的其他部分则保持原样。 在这个教程中,你将使用JavaScript制作一个基于浏览器的PDF模糊处理工具。用户可以上传PDF文件,预览每一页,在敏感内容上标出需要模糊处理的区域,调整模糊程度,对选定的页面应用模糊效果,预览最终结果,然后下载处理后的PDF文件——整个过程都不需要将文件上传到服务
阅读全文
如何在现代API中实施“以隐私保护为导向的设计理念”——开发人员的实用指南
作为软件开发人员,我们通常被教导要优先考虑速度、性能和正常运行时间等因素。在构建API时,我们的核心目标就是确保数据能够顺利地从A点传输到B点。 然而,全球范围内的数据隐私法规正在日益严格,用户也越来越关注自己的数字足迹。将隐私问题视为“事后才需要处理的法律事项”,或者认为可以在生产环境中再解决这些问题,已经不再是一种可持续的做法。 这时,《设计即隐私》这一理念就派上了用场。 “设计即隐私”是一个框架,旨在将隐私保护措施主动融入到工程开发的整个生命周期中。这意味着,你的系统架构应该默认就能保护用户数据。 在这份全面的指南中,我们将通过现代的工程模式、代码设计理念以及有针对性的数据库结构,来讲解
阅读全文
从项目到产品:将平台转化为人们真正会使用的成品
仅仅拥有一个平台是远远不够的;真正的挑战在于确保该平台能够被用户理解、使用,并真正被他们采纳。只有当某种功能能够被他人可靠地使用时,它才算真正实现了其价值。为了评估开发工作的进展,你可以问自己:“这个功能真的被人们在使用吗?”“它是否减轻了用户的使用难度?”这样的思考有助于让开发工作始终与实际的用户需求保持一致,而不仅仅是追求功能的完成。 作者:本·林德斯
阅读全文
如何使用shadcn/ui创建营销着陆页
大多数营销着陆页都会遇到同样的问题:你面对的只是一块空白屏幕,而你需要重新创建那些已经制作过无数次的页面元素。 标题栏、功能展示区、用户评价、价格信息、常见问题解答以及页脚这些内容都是常见的构建模块,但开发人员往往会在每个新项目中花费数小时来重新设计这些部分。 通过本指南,你将学习如何使用Next.js、shadcn/ui、Tailwind CSS以及可复用的shadcn组件来构建现代化的营销着陆页。你无需从零开始创建每一个页面元素,而是可以直接组装出一个可用于实际项目的完整页面,然后根据自己的品牌需求进行定制,从而获得一个适用于各种实际开发场景的基础框架。 目录 先决条件 我们要构建什么?
阅读全文