← 返回蜂巢洞察

如何使用JavaScript开发一款基于浏览器的PDF颜色叠加工具

有时你并不想更改PDF文件的实际内容,只是想要在文档的某些部分或全部上添加一层彩色背景而已。 这种方法在制作带有品牌标识的报告、为文档添加彩色背景、突出显示打印版内容、生成设计样稿、应用水印效果,或是为演示文稿准备材料时都非常有用。 “PDF颜色覆盖工具”能够实现这一功能——它在PDF页面上叠加一层半透明的彩色层,同时保留下方的原始文本、图片和布局。 用户无需使用图形设计软件逐页进行手动编辑,只需上传PDF文件,选择所需的覆盖颜色、调整透明度、选择混合模式,指定颜色层的显示位置,预览效果后即可下载修改后的文档。 在本教程中,你将学习如何使用JavaScript来开发这个工具。用户完全可以无需将

有时你并不想更改PDF文件的实际内容,只是想要在文档的某些部分或全部上添加一层彩色背景而已。

这种方法在制作带有品牌标识的报告、为文档添加彩色背景、突出显示打印版内容、生成设计样稿、应用水印效果,或是为演示文稿准备材料时都非常有用。

“PDF颜色覆盖工具”能够实现这一功能——它在PDF页面上叠加一层半透明的彩色层,同时保留下方的原始文本、图片和布局。

用户无需使用图形设计软件逐页进行手动编辑,只需上传PDF文件,选择所需的覆盖颜色、调整透明度、选择混合模式,指定颜色层的显示位置,预览效果后即可下载修改后的文档。

在本教程中,你将学习如何使用JavaScript来开发这个工具。用户完全可以无需将文档发送到服务器,就能完成所有这些操作。

目录

PDF颜色覆盖工具的功能及工作原理

“PDF颜色覆盖工具”会在一个或多个页面上添加一层彩色背景,而原始的PDF内容依然可见。与那些会永久改变每个像素颜色的工具不同,这种工具通过可调节的透明度和混合模式,将选定的颜色与现有页面内容融合在一起。

因此,它非常适合用于制作带有品牌标识的文档、为文档添加彩色背景、生成适合演示用的PDF文件、突出显示重点内容、创建主题报告,或者在不修改原始文件的情况下生成预览版本。

在这个项目中,用户可以上传PDF文件,预览每一页,通过颜色选择器或十六进制值来选择覆盖色,调整覆盖层的不透明度,选择混合模式,指定覆盖层应显示的位置,决定哪些页面需要应用这种效果,预览修改后的文档,然后直接从浏览器中下载最终的PDF文件。

在内部实现上,PDF.js会将每一页渲染到HTML画布上,然后JavaScript会根据用户选择的透明度与混合模式,在这些渲染好的页面上绘制彩色矩形。当所有选中的页面都处理完成后,PDF-lib会将这些修改过的页面合并成一个新的可下载PDF文件。

覆盖色是通过十六进制值来表示的。

const overlay = {
    color: "#667eea",
    opacity: 0.5
};

在绘制覆盖层时,JavaScript首先会设置透明度。

context.globalAlpha = overlay.opacity;

接下来,就会应用所选择的颜色。

context.fillStyle = overlay.color;

最后,在需要的区域绘制出这个彩色矩形。

context.fillRect(0, 0, canvas.width, canvas.height);

根据所选择的混合模式,覆盖层可以轻轻地为文档增添色彩,使颜色变深,营造出戏剧性的视觉效果,或者生成完全不同的视觉风格,同时保留页面原有的内容。

项目结构设置

在实现覆盖层功能之前,我们先来创建一个简单的项目结构。

我们将使用HTML、CSS和JavaScript,再加上PDF.js、Canvas API以及PDF-lib来构建这个应用程序。

我们的项目结构如下所示:

pdf-color-overlay/
│── index.html
│── style.css
│── script.js
│── pdf.worker.min.js
│── assets/

将HTML、CSS和JavaScript分开存放,可以使项目结构更加清晰,也有利于后续的维护和扩展。

所使用的库

我们的PDF颜色覆盖工具依赖于三项浏览器技术:它们共同作用,用于渲染PDF页面、应用颜色覆盖层,并生成新的可下载文档。

PDF.js能够在浏览器内部直接渲染PDF页面。

HTML Canvas API利用透明度与混合模式,在每个渲染好的页面上绘制颜色覆盖层。

PDF-lib在所有选中的页面处理完成后,会生成最终的PDF文件。

在加载应用程序之前,请先引入所需的库文件:

