如何在 C# (.NET) 中移除 PDF 與文件的註解

想像一下:您正在開發文件管理系統,使用者抱怨 PDF 充斥著過時的評論與標記,變得雜亂。或者您需要在將文件發送給客戶前先清理。聽起來很熟悉吧?

以程式方式移除 pdf 註解 不僅是可有可無的功能──它是自動化工作流程中維持文件乾淨、專業的必要條件。無論您處理的是法律合約、技術文件,或是協同審閱,了解如何有效剝除不需要的註解都能為您節省大量手動工作時間。

讓我們深入了解,讓您的註解移除順利運作。

快速解答

  • 此程式碼的功能是什麼? 它會載入文件、過濾不需要的註解,並儲存一個乾淨的副本。
  • 我可以只刪除特定的註解嗎? 可以──透過類型、作者、頁碼或自訂中繼資料進行過濾。
  • 需要授權嗎? 免費 30 天試用可用於開發;商業使用則需正式授權。
  • 大型 PDF 會導致記憶體問題嗎? 使用 using 區塊與批次處理以降低記憶體使用量。
  • 這能支援除 PDF 之外的格式嗎? 當然可以──GroupDocs.Annotation 支援 Word、Excel、PowerPoint 等多種格式。

什麼是 GroupDocs.Annotation?

GroupDocs.Annotation 是一個 .NET 函式庫,可讓您在超過 30 種檔案格式(包括 PDF、DOCX、XLSX、PPTX)中新增、讀取、編輯與刪除註解。它在不將整個檔案載入記憶體的情況下處理最高 500 MB 的文件,非常適合高流量的伺服器環境。

為什麼要以程式方式移除註解?

自動化移除註解可確保每份通過工作流程的文件皆乾淨、專業且符合規範。它消除手動操作、降低意外資料外洩風險,並使檔案大小保持較小,便於儲存與索引。

  • 自動化就緒 – 可在每個工作流程階段自動產生乾淨的版本。
  • 專業交付 – 客戶端 PDF 不會出現零星的評論或標記。
  • 法規遵循 – 某些行業禁止在提交的文件中留下隱藏評論。
  • 儲存效能 – 移除註解的 PDF 體積更小,索引速度更快。

前置條件與設定

