PDF.js 生態中如何處理“添加注釋\添加批注”以及 annotations.contents 屬性

我們來詳細解釋一下在 PDF.js 生態中如何處理“添加注釋”以及 annotations.contents 屬性。

核心要點:PDF.js 本身主要是閱讀器,不是編輯器

首先,最重要的一點是:PDF.js 的核心庫 (pdfjs-dist) 主要設計用于解析和渲染(顯示)PDF 文件,它本身并不提供直接修改 PDF 文件內容(包括添加、刪除或修改注釋并將其永久保存回原始 PDF 文件)的內置功能。

當你看到 PDF.js 的演示查看器(Viewer)允許你添加高亮、文本注釋、繪圖等時,這些操作通常是在瀏覽器層面實現的:

  1. 交互式添加:用戶通過 UI 工具在 PDF 的渲染層之上進行繪制或輸入。
  2. 臨時存儲:這些新創建的注釋信息(類型、位置、顏色、文本內容等)通常被存儲在瀏覽器中(例如,使用瀏覽器的 localStorage 或一個專門的 AnnotationStorage 對象),或者只是存在于當前的會話內存中。
  3. 渲染疊加:查看器將這些存儲的注釋信息在相應的頁面上渲染出來,看起來就像它們是 PDF 的一部分。
  4. 保存(可選,通常需要額外實現):要將這些注釋永久保存到 PDF 文件中,需要一個額外的步驟,通常涉及:
    • 將注釋數據發送到服務器。
    • 服務器使用一個能夠修改 PDF 文件的庫(例如 Node.js 的 pdf-lib,Java 的 iText/PDFBox,Python 的 PyPDF2/ReportLab 等)來解析原始 PDF,將新的注釋對象按照 PDF 規范添加到相應的頁面字典中,然后生成一個新的、包含注釋的 PDF 文件。
    • 或者,在客戶端使用像 pdf-lib 這樣的庫直接在瀏覽器中修改 PDF(這可能對性能要求較高,且需要用戶下載修改后的新文件)。

