← 返回蜂巢洞察

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

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

许多PDF文件包含不应公开分享的信息。在将文件发送给他人之前,个人资料、财务数据、签名、地址、账号信息、员工资料或机密商业数据通常都需要被隐藏起来。 一款PDF模糊处理工具可以让这一过程变得简单。它并不会永久删除这些内容,而是会在PDF文件的指定区域添加模糊效果,这样一来,敏感信息就会变得难以阅读,而文件的其他部分则保持原样。 在这个教程中,你将使用JavaScript制作一个基于浏览器的PDF模糊处理工具。用户可以上传PDF文件,预览每一页,在敏感内容上标出需要模糊处理的区域,调整模糊程度,对选定的页面应用模糊效果,预览最终结果,然后下载处理后的PDF文件——整个过程都不需要将文件上传到服务器上。 我们将使用PDF.js在浏览器中渲染PDF页面,利用HTML Canvas来创建和管理需要模糊处理的区域,而PDF-lib则会负责生成最终的模糊PDF文件。 完成这个教程后,你将会掌握一个功能完备的客户端PDF编辑工具,它的效果与我在AllInOneTools网站上提供的工具类似。 allinonetools - PDF工具 - 模糊PDF文件

目录

这款PDF模糊工具的功能及工作原理

一款PDF模糊处理工具可以帮助在文件共享之前保护敏感信息。它不会编辑或删除原始内容,而是会在选定的区域添加模糊效果,这样一来,机密信息就会变得无法阅读,而文件的其他部分则保持原样。

这种方法有助于隐藏个人详细信息、财务资料、账号号码、签名、地址、面部图像,或任何其他不应出现在最终文档中的私密内容。

在这个项目中,用户可以直接通过浏览器上传PDF文件,预览每一页,并在想要隐藏的区域上绘制一个或多个模糊处理区域。该工具还允许用户调整模糊程度,可以对选定的区域或整页内容进行模糊处理,将效果应用到当前页面、所有页面或特定页面范围上,预览完成后的文档,重命名输出文件,然后下载最终的PDF文件。

由于所有操作都在浏览器内部完成,因此没有任何文件会被上传到服务器上,从而有效保护了文档的隐私性。

在后台,该应用程序会首先使用PDF.js将每页PDF内容渲染到HTML画布上。它并不会立即修改原始PDF文件,而是会记录下用户创建的每个模糊处理区域的位置、大小、页码以及模糊程度等信息。

当用户点击“应用并完成”按钮时,这些存储的信息会被转换成实际的PDF页面坐标,然后选定的模糊效果会被应用到渲染好的页面上,PDF-lib会生成一份新的PDF文件,其中只包含被模糊处理的内容,而文档的其他部分则保持原样。

这种工作流程提供了交互式的编辑体验,在最终生成文档之前,原始PDF文件始终保持不变。

例如,每个模糊处理区域可以表示为一个如下所示的对象:

const blurRegion = {

    page: 2,

    x: 180,

    y: 240,

    width: 260,

    height: 90,

    intensity: 6

};

每个对象都存储了在最终生成PDF文件时重现模糊效果所需的所有信息。

项目设置

在编写任何代码之前,让我们先为这个PDF模糊处理工具创建一个简单的项目结构。

我们将使用纯HTML、CSS和JavaScript,以及两个第三方库:

  • PDF.js——用于在浏览器中渲染PDF页面。

  • PDF-lib——用于生成最终的模糊处理后的PDF文件。

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

pdf-blur-tool/

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

保持项目结构简单,有助于人们更好地理解各个组成部分的功能。

在加载自己的JavaScript代码之前,请先添加PDF.js和PDF-lib库。

<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 worker相关设置。

pdfjsLib.GlobalWorkerOptions.workerSrc =
    "pdf.worker.min.js";

该应用程序会在后台线程中处理PDF文件的渲染工作,这样在页面正在被渲染的过程中,用户界面仍然能够保持响应速度。

创建HTML布局

这个应用程序主要由四个部分组成:

  • 上传区域

  • PDF预览功能

  • 模糊处理设置面板

  • 最终下载区

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

<div id="uploadSection"></div>

