跳至內容
Astera Technology

PDF 蓋了字,仍要核能否抽出原文

比較視覺蓋層與生成前移除該字,範圍只限題內內容。

下載完整個案資料及參考答案 ↓

訂判斷及所需輸入

測試 PDF 把 SECRETALPHA 放黑色蓋層下,另有 VISIBLE。此檔案即使視覺蓋住,仍可抽到 SECRETALPHA;更正路徑要在生成新 PDF 前,從文件內容省略該字。

已填本地測試工作表
情況輸入 JSON
原例測試{"secret":"SECRETALPHA","remove":true}
下載可編輯 CSV ↓

跑所附比較

兩個 PDF 都在記憶體生成,再用 PDF.js 抽字。只核本例指定字及仍須保留的可見字;未確認全部 metadata、註解、內嵌內容,亦不是通用安全刪改程序。

錯誤及所需輸出
路徑輸出 JSON
錯誤路徑{"secretExtractable":true,"visibleExtractable":true,"pageCount":1}
所需路徑{"secretExtractable":false,"visibleExtractable":true,"pageCount":1}
下載可編輯 CSV ↓
Node24.14.1 程序;須 Playwright1.63 Chromium 及 PDF.js6.4.299
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {pathToFileURL} from 'node:url';
const htmlEscape=s=>String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
async function withPage(action,options={}){
  const require=createRequire(import.meta.url);const {chromium}=require('playwright');
  const browser=await chromium.launch({headless:true});
  try{const context=await browser.newContext(options);const page=await context.newPage();
    const requests=[];await page.route('**/*',route=>{requests.push(route.request().url());return route.abort();});
    return await action(page,context,requests);
  }finally{await browser.close();}
}
async function pdfObservation(html){
  const bytes=await withPage(async page=>{await page.setContent(html);return page.pdf({preferCSSPageSize:true,printBackground:true});});
  const require=createRequire(import.meta.url);
  const {getDocument}=await import(pathToFileURL(require.resolve('pdfjs-dist/legacy/build/pdf.mjs')).href);
  const loadingTask=getDocument({data:Uint8Array.from(bytes),useSystemFonts:true,disableFontFace:true});
  try{const document=await loadingTask.promise;const pages=[];for(let n=1;n<=document.numPages;n++){
    const page=await document.getPage(n);const content=await page.getTextContent();
    pages.push(content.items.filter(x=>typeof x.str==='string'&&x.str).map(x=>({text:x.str,x:x.transform[4],y:x.transform[5]})));
  }return {pageCount:document.numPages,pages};}finally{await loadingTask.destroy();}
}
const printStyle='<style>@page{size:100mm 100mm;margin:10mm}body{margin:0;font:10px Arial}p{margin:0}</style>';

const run=async function run(i,corrected=true){
    if(typeof i.remove!=='boolean'||typeof i.secret!=='string'||!/^[A-Z]{1,20}$/.test(i.secret)||i.secret==='VISIBLE')return {held:true};
    const secret=corrected&&i.remove?'':i.secret;
    const observation=await pdfObservation(printStyle+'<div style="position:relative;height:10mm"><p>'+htmlEscape(secret)+'</p><div style="position:absolute;inset:0;background:black"></div></div><p>VISIBLE</p>');
    const words=observation.pages.flat().map(x=>x.text);
    return {secretExtractable:words.includes(i.secret),visibleExtractable:words.includes('VISIBLE'),pageCount:observation.pageCount};
  };
const cases=[{"input":{"secret":"SECRETALPHA","remove":true},"corrected":false,"expected":{"secretExtractable":true,"visibleExtractable":true,"pageCount":1}},{"input":{"secret":"SECRETALPHA","remove":true},"corrected":true,"expected":{"secretExtractable":false,"visibleExtractable":true,"pageCount":1}},{"input":{"secret":"SECRETALPHA","remove":false},"corrected":true,"expected":{"secretExtractable":true,"visibleExtractable":true,"pageCount":1}},{"input":{"secret":"SECRETALPHA","remove":null},"corrected":true,"expected":{"held":true}}];
for(const test of cases){const actual=await run(test.input,test.corrected);assert.deepEqual(actual,test.expected);console.log(actual);}
下載可執行例子 ↓

按邊界依據作判斷

已訂保留就照留;移除決定未知便停,不能因有黑格就推已刪字。

  1. 訂明指定字怎樣處理
  2. 生成所需新檔案
  3. 抽字核指定內容

視覺蓋層不能證明儲存文字已移除。

可編輯邊界輸入及結果
條件輸入 JSON預期輸出 JSON
已訂保留該字{"secret":"SECRETALPHA","remove":false}{"secretExtractable":true,"visibleExtractable":true,"pageCount":1}
移除決定未知{"secret":"SECRETALPHA","remove":null}{"held":true}
下載可編輯 CSV ↓

原例更正後抽字,確立了甚麼?

查看參考答案

這份新 PDF 抽不到 SECRETALPHA,VISIBLE 仍在,共一頁。只屬該檔案的有限結果,不是通用安全保證。

例子檔案

相關資源