<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渲染在后台进行,这样即使在打开较大的PDF文件时,界面也能保持响应速度。

创建HTML布局

该应用程序分为四个主要部分:

  • 上传区域

  • PDF预览区

  • 覆盖设置选项

  • 下载区域

首先创建基本的布局结构。

<section id="uploadSection"></section>
<section id="previewSection" hidden></section>
<section id="settingsSection" hidden></section>
<section id="downloadSection" hidden></section>

最初,只有上传区域是可见的。在成功加载PDF文件后,其他部分才会显示出来。

选择主要元素

为应用程序中使用的各种元素存储对应的引用。

const uploadSection = document.getElementById("uploadSection");
const previewSection = document.getElementById("previewSection");
const settingsSection = document.getElementById("settingsSection");
const pdfCanvas = document.getElementById("pdfCanvas");

这些引用使得应用程序能够在不重复搜索DOM的情况下更新界面内容。

上传和预览PDF文件

上传区域支持拖放操作以及手动选择文件。

在加载文档之前,请确认所选文件确实是PDF格式。

async function uploadPdf(file) {
    if (!file || file.type !== "application/pdf") {
        alert("请选择PDF文件。");
        return;
    }

    await loadPdf(file);
}

经过验证后,PDF文件会被加载到内存中以便进行渲染。

上传区域支持拖放操作,并提供“选择PDF文件”按钮。

加载PDF文件

在用PDF.js打开文件之前,需要先将上传的文件转换为ArrayBuffer格式。

async function loadPdf(file) {
    const bytes = await file.arrayBuffer();
    pdfDocument = await pdfjsLib.getDocument({
        data: bytes
    }).promise;
    currentPage = 1;
    renderPage(currentPage);
}

一旦文件成功加载完毕,系统会自动渲染第一页内容。

渲染PDF页面

PDF.js会逐页将PDF内容渲染到HTML画布上。

需要获取用户选中的具体页面来进行渲染操作。

const page = await pdfDocument.getPage(currentPage);

创建视图窗口。

const viewport = page.getViewport({
    scale: 1.5
});

调整画布的大小。

pdfCanvas.width = viewport.width;
pdfCanvas.height = viewport.height;

渲染当前页面。

await page.render({
    canvasContext: pdfCanvas.getContext("2d"),
    viewport
}).promise;

渲染完成后,用户可以在应用任何覆盖效果之前查看当前的页面内容。

使用PDF.js渲染的PDF预览图,其中显示了页面导航功能。

在页面之间导航

大多数PDF文件都包含多页内容,因此该应用程序提供了简单的导航功能。

记录当前所在的页面。

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);
    }
});

更新页面指示器。

pageCounter.textContent = `当前页码:${currentPage},总页数:${pdfDocument.numPages}`;

现在,用户可以在决定如何应用颜色覆盖效果之前,先浏览上传的PDF文件内容。

设置覆盖效果

在上传并预览PDF文件后,用户可以配置颜色覆盖效果的具体应用方式。设置面板允许用户选择覆盖颜色、调整透明度、选择混合模式、指定覆盖效果出现的位置,并决定哪些页面应该应用这种效果,从而在生成最终的PDF文件之前进行相应的调整。

选择覆盖颜色

第一个设置选项允许用户选择用于覆盖PDF内容的颜色。

该应用程序同时支持使用颜色选取器进行选择,也支持直接输入十六进制代码。

创建颜色选取器。

<input type="color" id="overlayColor" value="#667eea">

创建用于输入十六进制颜色的文本框。

<input type="text" id="hexValue" value="#667eea">

获取用户选择的颜色值。

const overlayColor = document.getElementById("overlayColor").value;

如果用户手动输入十六进制颜色值,系统会自动将其与颜色选择器进行同步。

hexValue.addEventListener("input", () => {
    overlayColor.value = hexValue.value;
});

所选的颜色随后会被绘制在已渲染的PDF页面上。

带有十六进制颜色输入功能的覆盖色选择器。

调整不透明度

不透明度决定了覆盖层呈现的透明程度。

较低的不透明度值会让原始PDF内容更明显地显示出来,而较高的值则会产生更强烈的色彩效果。

创建一个用于调节不透明度的滑块。

<input type="range" id="opacity" min="0" max="100" value="50">

获取用户选择的值。

const opacity = Number(document.getElementById("opacity").value) / 100;

在绘制覆盖层之前,这个值会被用来设置画布的不透明度。

用于控制覆盖层透明度的滑块。

选择混合模式

混合模式决定了覆盖层颜色与原始PDF内容之间的叠加效果。

创建一个下拉菜单。