<div id="editorSection" hidden>

    <div id="pdfPreview"></div>

    

最初,只有上传区域是可见的。

当用户选择了一份PDF文件后,编辑区才会显示出来。

选择DOM元素

需要为应用程序中使用的各种元素创建相应的引用。

const uploadSection =
    document.getElementById(
        "uploadSection"
    );

const editorSection =
    document.getElementById(
        "editorSection"
    );

const resultSection =
    document.getElementById(
        "resultSection"
    );

const fileInput =
    document.getElementById(
        "pdfInput"
    );

const pdfCanvas =
    document.getElementById(
        "pdfCanvas"
    );

const canvasContext =
    pdfCanvas.getContext("2d");

这些引用使得应用程序能够在上传、编辑和下载这三个阶段之间自由切换。

上传和预览PDF文件

上传区域支持拖放操作,也支持传统的文件选择方式。

在选择PDF文件后,需要先确认它确实是一个PDF文件,才能继续下一步操作。

async function handlePdfUpload(
    file
) {

    if (
        !file ||
        file.type !==
        "application/pdf"
    ) {

        alert(
            "请选择一份PDF文件。"
        );

        return;

    }

    await loadPdf(file);

}

如果验证成功,该PDF文件就会被加载到内存中。

读取PDF文件内容

使用File API将上传的文件转换为ArrayBuffer格式。

async function loadPdf(
    file
) {

    const bytes =
        await file.arrayBuffer();

    pdfDocument =
        await pdfjsLib
            .getDocument({
                data: bytes
            })
            .promise;

    currentPage = 1;

    await renderPage(
        currentPage
    );

}

上传到的这些字节数据在后续生成模糊处理的PDF文件时也会被再次使用。

渲染第一页

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

首先需要获取用户请求查看的那一页。

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,

    viewport
}).promise;

渲染完成后,PDF页面就会在编辑器中显示出来。

上传的PDF文件会在预览区域中显示,并提供页面导航功能。

创建页面导航功能

大多数PDF文件都包含多页内容。

应允许用户使用“上一页”和“下一页”按钮在页面之间切换。

let currentPage = 1;

let pdfDocument = null;

跳转到上一页。

previousButton
.addEventListener(
    "click",
    async () => {

        if (
            currentPage === 1
        ) {

            return;

        }

        currentPage--;

        await renderPage(
            currentPage
        );

    }
);

跳转到下一页。

nextButton
.addEventListener(
    "click",
    async () => {

        if (
            currentPage ===
            pdfDocument.numPages
        ) {

            return;

        }

        currentPage++;

        await renderPage(
            currentPage
        );

    }
);

每当当前页面发生变化时,都要更新页面编号。

pageIndicator.textContent =
    `第${currentPage}页,共${pdfDocument.numPages}页`;

这样,在浏览较大的PDF文件时,用户就能获得清晰的反馈信息。

带有“上一页”和“下一页”按钮的PDF预览界面。

为模糊编辑做准备

当前页面内容渲染完成后,应用程序会在PDF画布上方创建一层透明层。

这层覆盖层能够捕捉用户的鼠标操作,而不会修改原始的PDF预览内容。

<canvas
    id="overlayCanvas">
</canvas>

需要将这层覆盖层的大小设置为与PDF预览区域相同。

overlayCanvas.width =
    pdfCanvas.width;

overlayCanvas.height =
    pdfCanvas.height;

在后续的教学步骤中,用户可以利用这一覆盖层来绘制模糊效果,而不会影响到底层的PDF页面内容。

显示编辑器界面

当第一页的渲染完成之后,将界面从上传页面切换到编辑器界面。

uploadSection.hidden =
    true;

editorSection-hidden =
    false;

现在用户可以预览文档,在不同页面之间切换,并开始选择那些需要被模糊处理的区域。

创建模糊处理区域

既然PDF预览功能已经可以正常使用了,我们就可以进一步开发该应用程序最重要的功能了:让用户能够对敏感信息进行模糊处理。

用户不必立即编辑PDF文件,而是可以先在页面预览上绘制一个或多个需要被模糊处理的区域。

