笔记
0工具
0此页用于记录用户反馈问题后的每一次改进
关于
“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。
md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。
要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。
在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。
要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。
工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。
写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。
适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。
不想公开、只想临时给人看:点“分享”生成一条带密码和有效期的链接,到期自动失效,你也能随时撤销。
你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。
全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。
这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。
不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。
产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。
如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。
趋势
如何使用JavaScript构建一个基于浏览器的PDF OCR文本转换器
你有没有遇到过这种情况——收到一个PDF文件,看起来内容满满,但就是选不中、复制不了、搜索不到?大概率是个扫描件。这种PDF本质上就是一张张图片叠在一起,里面的文字虽然人眼能看,但电脑根本不认识。要让它变成真正可编辑、可搜索的文本,就得靠OCR(光学字符识别)技术。
今天我就带你手撸一个完全在浏览器里运行的PDF OCR工具。用户可以上传PDF、预览页面、配置OCR参数、提取文字、实时看进度、检查识别置信度,最后还能导出结果。因为所有处理都在本地完成,你的敏感文档永远不会离开自己的设备,既快又安全。
读完这篇文章,你不仅能理解浏览器端OCR的工作原理,还能自己动手搭一个完整的PDF转文字工具。
为什么PDF OCR这么有用
很多PDF文件看起来有文字,其实只是扫描图像。虽然人眼能读,但计算机没法直接搜索、复制或编辑里面的内容。OCR技术就是来解决这个问题的——它从扫描页面上识别出字符,转成可编辑、可搜索的数字文本。
一旦文字被提取出来,你就可以复制、翻译、索引、摘要,或者导入到其他应用里用。
OCR的用途非常广泛:
- 企业:处理发票、采购单、收据、合同、银行流水、税务文件,省去手工录入
- 法务:搜索合同、宣誓书、法庭文件中的姓名、日期或特定条款
- 政府:将历史档案、申请表、护照、官方文件数字化,构建可搜索的电子档案
- 教育:把扫描的书籍、论文、讲义、考试材料转成可搜索文本
- 医疗:数字化处方、化验报告、保险索赔、病历,减少纸质文件
- 电商:处理来自Amazon、Flipkart、Meesho、Shopify等平台的发票、运单、采购单,快速提取订单号、客户信息、地址和产品详情
- 开发:构建文档管理系统、企业搜索工具、AI助手、工作流自动化平台时,让扫描文档变成可搜索的数字内容
最关键的是,我们的应用完全在浏览器里做OCR,用户处理机密文档时不需要上传到任何外部服务器,隐私和安全都有保障。
PDF OCR的工作原理
一个PDF OCR应用的工作流程大致是这样的:
- 用户上传PDF,浏览器验证文件并加载到内存
- 用PDF.js把每一页渲染成图片
- 把图片交给OCR引擎处理
- OCR引擎逐像素分析,识别字符、单词和句子,重建数字文本
- 根据用户选择的语言,应用对应的词典和字符模型提升准确率
- 如果启用了图像增强,会在识别前对图片做灰度化、对比度增强、锐化等处理
- 每处理完一页,更新进度条,让用户实时看到进展
- OCR引擎返回每页的置信度分数,帮助用户判断识别结果的可靠性
- 所有页面处理完后,合并文本,用户可以直接复制或导出为TXT或JSON文件
因为每一步都在本地完成,上传的PDF永远不会离开你的设备。对于敏感的商业文档、法律文件、医疗记录、财务报告、政府公文来说,这种方式非常合适。
项目搭建
我们用标准的前端技术来构建这个工具。先创建项目目录结构:
pdf-ocr-tool/
│── index.html
│── style.css
│── script.js
然后在index.html里引入需要的JavaScript库:
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.4.168/pdf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js"></script>
<script src="https://unpkg.com/pdf-lib"></script>
这三个库提供了渲染PDF页面、识别文字、管理PDF相关操作的全部能力。
我们用了哪些库
OCR涉及多个处理阶段,所以需要组合几个库来完成。
PDF.js
这是Mozilla开发的库,用来在浏览器里加载和渲染PDF文件。OCR引擎只能处理图片,不能直接理解PDF格式,所以我们先用PDF.js把每一页渲染成图片,作为OCR的输入。
Tesseract.js
这是最流行的开源OCR引擎Tesseract的JavaScript版本。它支持几十种语言,能直接从扫描文档中识别印刷体文字,不需要调用任何外部API或云服务。所有识别都在本地完成,这也是隐私保护的关键。
PDF-lib
这个库用来处理PDF相关的操作,比如添加注释、编辑元数据、修改文档等。虽然当前版本我们用得不多,但为以后扩展功能留了余地。
这三个库组合起来,就构成了一个完整的浏览器端OCR解决方案:渲染PDF页面 → 识别文字 → 追踪进度 → 报告置信度 → 导出结果,全程不上传任何数据。
创建上传界面
OCR工作流的第一步就是让用户选择一个PDF文件。在上传之前,应用需要验证文件类型,确保是支持的格式。
一个好的上传界面应该简单直观,同时支持桌面和移动端。除了传统的文件选择器,最好也支持拖拽上传,给用户多一种选择。
我的实现思路是这样的:
<div id="upload-section">
<div id="drop-zone">
<p>拖拽PDF文件到这里,或者点击选择文件</p>
<input type="file" id="file-input" accept=".pdf" hidden>
<button id="select-button">选择PDF文件</button>
</div>
</div>
对应的JavaScript逻辑:
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');
const selectButton = document.getElementById('select-button');
// 点击按钮触发文件选择
selectButton.addEventListener('click', () => fileInput.click());
// 文件选择后处理
fileInput.addEventListener('change', handleFileSelect);
// 拖拽事件
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
function handleFileSelect(e) {
const file = e.target.files[0];
if (file) handleFile(file);
}
function handleFile(file) {
if (file.type !== 'application/pdf') {
alert('请选择PDF文件');
return;
}
// 验证通过,开始加载PDF
loadPDF(file);
}
这里有几个关键点:
- 用
accept=".pdf"限制文件选择器只显示PDF文件 - 拖拽时加一个视觉反馈(
drag-over类),用户体验更好 - 通过
file.type验证文件类型,防止用户误传非PDF文件 - 验证通过后调用
loadPDF(),进入下一步
因为所有处理都在浏览器里完成,文件不会被上传到任何服务器。这一点对处理合同、病历、财务报表等敏感文档特别重要。
预览上传的PDF页面
加载PDF后,我们得让用户能看到每一页的内容,这样他们才能决定哪些页需要OCR处理。预览功能也方便用户检查文档是否完整。
用PDF.js渲染页面:
let pdfDoc = null;
const pdfjsLib = window['pdfjs-dist/build/pdf'];
async function loadPDF(file) {
const arrayBuffer = await file.arrayBuffer();
pdfDoc = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const totalPages = pdfDoc.numPages;
// 显示总页数
document.getElementById('page-count').textContent = `共 ${totalPages} 页`;
// 渲染每一页的缩略图
for (let i = 1; i <= totalPages; i++) {
await renderPage(i);
}
}
async function renderPage(pageNum) {
const page = await pdfDoc.getPage(pageNum);
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.createElement('canvas');
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.dataset.pageNum = pageNum;
const ctx = canvas.getContext('2d');
await page.render({ canvasContext: ctx, viewport: viewport }).promise;
// 添加到预览区
document.getElementById('preview-area').appendChild(canvas);
// 给每个canvas加一个复选框,让用户选择要OCR的页面
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true; // 默认全选
checkbox.dataset.pageNum = pageNum;
canvas.parentElement.insertBefore(checkbox, canvas);
}
这里用scale: 1.5来渲染,既能看清内容,又不会让图片太大导致内存溢出。实际项目中可以根据PDF的原始分辨率动态调整缩放比例。
每个canvas都加了一个复选框,默认全选。用户可以根据需要取消勾选某些页面,比如封面、空白页或者已经包含可搜索文字的页面,节省OCR处理时间。
配置OCR设置
OCR不是万能的,不同的文档需要不同的配置才能达到最佳效果。我提供了几个可配置的参数:
语言选择
Tesseract.js支持多种语言。默认是英文,但如果你处理的是中文文档,得切换到中文语言包。
<label for="language">识别语言:</label>
<select id="language">
<option value="eng">English</option>
<option value="chi_sim">简体中文</option>
<option value="chi_tra">繁体中文</option>
<option value="jpn">日语</option>
<option value="kor">韩语</option>
<option value="fra">法语</option>
<option value="deu">德语</option>
<option value="spa">西班牙语</option>
</select>
图像增强
对于质量较差的扫描件,预处理可以显著提升OCR准确率。我提供了三个选项:
<label>
<input type="checkbox" id="enhance-grayscale"> 灰度化
</label>
<label>
<input type="checkbox" id="enhance-contrast"> 增强对比度
</label>
<label>
<input type="checkbox" id="enhance-sharpen"> 锐化
</label>
对应的图像处理逻辑:
function enhanceImage(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
if (document.getElementById('enhance-grayscale').checked) {
// 灰度化:将RGB转为灰度值
for (let i = 0; i < data.length; i += 4) {
const gray = 0.299 * data[i] + 0.587 * data[i+1] + 0.114 * data[i+2];
data[i] = gray; // R
data[i+1] = gray; // G
data[i+2] = gray; // B
}
}
if (document.getElementById('enhance-contrast').checked) {
// 增强对比度:使用简单的线性变换
const factor = 1.5;
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, Math.max(0, (data[i] - 128) * factor + 128));
data[i+1] = Math.min(255, Math.max(0, (data[i+1] - 128) * factor + 128));
data[i+2] = Math.min(255, Math.max(0, (data[i+2] - 128) * factor + 128));
}
}
if (document.getElementById('enhance-sharpen').checked) {
// 锐化:使用3x3卷积核
// 这个实现稍微复杂一点,需要处理边缘
// 为了简化,这里用了一个简单的拉普拉斯算子
const width = canvas.width;
const height = canvas.height;
const output = new Uint8ClampedArray(data);
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
const idx = (y * width + x) * 4;
// 拉普拉斯算子:中心点 * 5 - 上下左右
for (let c = 0; c < 3; c++) {
const sum = 5 * data[idx + c]
- data[((y-1) * width + x) * 4 + c]
- data[((y+1) * width + x) * 4 + c]
- data[(y * width + (x-1)) * 4 + c]
- data[(y * width + (x+1)) * 4 + c];
output[idx + c] = Math.min(255, Math.max(0, sum));
}
}
}
imageData.data.set(output);
}
ctx.putImageData(imageData, 0, 0);
}
这些增强操作虽然简单,但在处理老旧扫描件或低质量复印件时效果很明显。灰度化可以减少颜色干扰,对比度增强让文字更清晰,锐化则让边缘更突出。
页面选择
前面已经实现了每页的复选框,用户可以在预览区勾选需要处理的页面。
从PDF中提取文字
配置好之后,点击"开始识别"按钮,应用就会遍历选中的页面,对每一页进行OCR处理。
核心逻辑如下:
async function extractText() {
const selectedPages = [];
document.querySelectorAll('#preview-area input[type="checkbox"]:checked').forEach(cb => {
selectedPages.push(parseInt(cb.dataset.pageNum));
});
if (selectedPages.length === 0) {
alert('请至少选择一页');
return;
}
const language = document.getElementById('language').value;
const results = [];
const totalPages = selectedPages.length;
for (let i = 0; i < selectedPages.length; i++) {
const pageNum = selectedPages[i];
// 更新进度
updateProgress(i + 1, totalPages, `正在处理第 ${pageNum} 页...`);
// 获取对应的canvas
const canvas = document.querySelector(`canvas[data-page-num="${pageNum}"]`);
// 如果启用了图像增强,先处理
if (shouldEnhance()) {
enhanceImage(canvas);
}
// 使用Tesseract.js进行OCR识别
const result = await Tesseract.recognize(
canvas.toDataURL('image/png'),
language,
{
logger: (m) => {
if (m.status === 'recognizing text') {
updateProgress(i + 1, totalPages,
`第 ${pageNum} 页 - ${Math.round(m.progress * 100)}%`);
}
}
}
);
results.push({
page: pageNum,
text: result.data.text,
confidence: result.data.confidence
});
}
// 所有页面处理完成
displayResults(results);
}
这里有几个值得注意的地方:
- 用
canvas.toDataURL('image/png')把canvas转成base64图片,作为OCR的输入 - Tesseract.js的
logger回调可以用来获取实时进度,我把它映射到UI上 - 每页的结果包括识别出的文本和置信度分数
- 所有页面处理完后,统一展示结果
Tesseract.js在第一次调用时会下载对应的语言包(比如eng.traineddata或chi_sim.traineddata),这个过程需要网络。下载完成后,语言包会缓存在浏览器里,后续使用不需要再下载。
追踪OCR进度
用户不喜欢等待,更不喜欢黑盒等待。所以进度反馈非常重要。我做了两层进度显示:
- 总体进度:显示"第 X 页 / 共 Y 页"
- 单页进度:显示当前页的识别进度百分比
UI实现:
<div id="progress-section" style="display:none;">
<div id="progress-bar">
<div id="progress-fill"></div>
</div>
<p id="progress-text"></p>
</div>
JavaScript更新逻辑:
function updateProgress(current, total, message) {
const percent = (current / total) * 100;
document.getElementById('progress-fill').style.width = `${percent}%`;
document.getElementById('progress-text').textContent = message;
}
进度条用CSS过渡动画,看起来更流畅:
#progress-fill {
height: 100%;
background: linear-gradient(90deg, #4CAF50, #8BC34A);
transition: width 0.3s ease;
border-radius: 5px;
}
这样用户就能清楚地知道:当前处理到哪一页了,这一页识别了多少。如果某页特别慢,用户也能判断是文档复杂还是程序卡住了。
理解OCR置信度分数
OCR不是100%准确的。Tesseract.js会为每页返回一个置信度分数(0-100),表示它对识别结果的把握程度。
置信度的含义:
- 90-100:非常可靠,文字清晰,字体标准
- 70-89:比较可靠,可能有少量错误
- 50-69:需要人工校对,可能有较多错误
- 低于50:不太可靠,建议重新扫描或调整参数
我在结果展示区用不同颜色标识置信度:
function getConfidenceColor(confidence) {
if (confidence >= 90) return '#4CAF50'; // 绿色
if (confidence >= 70) return '#FFC107'; // 黄色
if (confidence >= 50) return '#FF9800'; // 橙色
return '#F44336'; // 红色
}
function displayResults(results) {
const container = document.getElementById('results');
container.innerHTML = '';
results.forEach(result => {
const div = document.createElement('div');
div.className = 'result-page';
const confidenceColor = getConfidenceColor(result.confidence);
div.innerHTML = `
<h3>第 ${result.page} 页 -
<span style="color: ${confidenceColor};">
置信度: ${result.confidence.toFixed(1)}%
</span>
</h3>
<pre>${escapeHtml(result.text)}</pre>
`;
container.appendChild(div);
});
}
置信度分数可以帮助用户快速定位可能有问题的页面,优先校对那些低分结果。比如一份合同,如果某页的置信度只有60%,那这页的日期、金额、签名等关键信息就需要人工核实。
检查提取的文本
识别完成后,用户需要能方便地查看和校对结果。我做了几个设计:
分页展示
每页的结果独立显示,用户可以看到每页的原始文本和置信度。如果某页识别结果不理想,可以单独重新处理那一页。
直接编辑
提取的文本放在<pre>标签里,用户可以直接在页面上修改。如果OCR把"John Smith"识别成了"John Sm1th",用户当场就能改过来。
复制到剪贴板
提供一个"复制全部"按钮,一键复制所有文本:
document.getElementById('copy-button').addEventListener('click', () => {
const text = results.map(r => r.text).join('\n\n--- 第 X 页分隔线 ---\n\n');
navigator.clipboard.writeText(text).then(() => {
alert('已复制到剪贴板');
});
});
这里用navigator.clipboard.writeText(),现代浏览器都支持。如果浏览器不支持,可以降级到document.execCommand('copy')。
导出OCR结果
除了复制,用户可能还需要把结果保存为文件。我支持两种导出格式:
TXT格式
最通用的纯文本格式,几乎所有应用都能打开。
function exportAsTXT(results) {
const text = results.map(r =>
`=== 第 ${r.page} 页 (置信度: ${r.confidence.toFixed(1)}%) ===\n${r.text}`
).join('\n\n');
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ocr-result.txt';
a.click();
URL.revokeObjectURL(url);
}
JSON格式
结构化数据,适合程序进一步处理。包含每页的页码、文本和置信度。
function exportAsJSON(results) {
const data = results.map(r => ({
page: r.page,
text: r.text,
confidence: r.confidence
}));
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ocr-result.json';
a.click();
URL.revokeObjectURL(url);
}
导出时,我用URL.createObjectURL()生成一个临时URL,然后模拟点击下载。下载完成后调用revokeObjectURL()释放内存,避免内存泄漏。
性能优化建议
在真实项目中使用时,我发现几个性能瓶颈和优化点:
1. 页面渲染缩放
PDF.js渲染时,scale参数直接影响渲染质量和内存占用。对于OCR来说,scale: 2.0通常足够,再高只会增加处理时间而不会提升识别率。如果文档文字很小,可以适当调高到3.0。
2. 图像增强的顺序
如果同时启用多个增强选项,顺序会影响效果。我推荐的顺序是:先灰度化 → 再增强对比度 → 最后锐化。灰度化可以减少后续处理的数据量,对比度增强让文字更清晰,锐化作为最后一步强化边缘。
3. 并发处理
Tesseract.js支持并发识别,但浏览器环境有资源限制。我测试过同时处理2-3页,速度有提升,但超过4页反而会因为内存争抢变慢。建议控制在2-3个并发。
// 并发控制示例
async function processWithConcurrency(pages, concurrency = 2) {
const results = [];
const queue = [...pages];
async function worker() {
while (queue.length > 0) {
const pageNum = queue.shift();
const result = await processPage(pageNum);
results.push(result);
}
}
const workers = Array(concurrency).fill().map(() => worker());
await Promise.all(workers);
return results;
}
4. 内存管理
处理大PDF时,canvas对象会占用大量内存。处理完一页后,如果不需要预览,可以释放canvas:
function releaseCanvas(canvas) {
canvas.width = 0;
canvas.height = 0;
canvas.getContext('2d').clearRect(0, 0, 0, 0);
}
5. 语言包缓存
Tesseract.js的语言包下载后会被缓存。如果用户频繁切换语言,可以考虑预加载常用语言包:
// 预加载中英文语言包
await Tesseract.initialize({ languages: 'eng+chi_sim' });
实际使用中的重要注意事项
我在开发和测试过程中踩了不少坑,分享几个最重要的:
1. PDF质量决定OCR上限
OCR不是魔法。如果原始扫描件模糊、倾斜、有折痕或污渍,识别率会大幅下降。对于重要文档,建议用300 DPI以上的分辨率扫描。
2. 字体识别有局限
Tesseract对标准印刷体(如Arial、Times New Roman)识别率很高,但对手写体、艺术字体、极小的字号(<8pt)或者特殊符号(如数学公式)表现不佳。
3. 中文识别需要更多资源
中文语言包(chi_sim)比英文包大得多(约15MB vs 5MB),下载和加载时间更长。而且中文识别对图像质量要求更高,因为汉字结构复杂。
4. 处理时间与页数成正比
一个简单的经验公式:英文文档每页约1-3秒,中文文档每页约3-8秒(取决于页面内容密度和图像质量)。处理100页的文档可能需要几分钟。
5. 浏览器标签页休眠
如果用户切换到其他标签页,浏览器可能会降低当前标签页的优先级,导致OCR变慢甚至超时。可以在代码里检测document.visibilityState:
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.warn('用户离开了页面,OCR可能会变慢');
}
});
6. 大文件处理
超过50页或100MB的PDF,浏览器可能会崩溃。建议在代码中加入文件大小检查:
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
if (file.size > MAX_FILE_SIZE) {
alert('文件过大,建议分割后处理');
return;
}
常见错误及避免方法
错误1:未检查文件类型
用户可能上传图片或其他格式。一定要做类型验证。
错误2:忽略CORS问题
如果从不同源加载PDF.js或Tesseract.js,可能会遇到CORS错误。建议使用CDN或者自托管这些库。
错误3:未处理Tesseract.js初始化失败
网络问题可能导致语言包下载失败。加入重试机制:
async function initializeOCR(language, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await Tesseract.initialize({ languages: language });
return;
} catch (error) {
console.warn(`初始化失败,第${i+1}次重试...`);
if (i === retries - 1) throw error;
}
}
}
错误4:内存泄漏
每次OCR处理都会创建新的canvas和worker。如果不及时清理,浏览器内存会持续增长。用完后要释放资源。
错误5:忽略用户反馈
OCR结果可能有错误。让用户能编辑结果、重新处理特定页面,而不是只能接受或放弃。
错误6:没有处理空结果
有些页面可能完全没有文字(比如纯图片页)。代码要能处理空文本的情况,而不是报错。
总结
我们一起构建了一个完全在