<select id="blendMode">
    <option value="source-over">>正常显示乘法混合覆盖显示柔光效果硬光效果差值混合
</select>

获取用户选择的混合模式。

const blendMode = document.getElementById("blendMode").value;

每种混合模式都会产生不同的视觉效果,同时还能保留覆盖层下方的原始PDF内容。

显示可用混合模式的下拉菜单。

选择覆盖层的位置

覆盖层并不一定需要覆盖整个页面,用户也可以根据需要仅将其应用到特定的区域。

创建可供选择的选项。

<input type="radio" name="position" value="full" checked>
全页显示
<input type="radio" name="position" value="header">>仅显示页眉
<input type="radio" name="position" value="footer">>仅显示页脚

获取用户选中的位置。

const position = document.querySelector('input[name="position"]:checked').value;

在处理过程中,应用程序仅会在用户选中的区域内显示覆盖层。

覆盖层应用选项,包括“全页覆盖”、“仅标题覆盖”以及“仅页脚覆盖”。

选择要处理的页面

用户可以通过多种方式应用覆盖层效果:

  • 仅当前页面

  • 整个文档

  • 为每页单独设置覆盖层

  • 特定页面

创建用于选择页面的控件。

<input type="radio" name="pages" value="current" checked>
仅当前页面
<input type="radio" name="pages" value="all">
所有页面
<input type="radio" name="pages" value="separate">
为每页单独设置覆盖层
<input type="radio" name="pages" value="custom">>特定页面
<input type="text" id="pageRange" placeholder="例如:1、3-5、10">

获取用户选择的选项。

const pageMode = document.querySelector('input[name="pages"]:checked').value;

读取用户输入的特定页面范围。

const pageRange = document.getElementById("pageRange").value.trim();

这种灵活性使用户能够根据文档的具体情况选择不同的覆盖层应用策略。

页面应用选项,包括“当前页面”、“所有页面”、“为每页单独设置覆盖层”以及“特定页面”。

应用覆盖层效果

一旦所有设置都完成配置,用户就可以开始处理PDF文件了。

创建用于执行操作的按钮。

<button id="applyOverlay">>应用覆盖层</button>

启动处理流程。

applyOverlay.addEventListener("click", async () => {
    await processOverlay();
});
应用覆盖层按钮。

重新开始

用户可以随时重置应用程序并上传其他文档。

创建用于重置的按钮。

<button id="resetTool">>重新开始</button>

重置应用程序设置。

resetTool.addEventListener("click", () => {
    location.reload();
});

上传区域会再次变得可见,这样就可以在不需要手动清除所有设置的情况下处理另一个PDF文件了。

为PDF页面添加颜色覆盖层

现在我们将构建该应用程序的主要功能:为PDF页面添加彩色覆盖层。

首先,使用PDF.js将每页选中的PDF内容渲染到HTML画布上。然后,JavaScript会使用所选的混合模式在页面上绘制半透明的彩色矩形。当所有选中的页面都处理完成后,PDF-lib会生成一个新的可下载PDF文件。

应用覆盖层的颜色

在开始绘制任何内容之前,先获取用户选择的颜色。

const overlayColor = document.getElementById("overlayColor").value;

将这个颜色设置为画布的填充色。

context.fillStyle = overlayColor;

这个颜色将会被绘制在每页PDF中选中的区域上。

设置覆盖层的透明度

透明度的大小决定了在覆盖层下面原本的页面内容能显示多少。

将用户选择的透明度值应用起来。

context.globalAlpha = opacity;

较低的透明度会产生较淡的颜色效果,而较高的透明度则会产生更明显的视觉效果。

应用混合模式

Canvas支持多种合成模式,这些模式决定了覆盖层与页面原有内容的叠加方式。

context.globalCompositeOperation = blendMode;

一些常见的混合模式包括:

  • 正常模式 – 直接在页面上绘制颜色。

  • 乘法模式 – 会使显示效果变暗。

  • 叠加模式 – 会增加整体的对比度。

  • 柔光模式 – 会产生柔和的照明效果。

  • 硬光模式 – 会产生更强烈的对比度。

  • 差值模式 > 会根据颜色差异生成反转效果的图像。

绘制覆盖层

在配置好了颜色、透明度和混合模式之后,就在画布上绘制覆盖层吧。

如果想要在整个页面上添加覆盖层,可以这样写:

context.fillRect(0, 0, canvas.width, canvas.height);

但如果用户选择了“仅显示页眉”,那么只需在页面的顶部区域绘制矩形即可。

context.fillRect(0, 0, canvas.width, 120);