每个模糊处理区域都会保存自己的位置、大小以及模糊程度。这些信息会在后续的处理过程中被转换成实际的PDF坐标。

创建模糊处理区域对象

每一个模糊处理区域都会被表示为一个JavaScript对象。

例如:

const blurArea = {

    page: currentPage,

    x: 0,

    y: 0,

    width: 0,

    height: 0,

    intensity: 6

};

应用程序并不会立即修改PDF文件的内容,而是会记录下这些对象的信息,直到用户点击“应用并完成处理”按钮为止。

存储多个模糊处理区域

用户往往需要隐藏多条信息,因此可以将所有的模糊处理区域存储在一个数组中。

const blurAreas = [];

每当有新的模糊处理区域被创建时,就将其添加到这个数组中。

blurAreas.push({

    page: currentPage,

    x,

    y,

    width,

    height,

    intensity:
        blurIntensity

});

这样以后就可以轻松地重新绘制、编辑或删除这些模糊处理区域了。

开始选择需要模糊处理的区域

透明的覆盖画布会捕获用户的鼠标操作行为。

当用户按下鼠标按钮时,系统会记录下起始位置。

let isDrawing = false;

let startX = 0;

let startY = 0;

overlayCanvas
.addEventListener(
    "mousedown",
    event => {

        isDrawing = true;

        startX = event.offsetX;

        startY = event.offsetY;

    }
);

模糊处理区域的起始位置就是在这个点确定的。

绘制模糊处理区域

当鼠标移动时,系统会更新这个模糊处理区域的尺寸。

overlayCanvas
.addEventListener(
    "mousemove",
    event => {

        if (
            !isDrawing
        ) {

            return;

        }

        drawPreviewBox(

            startX,

            startY,

            event.offsetX,

            event.offsetY

        );

    }
);

当用户拖动鼠标时,预览图像会实时更新显示效果。

完成选择操作

当释放鼠标按钮时,需要保存已选中的模糊区域。

overlayCanvas
.addEventListener(
    "mouseup",
    event => {

        isDrawing = false;

        blurAreas.push{

            page:
                currentPage,

            x:
                startX,

            y:
                startY,

            width:
                event.offsetX -
                startX,

            height:
                event.offsetY -
                startY,

            intensity:
                blurIntensity

        });

        redrawBlurAreas();

        updateBlurList();

    }
);

每个模糊区域都会成为当前编辑会话的一部分。

用户在PDF预览中将模糊矩形拖动到敏感信息上。

绘制现有的模糊区域

每当页面内容发生变化或有新的模糊区域被添加时,都需要重新绘制所有的模糊框。

function redrawBlurAreas() {

    overlayContext.clearRect(

        0,

        0,

        overlayCanvas.width,

        overlayCanvas.height

    );

    blurAreas

        .filter、

            area =>

                area.page ===

                currentPage

        )

        .forEach、

            drawBlurArea

        );
}

这样就能确保在编辑过程中,之前创建的模糊区域仍然可见。

显示模糊框

所有被保存的模糊区域都会以虚线轮廓的形式呈现出来。

function drawBlurArea(
    area
) {

    overlayContext
        .setLineDash([6, 4]);

    overlayContext
        .strokeStyle =
        "#4f6cff";

    overlayContext
        .strokeRect(

            area.x,

            area.y,

            area.width,

            area.height

        );

}

这些虚线轮廓仅起到辅助作用,并不会出现在最终的PDF文件中。

模糊选项

用户可以自行选择模糊效果的应用方式。

该工具支持两种模式:

  • 模糊选中的区域

  • 模糊整个页面

所选择的模式会决定编辑过程中的具体操作方式。

const blurMode =
document.querySelector(

    'input[name="blurMode"]:checked'

).value;

如果选择了模糊选中的区域,用户就需要手动绘制模糊矩形。

如果选择了模糊整个页面,应用程序会直接对整个页面进行模糊处理,而无需用户进行手动选择。

模糊选项,允许用户选择是对选中的区域还是整个PDF页面应用模糊效果。

调整模糊强度

不同的文档需要不同级别的模糊效果。

通过滑块,用户可以在应用模糊效果之前自定义模糊程度。