關于 annotations.contents

  • 讀取時: 當你使用 page.getAnnotations() 獲取已存在于 PDF 文件中的注釋時,contents 屬性是 PDF 規范中定義的注釋字典(Annotation Dictionary)里的 /Contents 鍵對應的值。這通常用于存儲:

    • 文本注釋(Sticky Note,類型為 ‘Text’)的彈出窗口中顯示的文本。
    • 自由文本注釋(Free Text,類型為 ‘FreeText’)框中顯示的文本。
    • 某些其他注釋類型可能用它來存儲描述性文本。
    // (假設你已經獲取了 page 對象)
    const annotations = await page.getAnnotations();
    annotations.forEach(anno => {if (anno.subtype === 'Text' || anno.subtype === 'FreeText') {// 讀取已存在注釋的 contentsconsole.log(`注釋類型: ${anno.subtype}, 內容: ${anno.contents}`);}// 其他類型的注釋可能沒有 'contents' 或其含義不同
    });
    
  • 添加時(在 Viewer 或通過外部庫): 當你想要添加一個新的文本類注釋時,你需要設置這個 contents 屬性為你希望注釋包含的文本內容。

    • 在 PDF.js Viewer 中:當你使用文本工具添加注釋并輸入文字時,查看器內部的邏輯會將你輸入的文字賦值給它正在創建或管理的注釋對象的 contents 屬性(以及其他必要的屬性如 rect, subtype, color 等)。
    • 使用外部庫(如 pdf-lib)添加時:你需要手動構建一個符合 PDF 規范的注釋字典對象,并在其中包含 /Contents 鍵(在 JavaScript 對象中通常是 contents 屬性),然后將這個字典添加到頁面的 /Annots 數組中。

示例:使用 pdf-lib 在瀏覽器或 Node.js 中添加帶 contents 的文本注釋(概念性)

這個例子不是使用 PDF.js,而是展示了如何用一個能夠修改 PDF 的庫 (pdf-lib) 來完成這個任務,這通常是實現“永久添加注釋”所需要的方法。

// 需要先安裝 pdf-lib: npm install pdf-lib
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';async function addTextAnnotation(inputPdfBytes, pageIndex, textContent, rect) {// 加載 PDF 文檔const pdfDoc = await PDFDocument.load(inputPdfBytes);const pages = pdfDoc.getPages();const targetPage = pages[pageIndex]; // 獲取要添加注釋的頁面 (0-based index)// 獲取頁面的尺寸,用于可能的坐標計算const { width, height } = targetPage.getSize();// 準備字體 (對于 FreeText 可能需要)const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);// 創建一個文本注釋 (Sticky Note / Popup)// 注意:添加注釋通常涉及創建注釋本身和可能的彈出窗口 (Popup)// 這里簡化,僅展示核心概念 - pdf-lib 可能有更高級的 API// 需要查閱 pdf-lib 文檔以獲取創建特定注釋類型的準確方法// 假設我們要創建一個 'Text' (Sticky Note) 注釋// 1. 定義注釋的外觀字典 (Appearance Dictionary - /AP) - 這部分復雜,pdf-lib 可能簡化了它// 2. 創建注釋字典const textAnnotationDict = pdfDoc.context.obj({Type: 'Annot', // PDF 對象類型Subtype: 'Text', // 注釋子類型:文本注釋 (Sticky Note)Rect: rect, // 注釋在頁面上的位置 [lowerLeftX, lowerLeftY, upperRightX, upperRightY]Contents: textContent, // <--- 設置注釋的文本內容C: [1, 1, 0], // 顏色 (RGB, e.g., Yellow)T: '作者名稱', // 標題 (可選)M: new Date().toISOString(), // 修改日期 (可選)Name: 'Comment', // 圖標名稱 (e.g., 'Comment', 'Note')Open: false, // 初始狀態是否打開 Popup (通常 false)// 可能還需要 /P (頁面引用), /Popup (關聯的彈出窗口對象) 等});// 將注釋字典添加到頁面的 /Annots 數組// pdf-lib 提供了更方便的方法來添加注釋,而不是直接操作字典:// (請查閱 pdf-lib 文檔,以下為示意)targetPage.node.addAnnot(textAnnotationDict); // 這行是示意,具體 API 可能不同// 另存為新的 PDF 文件const pdfBytes = await pdfDoc.save();// 返回修改后的 PDF 文件字節流 (Uint8Array)return pdfBytes;
}// --- 使用示例 (假設在瀏覽器中) ---
async function handleFileSelect(event) {const file = event.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = async (e) => {const inputPdfBytes = new Uint8Array(e.target.result);try {const newPdfBytes = await addTextAnnotation(inputPdfBytes,0, // 添加到第一頁 (index 0)'這是我用 pdf-lib 添加的注釋內容!', // 設置 contents[50, 700, 200, 750] // 注釋的位置 [x1, y1, x2, y2] (從左下角算起));// 讓用戶下載修改后的 PDFconst blob = new Blob([newPdfBytes], { type: 'application/pdf' });const link = document.createElement('a');link.href = URL.createObjectURL(blob);link.download = 'annotated_document.pdf';link.click();URL.revokeObjectURL(link.href);} catch (error) {console.error("添加注釋失敗:", error);}};reader.readAsArrayBuffer(file);
}

總結

  • 使用 PDF.js 的 page.getAnnotations() 可以讀取 PDF 文件中已有注釋的 contents 屬性。
  • PDF.js 的核心庫不能直接用于添加注釋并永久保存到 PDF 文件中。
  • PDF.js 的查看器可以在界面上創建和顯示注釋,但這些注釋默認是臨時存儲的(如 localStorage),需要額外的工作才能將其永久保存到 PDF 文件中。
  • 永久添加注釋(包括設置 contents),你需要:
    • 要么將注釋數據發送到服務器,使用服務器端的 PDF 修改庫來處理。
    • 要么在客戶端使用像 pdf-lib 這樣的 JavaScript PDF 修改庫來直接操作 PDF 文件字節流,然后生成一個新的、包含注釋的 PDF 文件供用戶下載。
  • 當你使用這些修改庫添加注釋時,你需要按照 PDF 規范構建注釋對象,并將所需的文本內容賦值給 contents 屬性(或 PDF 字典中的 /Contents 鍵)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/79672.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79672.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/79672.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

當HTTP遇到SQL注入:Java開發者的攻防實戰手冊

一、從HTTP請求到數據庫查詢:漏洞如何產生? 危險的參數拼接:Servlet中的經典錯誤 漏洞代碼重現: public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String category = request.getParameter("…

【grafana原生告警中心配置飛書機器人告警】

在grafana中的connect point中使用webhook的方式推送到飛書&#xff0c;始終無法觸發告警&#xff0c;原因是grafana推送的格式飛書不識別&#xff0c;現有兩種方式 1.使用中轉服務 使用flask搭建一個服務&#xff0c;grafana告警先通過webhook發送到web服務中&#xff0c;格…

kafka集群認證

1、安裝Kerberos(10.10.10.168) yum install krb5-server krb5-workstation krb5-libs -y ? 查看版本 klist -V ? Kerberos 5 version 1.20.1 ? 編輯/etc/hosts 10.10.10.168 ms1 10.10.10.150 ms2 10.10.10.110 ms3 vim /etc/krb5.conf # Configuration snippets ma…

前端工程化之自動化測試

自動化測試 自動化測試為什么需要測試&#xff1f;什么時候需要考慮測試測試類型前端測試框架單元測試Jest 重點掌握項目示例package.jsonsrc/utils/math.tssrc/utils/math.test.ts進行測試jest.config.js覆蓋率直觀看覆蓋率coverage/lcov-report/index.html src/main.test.tst…

分布式系統核心原理

CAP定理與權衡實踐 CAP定理 一致性&#xff08;Consistency&#xff09; 強一致性&#xff1a;所有讀寫操作均基于最新數據&#xff08;如銀行轉賬&#xff09;。 最終一致性&#xff1a;數據副本經過一段時間后達到一致&#xff08;如社交媒體的點贊數&#xff09;。 技術實現…

Step文件無法編輯怎么辦?

Step文件無法編輯怎么辦&#xff1f; 這里介紹兩種方法&#xff0c; 1、 直接導入 準備step文件&#xff0c;solidworks導入后是這樣&#xff0c;不能在上面直接編輯 圖 1 點擊右鍵&#xff0c;選擇解除特征&#xff08;不同版本的可能不太一樣&#xff0c;這里是solidworks2…

TIM_ITConfig() 和 TIM_Cmd()

在STM32的定時器中斷配置中&#xff0c;TIM_ITConfig() 和 TIM_Cmd() 是兩個關鍵函數&#xff0c;它們分別控制中斷使能和定時器計數器的啟停&#xff0c;作用層級不同。以下是詳細解釋&#xff1a; 1. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE) 作用 啟用定時器的特定中斷…

TensorFlow 實現 Mixture Density Network (MDN) 的完整說明

本文檔詳細解釋了一段使用 TensorFlow 構建和訓練混合密度網絡&#xff08;Mixture Density Network, MDN&#xff09;的代碼&#xff0c;涵蓋數據生成、模型構建、自定義損失函數與預測可視化等各個環節。 1. 導入庫與設置超參數 import numpy as np import tensorflow as t…

數據結構實驗7.2:二叉樹的基本運算

文章目錄 一&#xff0c;實驗目的二&#xff0c;問題描述三&#xff0c;基本要求四&#xff0c;實驗操作五&#xff0c;示例代碼六&#xff0c;運行效果 一&#xff0c;實驗目的 深入理解樹與二叉樹的基本概念&#xff0c;包括節點、度、層次、深度等&#xff0c;清晰區分二叉…

直線軸承常規分類知多少?

直線軸承的分類方式多樣&#xff0c;以下是從材質、結構形狀和常規系列三個維度進行的具體分類&#xff1a; 按主要材質分類 外殼材質&#xff1a;常見的有不銹鋼&#xff0c;具有良好的耐腐蝕性&#xff0c;適用于一些對環境要求較高、易受腐蝕的工作場景&#xff1b;軸承…

websocket和SSE學習記錄

websocket學習記錄 websocket使用場景 即時聊天在線文檔協同編輯實施地圖位置 從開發角度來學習websocket開發 即使通信項目 通過node建立簡單的后端接口,利用fs&#xff0c; path&#xff0c; express app.get(*, (req, res) > {const assetsType req.url.split(/)[…

CUDA編程中影響性能的小細節總結

一、內存訪問優化 合并內存訪問&#xff1a;確保相鄰線程訪問連續內存地址&#xff08;全局內存對齊訪問&#xff09;。優先使用共享內存&#xff08;Shared Memory&#xff09;減少全局內存訪問。避免共享內存的Bank Conflict&#xff08;例如&#xff0c;使用padding或調整訪…

【雙指針】對撞指針 快慢指針 移動零

文章目錄 雙指針介紹對撞指針快慢指針283. 移動零解題思路算法思路算法流程雙指針介紹 ? 算法中的雙指針,并不一定是指我們平常在 c/c++ 使用的指針類型,更多時候其實是數組的下標等,因為它們也是有標識某個元素的功能,通常我們也就順其自然地稱其為 “指針” ! ? 常見…

數據結構0基礎學習堆

文章目錄 簡介公式建立堆函數解釋 堆排序O(n logn)topk問題 簡介 堆是一種重要的數據結構&#xff0c;是一種完全二叉樹&#xff0c;&#xff08;二叉樹的內容后面會出&#xff09;&#xff0c; 堆分為大小堆&#xff0c;大堆&#xff0c;左右結點都小于根節點&#xff0c;&am…

4.17--4.19刷題記錄(貪心)

第一部分&#xff1a;準備工作 代碼隨想錄中解釋為&#xff1a;貪心的本質是選擇每一階段的局部最優&#xff0c;從而達到全局最優。 而我的理解為&#xff1a;貪心實質上是具有最優子結構的一種算法。所有的解都能由當前最優的解組成。 第二部分&#xff1a;開始刷題 &…

學習筆記十七——Rust 支持面向對象編程嗎?

&#x1f9e0; Rust 支持面向對象編程嗎&#xff1f; Rust 是一門多范式語言&#xff0c;主要以 安全、并發、函數式、系統級編程為核心目標&#xff0c;但它同時也支持面向對象的一些關鍵特性&#xff0c;比如&#xff1a; 特性傳統 OOP&#xff08;如 Java/C&#xff09;Ru…

【Linux】43.網絡基礎(2.5)

文章目錄 2.4 TCP/UDP對比2.4.1 用UDP實現可靠傳輸(經典面試題) 2.5 TCP 相關實驗2.5.1 理解 listen 的第二個參數 2.4 TCP/UDP對比 我們說了TCP是可靠連接, 那么是不是TCP一定就優于UDP呢? TCP和UDP之間的優點和缺點, 不能簡單, 絕對的進行比較TCP用于可靠傳輸的情況, 應用于…

three.js與webgl在buffer上的對應關系

一、three.js的類名 最近開始接觸three.js 看到three.js中的一些類名和webgl的很相似 不自覺的就想對比一下 二、three.js中繪制4個點 // 創建點的幾何體 const vertices new Float32Array([0.0, 0.0, 0.0, // 點11.0, 0.0, 0.0, // 點20.0, 1.0, 0.0, // 點30.…

DataWhale AI春訓營 問題匯總

1.沒用下載訓練集導致出錯&#xff0c;爆錯如下。 這個時候需要去比賽官網下載對應的初賽訓練集 unzip -d /mnt/workspace/sais_third_new_energy_baseline/data /mnt/workspace/sais_third_new_energy_baseline/初賽訓練集.zip 在命令行執行這個命令解壓 2.沒定義測試集 te…

CANFD技術在新能源汽車通信網絡中的應用與可靠性分析

一、引言 新能源汽車產業正處于快速發展階段&#xff0c;其電子系統復雜度不斷攀升&#xff0c;涵蓋眾多傳感器、控制器與執行器。高效通信網絡成為確保新能源汽車安全運行與智能功能實現的核心要素。傳統CAN總線因帶寬限制&#xff0c;難以滿足高級駕駛輔助系統&#xff08;A…