開發環境

  • .NET Core 3.1、.NET 5+ 或 .NET Framework 4.7.2+
  • Visual Studio 2022(或您偏好的任何 C# IDE)
  • 具備 using 陳述式與例外處理的基本概念

必要套件

GroupDocs.Annotation for .NET(範例使用 25.4.0 版;較新版本亦完全相容)。

安裝 GroupDocs.Annotation

Package Manager Console(最常用):

Install-Package GroupDocs.Annotation -Version 25.4.0

Package Manager UI: 搜尋 “GroupDocs.Annotation” 並安裝最新的穩定版。

.NET CLI(如果您習慣使用指令列):

dotnet add package GroupDocs.Annotation --version 25.4.0

取得授權設定

正式環境需要授權檔案。您可以先使用免費試用版。

開發/測試用:

  1. 前往 Temporary License Page
  2. 申請 30 天評估授權
  3. 透過電子郵件收到 .lic 檔案

基本授權設定:
License 為 GroupDocs.Annotation 提供的類別,用於將授權檔套用至函式庫。

using System;
using GroupDocs.Annotation;

class Program
{
    static void Main(string[] args)
    {
        // Set up your license (skip this during trial period)
        License lic = new License();
        lic.SetLicense("path-to-your-license.lic");
        
        Console.WriteLine("GroupDocs.Annotation is ready to rock!");
    }
}

小技巧: 將授權檔存放於安全位置,並使用 License license = new License(); license.SetLicense("path/to/license.lic"); 載入。切勿在正式環境中硬編碼絕對路徑。

步驟式實作指南

如何移除特定 PDF 註解?

本節說明如何載入 PDF、辨識要刪除的註解,並在保留原始內容的同時儲存清理後的副本。

步驟 1:載入文件

Annotator 為 GroupDocs.Annotation 的核心類別,用於開啟檔案並取得其註解集合。

string inputFilePath = "YOUR_DOCUMENT_DIRECTORY/ANNOTATED.pdf"; // Replace with your actual path

using (Annotator annotator = new Annotator(inputFilePath))
{
    // All the magic happens inside this using block
    // The using statement ensures proper resource cleanup
}

常見問題: 確認檔案路徑正確且檔案未被其他程序鎖定。路徑拼寫錯誤常導致 “找不到檔案” 的錯誤。

步驟 2:取得並過濾註解

Annotation 物件代表單一的標記項目,如評論、醒目標示或印章。您可以在決定刪除前檢查每個註解的類型、作者、頁碼或自訂中繼資料。

var annotations = annotator.Get();
Console.WriteLine($"Found {annotations.Count} annotations in the document");

// Let's see what we're working with
foreach (var annotation in annotations)
{
    Console.WriteLine($"Type: {annotation.Type}, Page: {annotation.PageNumber}");
}

// Remove the first annotation (basic example)
if (annotations.Count > 0)
{
    Console.WriteLine($"Removing annotation of type: {annotations[0].Type}");
    annotator.Remove(annotations[0]);
}

為什麼這樣有效: 先過濾可避免在清除內部評論時不小心刪除有用的標記(例如法律文件的醒目標示)。

步驟 3:儲存清理後的文件

為清理後的檔案取一個不同的名稱(例如加上 cleaned_ 前綴或時間戳記),以免覆寫原始檔案。

string outputDirectory = "YOUR_OUTPUT_DIRECTORY"; // Your output folder
string outputPath = Path.Combine(outputDirectory, "cleaned_" + Path.GetFileName(inputFilePath));

// Save the document with annotations removed
annotator.Save(outputPath);
Console.WriteLine($"Clean document saved to: {outputPath}");

檔名策略: cleaned_2024_09_15_myfile.pdf 可輕鬆追蹤處理日期。

如何一次移除所有 PDF 註解(徹底方式)?

當您需要徹底清空時,此方法可一次移除所有註解。

RemoveAll 會從已載入的文件中移除所有註解。

string inputFilePath = "YOUR_DOCUMENT_DIRECTORY/ANNOTATED.pdf";
string outputPath = "YOUR_OUTPUT_DIRECTORY/completely_clean.pdf";

using (Annotator annotator = new Annotator(inputFilePath))
{
    var annotations = annotator.Get();
    
    if (annotations.Count > 0)
    {
        Console.WriteLine($"Removing all {annotations.Count} annotations...");
        
        // Remove all annotations in one go
        foreach (var annotation in annotations)
        {
            annotator.Remove(annotation);
        }
        
        annotator.Save(outputPath);
        Console.WriteLine("All annotations removed successfully!");
    }
    else
    {
        Console.WriteLine("No annotations found in the document.");
    }
}

常見陷阱與解決方案

問題 1:「檔案被鎖定」例外

徵兆: 出現檔案被使用中的例外。
解決方案: 使用 using 陳述式包住檔案存取,並確保沒有其他程序持有檔案句柄。

// DON'T do this
var annotator1 = new Annotator(filePath);
var annotator2 = new Annotator(filePath); // This might fail

// DO this instead
using (var annotator = new Annotator(filePath))
{
    // All your work here
} // Automatically disposed and file is released

問題 2:註解未實際移除

徵兆: 程式執行後註解仍在。
常見原因: 可能檢查了錯誤的輸出檔,或過濾了錯誤的註解類型。
除錯方式:

var annotations = annotator.Get();
foreach (var annotation in annotations)
{
    Console.WriteLine($"ID: {annotation.Id}, Type: {annotation.Type}");
    Console.WriteLine($"Page: {annotation.PageNumber}, Author: {annotation.User}");
}

問題 3:大型文件的記憶體問題

徵兆: 處理超過 100 MB 的 PDF 時崩潰或嚴重變慢。
解決方案: 以批次方式處理文件,並即時釋放資源。

// For very large documents, consider processing page by page
using (var annotator = new Annotator(largePdfPath))
{
    var annotations = annotator.Get();
    
    // Process in chunks of 50 annotations
    for (int i = 0; i < annotations.Count; i += 50)
    {
        var batch = annotations.Skip(i).Take(50);
        foreach (var annotation in batch)
        {
            annotator.Remove(annotation);
        }
        
        // Optional: Force garbage collection for very large documents
        GC.Collect();
    }
    
    annotator.Save(outputPath);
}

效能優化建議

批次處理策略

將註解收集至清單中,一次批次刪除以減少 API 呼叫次數。

using (var annotator = new Annotator(inputPath))
{
    var annotations = annotator.Get();
    var toRemove = annotations.Where(a => ShouldRemoveAnnotation(a)).ToList();
    
    Console.WriteLine($"Removing {toRemove.Count} out of {annotations.Count} annotations");
    
    // Remove all at once instead of individual Remove() calls
    foreach (var annotation in toRemove)
    {
        annotator.Remove(annotation);
    }
    
    annotator.Save(outputPath);
}

bool ShouldRemoveAnnotation(AnnotationBase annotation)
{
    // Your custom logic here
    return annotation.Type == AnnotationType.Area || 
           annotation.CreatedOn < DateTime.Now.AddMonths(-6);
}

記憶體管理最佳實踐

  • 始終使用 using 陳述式以自動釋放。
  • 切勿同時載入多個大型 PDF。
  • 當記憶體受限時,請以順序方式處理文件,而非平行執行。

快取授權物件

在應用程式啟動時建立一次 License 實例,之後在每次處理文件時重複使用。

public class DocumentProcessor
{
    private static readonly License _license = new License();
    
    static DocumentProcessor()
    {
        _license.SetLicense("your-license-path.lic");
    }
    
    public void ProcessDocument(string filePath)
    {
        // License is already set, just use the annotator
        using (var annotator = new Annotator(filePath))
        {
            // Your processing logic
        }
    }
}

真實案例與範例

情境 1:法律文件工作流程

律師事務所需要將乾淨的合約寄給客戶,同時保留內部評論供內部審閱。

public void PrepareClientDocument(string internalContractPath, string clientVersion)
{
    using (var annotator = new Annotator(internalContractPath))
    {
        var annotations = annotator.Get();
        
        // Remove only internal comments, keep client-facing highlights
        var internalComments = annotations.Where(a => 
            a.Type == AnnotationType.TextField && 
            a.User?.Contains("@lawfirm.com") == true);
            
        foreach (var comment in internalComments)
        {
            annotator.Remove(comment);
        }
        
        annotator.Save(clientVersion);
    }
}

情境 2:自動化報告產生

每月分析報告經過審閱流程,最終發佈版本必須沒有註解。

public void FinalizeReport(string draftPath, string finalPath)
{
    using (var annotator = new Annotator(draftPath))
    {
        var annotations = annotator.Get();
        
        // Remove all review comments but keep approved highlights
        var reviewComments = annotations.Where(a => 
            a.Type == AnnotationType.TextField ||
            a.Type == AnnotationType.Point);
            
        Console.WriteLine($"Cleaning {reviewComments.Count()} review annotations...");
        
        foreach (var annotation in reviewComments)
        {
            annotator.Remove(annotation);
        }
        
        annotator.Save(finalPath);
        Console.WriteLine($"Final report ready: {finalPath}");
    }
}

進階錯誤處理

穩健的正式環境程式碼應預測並記錄最常見的例外,例如 IncorrectPasswordException 或 OutOfMemoryException。

IncorrectPasswordException 於未提供正確密碼而開啟受密碼保護的 PDF 時拋出。

public bool RemoveAnnotationsSafely(string inputPath, string outputPath)
{
    try
    {
        using (var annotator = new Annotator(inputPath))
        {
            var annotations = annotator.Get();
            
            if (annotations.Count == 0)
            {
                Console.WriteLine("No annotations to remove.");
                // Still copy the file to output location
                File.Copy(inputPath, outputPath, overwrite: true);
                return true;
            }
            
            foreach (var annotation in annotations)
            {
                try
                {
                    annotator.Remove(annotation);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Failed to remove annotation {annotation.Id}: {ex.Message}");
                    // Continue with other annotations
                }
            }
            
            annotator.Save(outputPath);
            Console.WriteLine($"Successfully processed document: {Path.GetFileName(outputPath)}");
            return true;
        }
    }
    catch (FileNotFoundException)
    {
        Console.WriteLine($"Input file not found: {inputPath}");
        return false;
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine($"Access denied. Check file permissions for: {inputPath}");
        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Unexpected error: {ex.Message}");
        return false;
    }
}

測試您的實作

簡易單元測試可驗證處理後註解數量降為零。

public void TestAnnotationRemoval()
{
    string testFile = "test-document-with-annotations.pdf";
    string outputFile = "test-output.pdf";
    
    // Before removal
    using (var annotator = new Annotator(testFile))
    {
        var beforeCount = annotator.Get().Count;
        Console.WriteLine($"Annotations before removal: {beforeCount}");
    }
    
    // Remove annotations
    bool success = RemoveAnnotationsSafely(testFile, outputFile);
    
    if (success)
    {
        // After removal
        using (var annotator = new Annotator(outputFile))
        {
            var afterCount = annotator.Get().Count;
            Console.WriteLine($"Annotations after removal: {afterCount}");
            Console.WriteLine($"Removed: {beforeCount - afterCount} annotations");
        }
    }
}

疑難排解指南

  • IncorrectPasswordException – 透過 LoadOptions 提供 PDF 密碼。

LoadOptions loadOptions = new LoadOptions { Password = “your-pdf-password” }; using (var annotator = new Annotator(filePath, loadOptions)) { // Your code here }


- **註解仍可見** – 某些 PDF 閱讀器會快取註解串流;請重新整理或使用其他閱讀器開啟。  

- **OutOfMemoryException** – 將 PDF 分成較小的區塊處理,或提升應用程式的記憶體上限。  

- **某些註解類型無法刪除** – 使用 `annotation.Type` 辨識,並針對表單欄位等特殊類型另行處理。  

## 效能基準測試

根據使用 GroupDocs.Annotation 25.4.0 的內部測試:

- **小型 PDF(< 1 MB、< 50 註解):** < 0.5 秒  
- **中型 PDF(1‑10 MB、50‑200 註解):** 1‑3 秒  
- **大型 PDF(10‑50 MB、200+ 註解):** 5‑15 秒  
- **超大型 PDF(> 50 MB):** 建議使用批次處理以保持每檔案低於 20 秒。  

## 資源
- [GroupDocs.Annotation Documentation](https://docs.groupdocs.com/annotation/net/)  
- [API Reference](https://reference.groupdocs.com/annotation/net/)  
- [Download GroupDocs.Annotation for .NET](https://releases.groupdocs.com/annotation/net/)  
- [Purchase Options](https://purchase.groupdocs.com/buy)  
- [Support Forum](https://forum.groupdocs.com/c/annotation/)  

## 結論

您現在已擁有完整的 **remove pdf annotations**(移除 PDF 註解)工具組於 C# 中。請記得:

1. 使用 `using` 區塊以確保資源正確釋放。  
2. 刪除前先過濾註解,以避免意外資料遺失。  
3. 依上述策略處理受密碼保護的檔案與大型 PDF。  
4. 在正式上線前,以真實文件進行測試。  

將這些模式整合至更廣的文件處理流程,使用者每次都能獲得更乾淨、更專業的 PDF。

## 常見問題

**Q:我可以移除 Word 文件的註解,而不僅是 PDF 嗎?**  
A:可以──GroupDocs.Annotation 支援 DOCX、XLSX、PPTX 以及其他多種格式。載入相應檔案類型後,使用相同的 API 呼叫即可。

**Q:如何只移除特定類型的註解(例如僅評論)?**  
A:在呼叫刪除方法前,先以 `annotation.Type == AnnotationType.Comment` 來過濾註解集合。  
```csharp
var commentsOnly = annotations.Where(a => a.Type == AnnotationType.TextField);

Q:移除註解會影響文件的版面或格式嗎?
A:不會。註解以覆蓋層方式儲存,刪除後不會影響底層內容。

Q:我可以復原註解的移除嗎?
A:函式庫未提供「復原」功能。請務必在原始文件的副本上操作,並保留備份。

Q:如何處理受密碼保護的 PDF?
A:在建立 Annotator 實例時,透過 LoadOptions 傳入密碼。

Q:有沒有辦法依作者刪除註解?
A:可以──檢查 annotation.User 屬性,僅刪除符合指定作者名稱的註解。

Q:隱藏與移除註解有何差異?
A:隱藏僅讓註解在檢視器中不可見;移除則永久從檔案中刪除。GroupDocs.Annotation 只支援移除。


Last Updated: 2026-06-01
Tested With: GroupDocs.Annotation 25.4.0 for .NET
Author: GroupDocs

相關教學