const blurSlider =
document.getElementById(
    "blurIntensity"
);

let blurIntensity = 6;

blurSlider
.addEventListener(
    "input",
    event => {

        blurIntensity =
        Number(
            event.target.value
        );

    }
);

用户选择的模糊强度值会保存在每一个新创建的模糊区域中。

blurArea.intensity =
blurIntensity;

数值越大,模糊效果越明显。

允许用户调整模糊强度的滑块

管理多个模糊区域

许多文档包含机密信息,因此需要对这些信息进行保护。

该应用程序不会限制用户只能使用一个模糊区域,而是会显示所有已保存的模糊区域。

例如:

模糊区域 #1

模糊区域 #2

模糊区域 #3

每个模糊区域都配有删除按钮。

function removeBlurArea(
    index
) {

    blurAreas.splice(
        index,
        1
    );

    redrawBlurAreas();

    updateBlurList();

}

这样,用户就可以只删除不再需要的模糊区域了。

如果要清除当前页面上的所有模糊区域,可以执行以下操作:
function clearCurrentPage() {

    const remaining =

    blurAreas.filter(

        area =>

            area.page !==
            currentPage

    );

    blurAreas.length = 0;

    blurAreas.push(
        ...remaining
    );

    redrawBlurAreas();

}
模糊区域管理器,可显示多个模糊区域,并提供删除按钮及“清除当前页面”选项

为页面应用模糊效果

用户可能只需要对文档中的某个或某些页面应用模糊处理。

编辑器提供了三种选择方式:

  • 仅当前页面

  • 所有页面

  • 特定页面

const pageOption =
document.querySelector(

'input[name="pageOption"]:checked'

).value;

如果用户选择“特定页面”,就可以输入如“1, 3-5, 8”这样的页码范围。

这些页码会在最终生成PDF文件之前被转换成数组形式。应用到页面选项包括“当前页面”、“所有页面”和“特定页面”。高度为292像素,采用延迟加载方式。图片来源:https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/f3e1a72c-690d-4454-8b7a-50920817cd13.png。样式设置为:display:block; margin:0 auto; 宽度为566像素。

应用模糊效果

到目前为止,用户已经上传了PDF文件,选定了一个或多个需要应用模糊效果的区域,调整了模糊程度,并指定了哪些页面需要进行处理。

最后的步骤是将这些需要应用模糊效果的区域转换成生成的PDF文件中的实际模糊内容。

该应用程序并不会直接修改原始文档,而是会在保留原始文件的情况下生成一个新的PDF文件。

加载原始PDF文件

首先需要将用户上传的PDF文件加载到PDF-lib中。

async function applyBlur() {

    const pdfDoc =

        await PDFLib.PDFDocument.load(
            originalPdfBytes.slice()
        );

    const pages =
        pdfDoc.getPages();

}

通过使用原始文件的字节副本,可以确保上传的文档内容不会被修改。

处理选定的页面

需要确定哪些页面应该应用模糊效果。

const selectedPages =

parsePageSelection(

    pageSelection,

    pdfDoc.getPageCount()

);

例如:

当前页面

↓

[2]

所有页面

↓

[1,2,3,4]

特定页面

↓

[1,3,5]

只有这些页面会在处理过程中被修改。

将每页渲染成图像

由于模糊效果是基于像素进行的,因此系统中会将为每个选定的PDF页面创建一个离屏画布,用于存储处理后的结果。

const page =

await pdfDocument.getPage(
    pageNumber
);

const viewport =
page.getViewport、

    scale: 2

);

const canvas =
document.createElement(
    "canvas"
);

canvas.width =
viewport.width;

canvas.height =
viewport.height;

接下来需要渲染这些页面。

await page.render(

    canvasContext:
    canvas.getContext("2d"),

    viewport

).promise;

现在,画布中已经包含了这些PDF页面的位图版本,这些位图可以进一步被编辑或处理。

对选定的区域应用模糊效果

首先需要获取当前页面中所有需要应用模糊效果的区域。

const pageRegions =

blurAreas.filter(

    area =>

        area.page === pageNumber

);