而如果选择“仅显示页脚”,则应在页面的底部附近绘制覆盖层。

context.fillRect(0, canvas.height - 120, canvas.width, 120);

这些选项允许在不修改原始PDF内容的情况下,应用不同的叠加效果。

处理选定的页面

配置好叠加效果后,只需处理用户所选的页面即可。

遍历所有被选中的页面。

for (let page = startPage; page <= endPage; page++) {
    await processPage(page);
}

每个经过处理的页面在生成最终文档之前都会被暂时保存下来。

如果选择了“仅当前页面”,那么只有当前活跃的页面会被处理;如果选择了“所有页面”,则叠加效果会应用于整个文档。

生成最终的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
]);

在新的PDF页面上绘制处理后的图像。

page.drawImage(image, {
    x: 0,
    y: 0,
    width: image.width,
    height: image.height
});

重复上述步骤,直到所有选定的页面都被添加到新的PDF文档中。

保存生成的PDF文件

当所有页面都处理完毕之后,将最终生成的PDF文件保存下来。

const pdfBytes = await outputPdf.save();

创建一个可供下载的文件。

generatedPdfBlob = new Blob([pdfBytes], {
    type: "application/pdf"
});

现在,包含这些叠加效果的新的PDF文件已经可以用来预览了。

应用了选定的颜色叠加效果后的PDF预览图。

预览结果

在下载处理后的PDF文件之前,用户应该能够先查看最终的预览效果。这样就可以确保所选择的颜色、不透明度、混合模式以及页面范围确实被正确应用了。

加载生成的PDF文件进行预览。

let finalPdf = null;
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
    });

    previewCanvas.width = viewport.width;
    previewCanvas.height = viewport.height;

    await page.render({
        canvasContext: previewCanvas.getContext("2d"),
        viewport
    }).promise;
}

用户在下载处理后的PDF文件之前,可以先浏览其中的内容。

最终PDF预览图,显示应用颜色叠加效果,可供用户在下载前查看。

重命名与下载

在保存生成的PDF文件之前,用户可以自定义文件的名称。

创建用于输入文件名的字段。

<input type="text" id="outputFilename" value="color-overlay.pdf">

获取用户输入的文件名。

function getFilename() {
    let filename = outputFilename.value.trim();

    if (!filename) {
        filename = "color-overlay.pdf";
    }

    if (!filename.toLowerCase().endsWith(".pdf")) {
        filename += ".pdf";
    }

    return filename;
}

显示有关生成的PDF文件的信息。

pageCount.textContent = `${finalPdf.numPages} 页`;
fileSize.textContent = formatFileSize(generatedPdfBlob.size);

下载已完成处理的文档。

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文件即可开始上传。

支持拖放上传的文件区域及“选择PDF文件”按钮。

步骤2:预览文档

上传的PDF文件会逐页进行渲染,因此用户可以在应用任何修改之前先查看文档的内容。

带页面导航控件的PDF预览界面。

步骤3:配置叠加效果

用户可以选择叠加颜色的深浅、调整不透明度、选择混合模式、确定叠加位置,并决定哪些页面需要应用这种效果。

包含颜色、不透明度、混合模式、位置及页面选择选项的叠加设置面板。

步骤4:应用叠加效果

点击应用叠加效果按钮,即可使用所选的设置来处理指定的页面。

“应用叠加效果”按钮。

步骤5:查看处理后的PDF文件

处理完成的PDF文件会显示在预览窗口中,用户可以在下载之前确认叠加效果是否满意。

应用叠加效果后的最终PDF预览界面。

步骤6:重命名并下载文件

最后,用户可以根据需要为生成的PDF文件重新命名,查看文件的页数和大小,然后将其下载下来。

包含文件名、页数、文件大小及下载按钮的下载界面。

性能优化建议

对于较大的PDF文件,由于需要渲染和更新每一页内容,因此处理速度可能会较慢。仅处理所需的页面有助于提升处理效率。

for (const page of selectedPages) {
    await processPage(page);
}

在下载文件后,应释放临时使用的资源,以减少内存占用。

URL.revokeObjectURL(downloadUrl);

这些小的优化措施能够帮助应用程序在处理大型多页PDF文件时保持良好的响应速度。

常见错误

一个常见的错误是在没有先恢复原始页面内容的情况下就应用多个叠加效果。在添加新的叠加效果之前,务必先重新渲染PDF页面的内容。

await renderPage(currentPage);

另一个问题是,在更改不透明度或混合模式后,往往会忘记将画布恢复到默认状态。

context.globalAlpha = 1;
context.globalCompositeOperation = "source-over";