然后遍历每一个需要应用模糊效果的区域。

pageRegions.forEach(

    area => {

        blurCanvasRegion(

            canvas,

            area

        );

    }

);

每个区域都会被独立地应用模糊效果。

模糊画布区域

浏览器的Canvas API允许在绘图过程中应用滤镜效果。

根据所选的强度值来设置模糊滤镜。

context.filter = `blur(${area.intensity}px)`;

仅重新绘制选中的区域。

context.drawImage(
    canvas,
    area.x,
    area.y,
    area.width,
    area.height,
    area.x,
    area.y,
    area.width,
    area.height
);

绘图完成后,需重置滤镜效果。

context.filter = "none";

只有选中的矩形区域会被模糊处理,页面的其他部分保持不变。

模糊整个页面

如果用户选择“模糊整个页面”,操作过程会更为简单。

将滤镜应用于整个画布区域。

context.filter = `blur(${blurIntensity}px)`;
context.drawImage(
    canvas,
    0,
    0
);
context.filter = "none";

整个页面都会应用所选的模糊效果。

将画布转换成PDF图像

编辑完画布内容后,需要将其转换为图像文件。

const imageData = canvas.toDataURL("image/png");

将图像数据转换为字节格式。

const bytes = await fetch(imageData).then(response => response.arrayBuffer());

最后将生成的图像嵌入到PDF文档中。

const image = await pdfDoc.embedPng(bytes);

替换PDF文档中的原有内容。

const pdfPage = pages[pageNumber - 1];
const size = pdfPage.getSize();
pdfPage.drawImage(image, {
    x: 0,
    y: 0,
    width: size.width,
    height: size.height
});

对每一页都重复上述步骤即可。

显示处理状态

生成较大的PDF文件可能需要几秒钟的时间。

在处理过程中,会显示加载提示信息。

applyButton.disabled = true;
applyButton.textContent = "正在应用滤镜...";

处理完成后:

applyButtondisabled = false;
applyButton.textContent = "应用完成";

这样的设计能够让用户清楚地了解到应用程序当前正在执行什么操作。

用于生成模糊PDF的‘应用完成’按钮

在处理过程中:

在PDF模糊处理完成期间,会显示相应的处理状态。

生成最终的PDF文件

在处理完每一页后,需将完成的文档保存下来。

const pdfBytes =

await pdfDoc.save();

const outputBlob =
new Blob(

[pdfBytes],

{

type:
"application/pdf"

}

);

将生成的文件保存起来,以便日后预览或下载。

generatedPdfBlob =
outputBlob;

此时,模糊处理后的PDF文件已经成功生成。

预览结果

隐藏编辑界面,显示最终的文档内容。

editorSection.hidden =
true;

resultSection-hidden =
false;

结果页面会显示以下内容:

  • 最终PDF文件的预览版本

  • 可编辑的文件名

  • 总页数

  • 文件大小

  • 下载按钮

最终PDF文件的预览界面,其中会显示多个被模糊处理的区域,并提供下载选项。

用户可以在下载之前先查看处理后的文档内容。

重命名与下载

在下载之前,用户可能希望给生成的文件重新命名。

添加一个用于输入文件名的字段。

<input

type="text"

id="outputFilename"

value="blurred-document.pdf">

需要对输入的文件名进行验证。

function getFilename() {

    let filename =
    outputFilename.value.trim();

    if (!filename) {

        filename =
        "blurred-document.pdf";

    }

    if (
        !filename
        .toLowerCase()
        .endsWith(".pdf")
    ) {

        filename += ".pdf";

    }

    return filename;

}

还会显示一些关于文件的额外信息。

pageCount.textContent =