最后,如果使用过高的不透明度,那么原本的PDF内容将会被完全掩盖。选择适当的透明度通常能够获得更加平衡的效果。

结论

通过本教程,您使用JavaScript开发了一个基于浏览器的PDF颜色叠加工具。

您学会了如何上传PDF文件,如何使用PDF.js渲染页面,如何设置叠加颜色的样式、调整不透明度、应用不同的混合模式,如何定位叠加层的位置,如何处理选中的页面,如何利用PDF-lib生成新的PDF文件,如何预览最终结果,如何重新命名输出文件,以及如何直接从浏览器中下载文件。

由于整个工作流程都是在本机上运行的,因此用户可以在不将敏感文件上传到外部服务器的情况下对PDF文档进行自定义操作。

您可以通过PDF颜色叠加工具来体验这一完整的工作流程。

在此基础上,您还可以为该项目添加渐变效果、自定义叠加形状、图片叠加功能、可重复使用的颜色预设选项、水印模板,或是其他的PDF编辑功能,从而进一步提升其灵活性。

相关文章

技术实践

如何使用JavaScript开发一款基于浏览器的PDF模糊处理工具

许多PDF文件包含不应公开分享的信息。在将文件发送给他人之前,个人资料、财务数据、签名、地址、账号信息、员工资料或机密商业数据通常都需要被隐藏起来。 一款PDF模糊处理工具可以让这一过程变得简单。它并不会永久删除这些内容,而是会在PDF文件的指定区域添加模糊效果,这样一来,敏感信息就会变得难以阅读,而文件的其他部分则保持原样。 在这个教程中,你将使用JavaScript制作一个基于浏览器的PDF模糊处理工具。用户可以上传PDF文件,预览每一页,在敏感内容上标出需要模糊处理的区域,调整模糊程度,对选定的页面应用模糊效果,预览最终结果,然后下载处理后的PDF文件——整个过程都不需要将文件上传到服务

阅读全文
技术实践

如何使用JavaScript构建基于浏览器的PDF过滤工具开发环境

PDF编辑并不仅限于添加签名或合并文档。有时,你只是想通过增加亮度、提升对比度、添加模糊效果、将其转换为灰度图,或者应用一些创意视觉效果来改善PDF的外观——而这一切都不需要打开Photoshop或安装任何桌面软件。 在这个教程中,你将使用JavaScript、PDF.js、Canvas API以及PDF-lib构建一个基于浏览器的PDF过滤器工具。用户可以上传PDF文件,预览每一页,叠加多个过滤层,应用预设效果,处理选定的页面,预览最终结果,重新命名文件,然后直接从浏览器中下载编辑后的PDF。 由于所有操作都在用户的设备上本地进行,因此上传的PDF文件永远不会离开用户的设备,这使得这个工具既

阅读全文
技术实践

如何使用JavaScript构建基于浏览器的PDF颜色反转工具

长时间阅读PDF文档会让人感到疲劳,尤其是当文档的背景颜色很鲜艳,或者你在光线较暗的环境中阅读时。 在某些情况下,设计师、开发人员和印刷专业人士可能希望查看文档在颜色反转后的效果,或者为便于访问或审查而准备不同的版本。 PDF颜色反转工具能够实现这一目标——它在保持文档结构不变的前提下,改变PDF页面的颜色。用户无需手动编辑每一张图片或图形,只需上传PDF文件,选择颜色反转模式,指定需要处理的页面范围,调整输出质量,然后点击几下即可生成新的PDF文件并下载。 在本教程中,你将使用JavaScript制作一个基于浏览器的PDF颜色反转工具。用户可以上传PDF文件,预览其内容,选择反转方式,指定处

阅读全文
技术实践

如何使用 Fastlane 和 GitHub Actions 自动化Flutter应用的发布流程,以便将其分发到 Firebase App Distribution、Google Play、TestFlight 以及 App Store Connect平台上

想象一下:现在是周五下午4点,你的团队刚刚完成了本次迭代周期的最后一项功能开发。但产品经理要求在当天结束前通过TestFlight发送一个新的版本,这样客户就可以在周末进行测试。 你打开Xcode,等待代码打包完成,处理一个昨天还不存在的签名错误,修复问题后重新打包代码,再次等待上传,然后等待App Store Connect进行处理。接着你需要为Android版本重复这些步骤,不过这次是通过Android Studio来操作的。你需要签署APK文件,登录Firebase App Distribution平台,将文件上传进去,添加测试人员,编写发布说明,最后点击发送按钮。 现在已经是下午6点4

阅读全文