`页数:
${finalPdfDocument.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文件,而不会向任何远程服务器发送任何文件。

下载页面,显示可编辑的文件名、页数、文件大小以及“下载”按钮。” height=

居中显示

演示:PDF模糊工具的工作原理

让我们一起来了解整个操作流程。

步骤1:上传PDF文件

用户可以通过拖放操作或点击“选择PDF文件”按钮来上传PDF文件。

浏览器会验证文件的合法性,并将其准备好进行渲染处理。

用于选择PDF文件的上传界面。” height=

步骤2:预览文档

上传的PDF文件会显示在预览窗口中。

用户可以通过页面导航控件来浏览文档内容。

带有“上一页”和“下一页”导航功能的PDF预览界面。” height=

步骤3:配置模糊效果设置

用户可以选择是对特定区域进行模糊处理,还是对整个页面应用模糊效果。

在创建模糊区域之前,用户还可以调整模糊效果的强度。 显示各种模糊选项的设置面板。” height=

步骤4:绘制模糊区域

用户可以直接在PDF预览页面上点击并拖动,从而为需要隐藏的内容划定模糊区域。

在同一页面上可以创建多个模糊区域。 用户正在为机密信息划定模糊区域。” height=

步骤5:调整模糊效果强度

模糊效果强度滑块用于控制模糊效果的强弱。

数值越大,模糊效果越明显。 用于调整模糊效果的滑块。” height= 用于控制模糊效果强度的滑块。” height=

步骤6:管理模糊区域

可以单独删除某些模糊区域,也可以清除当前页面上所有的模糊区域。

这样在生成最终的PDF文件之前,编辑工作会变得更加方便。

带有多个模糊区域的模糊区域管理面板。

步骤7:选择页面

用户可以决定是否对以下页面应用模糊效果:

  • 当前页面

  • 所有页面

  • 特定页面

例如:

1,3-5,8
“应用于哪些页面”选项栏,包含“当前页面”、“所有页面”和“特定页面”三个选择。

步骤8:应用模糊效果

在检查完所有设置后,用户只需点击应用并完成处理即可。

系统会生成一份包含所选模糊效果的新PDF文件。

“应用并完成处理”按钮,用于生成带有模糊效果的PDF文件。

步骤9:查看最终文档

完成处理后的PDF文件会显示在预览窗口中。

用户在下载之前可以仔细检查所有模糊区域的效果。

下载前的最终模糊PDF文件预览图。

步骤10:重命名并下载

最后,用户可以根据需要为输出文件重新命名,然后点击下载按钮。

浏览器会将生成的PDF文件保存到本地。

“下载”选项区,包含文件名编辑框和“下载”按钮。

性能提示

较大的PDF文件可能需要更长的时间来渲染和处理,但通过一些简单的优化措施,可以让编辑器保持良好的响应速度。

只需渲染用户当前正在查看的页面,而无需加载整个文档。

await renderPage(
    currentPage
);

如果进行了修改,只需重新使用相同的画布,并仅重新绘制需要应用模糊效果的区域即可。

overlayContext.clearRect(
    0,
    0,
    overlayCanvas.width,
    overlayCanvas.height
);

redrawBlurAreas();

在最终处理阶段,仅生成用户选中的页面。

for (const page of selectedPages) {

    await processPage(page);

}

下载完成后,立即释放临时资源。

URL.revokeObjectURL(
    downloadUrl
);

这些优化措施能够减少内存占用,确保即使处理大型多页文档,PDF模糊工具也能顺利运行。

常见错误

一个常见的问题是:在未考虑当前缩放级别之前就直接存储模糊处理所需的坐标值。

在生成最终文档之前,务必将预览坐标转换为PDF文档的坐标系统。

const scaleX =

pdfWidth /
canvas.width;

const scaleY =

pdfHeight /
canvas.height;

另一个错误是让模糊处理区域超出页面边界范围。

在进行处理之前,需对这些值进行限制。

blurArea.x = Math.max(
0,
blurArea.x
);

blurArea.y = Math.max(
0,
blurArea.y
);

用户在下载文件之前,也应仔细检查最终预览效果,尤其是当多个模糊处理区域分布在不同页面上时。

最后需要提醒的是:该项目仅对渲染后的PDF页面应用了视觉模糊效果。如果你的应用程序需要永久删除敏感内容而非仅仅对其进行视觉遮蔽,那么就需要使用其他的文档编辑技术。

总结

通过本教程,你使用了JavaScript开发了一个基于浏览器的PDF模糊工具。

你学会了如何上传和预览PDF文件、在页面之间切换、创建并管理多个模糊处理区域、调整模糊效果强度、对选中的页面应用模糊处理、使用PDF-lib生成新的PDF文件、预览处理后的结果以及下载最终文件——而且这一切都不需要将数据上传到服务器。

通过结合PDF.js、HTML Canvas API和PDF-lib,你打造了一个以保护用户隐私为目标的PDF编辑工具,而这个工具完全运行在浏览器内部。

你可以通过PDF模糊工具来体验整个使用流程。

今后,你还可以为这个项目添加更多功能,比如可移动和可调整大小的模糊处理区域、撤销/重做功能、可重复使用的模糊预设设置、键盘快捷键、触摸设备支持,以及更多的注释工具,从而让这个基于浏览器的PDF编辑工具变得更加完善。

相关文章

技术实践

如何使用Node.js和Google Gemini通过函数调用来构建一个人工智能代理

github.com/ziaongit/nodejs-gemini-agent 。 目录 功能调用机制的工作原理 我们正在构建什么 先决条件 项目设置 工具的定义 工具功能的实现 构建智能代理的循环机制 命令行入口点的设置 添加Express HTTP服务器 智能代理的测试 故障排除 接下来要构建什么 功能调用机制的工作原理 这里有一个让人感到惊讶的地方:Gemini并不会直接运行你的代码。它只会返回一个结构化对象,其中包含诸如“调用 get_weather 函数、将 city 设置为柏林”这样的指令。你的代码会接收到这些指令,然后执行相应的功能并将结果反馈回去。Gemini会检查这些结果是否

阅读全文
技术实践

用于估算人工智能提示设计效果的产品实验反事实方法

想象一下,你的团队在两周前将A提示在全球范围内推广使用。由于时间紧迫且对效果充满信心,因此在没有进行任何A/B测试、也没有设置测试对照组或保留用户群的情况下,这一更新就应用到了100%的用户身上。 虽然各项完成率看起来都很稳定,但深夜时一位同事从测试环境中提交了一个新的提示方案,这就引发了一个重要的问题:这个替代方案是否才是更合适的上线选择呢? 现在你陷入了这些已记录的数据所构成的困境之中。这个问题看似无解,但实际上并非如此。产品团队会通过前瞻性实验来观察如果推出某个功能会发生什么结果;而反事实估算则能帮助我们了解:如果选择了其他方案,最终会得到什么样的结果。 对于那些负责处理大语言模型产品日

阅读全文
技术实践

如何利用Gemini构建人工智能功能:面向开发者的提示工程实用指南

大多数关于提示工程的教学教程都遵循相同的流程:安装SDK,输入API密钥,调用 generateContent 函数,然后打印输出结果。模型会生成一些看似合理的内容,之后教学教程也就结束了。 但当你真正尝试将这个系统投入实际使用时,才会发现其实真正的准备工作根本还没有开始。 “API返回的文本”与“让用户感到可信的实际功能”之间的差距,正是需要耗费大量精力去解决的地方。 这个差距中充满了各种棘手的问题:模型生成的内容听起来和其他聊天机器人没什么两样;它会编造用户从未说过的话;它返回的数据会被用Markdown格式包裹起来;系统会在凌晨2点出现故障;而对于那些只是想得到答案的用户来说,系统展示的

阅读全文
技术实践

如何使用纯JavaScript使静态HTML页面在浏览器中可被编辑

当你为他人维护文档时,比如简历、一页的作品集或可打印的菜单,瓶颈往往不在于布局设计,而在于编辑流程本身。 任何微小的修改(“把这个项目符号上移”、“删除那行内容”、“这个链接已经失效了”)都需要由你这位使用代码编辑器的人来完成,尽管提出修改要求的人自己非常清楚他们想要做什么。 我在为一位家庭成员维护简历时遇到了这个问题。那份简历被制作成了一个静态的HTML文件,设计已经完成,内容也是由当事人提供的。但每次进行修改——无论是重新排列职位顺序、添加证书信息、修复链接,还是调整打印分页设置——都意味着要再次发送修改内容给我,然后让我去编辑文件。经过第十次这样的循环后,我终于意识到:其实应该让页面能够

阅读全文