
從Syncfusion OCR遷移到IronOCR
本指南針對需要從掃描文件和PDF中提取文字的.NET開發者,詳細講解了從Syncfusion OCR Processor遷移到IronOCR的完整過程。 它涵蓋了需要更換IronOcr NuGet包的具體配置更改、程式碼重寫和部署清理,特別是消除tessdata文件管理和每個Syncfusion OCR部署都需要的Tesseract二進制路徑配置。
為什麼要從Syncfusion OCR遷移
Syncfusion OCR是嵌入在1600個組件套件中的Tesseract包裝器。對於僅需提取文字的團隊而言,該架構在各個級別上都造成摩擦:設置、部署、維護和授權。
tessdata文件夾跟隨每個環境。 每個開發者工作站、CI運行器、預部署伺服器和生產容器都需要一個包含應用使用語言的.traineddata文件的tessdata目錄。 僅英語標準模式就有23 MB,或者最佳LSTM模式則有94 MB。 一個支持五種語言的應用將為每個部署增加100–500 MB的內容。 該文件夾必須位於OCRProcessor構造函式期望的確切路徑,否則應用在啟動時立即報錯。這不是一次性設置成本,而是每當為新的環境配置時,就需要出現的一次性運營成本。
Tesseract二進制路徑配置在不同環境下中斷。 OCRProcessor構造函式要求tessdata目錄路徑,且必須在每個目標平台上被正確解析。 適用於Windows開發機的路徑(@"tessdata/")在Linux容器中失效,除非部署管道明確複製該文件夾。 Docker映像構建必須包括COPY tessdata/ /app/tessdata/層。 CI管道必須編寫tessdata下載腳本。 氣隙環境必須將二進制文件分發與NuGet包恢復分開管理。 每個環境都帶來新的路徑不匹配機會,導致OCR靜默失敗或運行時異常。
以PDF為中心的架構對圖像輸入施加了轉換開銷。 Syncfusion的OCRProcessor 接受PdfLoadedDocument物件而不是圖像文件。 從JPG中提取文字需要建立PdfLoadedDocument,然後運行OCR——在文字識別步驟前一共有九個操作。這種往返增加了每個圖像優先OCR工作流程的執行開銷和程式碼複雜性。
套件授權創造了因增長而觸發的合規事件。 Syncfusion社區授權要求少於五個開發者,少於十名員工,年收入少於100萬美元,且一生累計外部資金少於300萬美元——這些都需同時滿足。 任何門檻突破都會立即使授權失效,並且需要以每年每位開發者995–1595美元的商業升級。 五人開發團隊使用Syncfusion商業OCR三年需支付14,925–23,925美元,而IronOCR Professional以一次性支付2,999美元便可獲得相同的文字提取能力。
沒有內建的預處理意味著需要外部依賴來處理劣化掃描。 Tesseract在旋轉的、有雜訊或低對比度圖像上結果欠佳,除非經過預處理。 Syncfusion不提供預處理API。 需要轉換、降噪或對比度校正的開發人員必須新增單獨的成像程式庫(System.Drawing, SkiaSharp, ImageSharp),實作濾鏡,並將輸出接線到PDF往返程式中,然後才能開始OCR。 對於IronOCR來說,該功能內建於20–40行程式碼中。
只需OCR功能,但整個套件已授權。 Syncfusion無論實際使用了哪些功能,都會撈取Syncfusion.Compression.Net.Core及其他過渡依賴。 對於構建專注於文件處理服務的團隊,該依賴圖內含不相干於文字提取的組件帶來較大負擔——在建造時間、容器映像大小以及授權成本上。
根本問題
Syncfusion OCR需要在任何OCR呼叫可能發生前配置一個tessdata文件系統路徑:
// Syncfusion: tessdata path required — fails in any environment where this path is wrong
private const string TessDataPath = @"tessdata/";
using var document = new PdfLoadedDocument("scanned-invoice.pdf");
using var processor = new OCRProcessor(TessDataPath); // throws if path does not resolve
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var text = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
text.AppendLine(page.ExtractText());' Syncfusion: tessdata path required — fails in any environment where this path is wrong
Private Const TessDataPath As String = "tessdata/"
Using document As New PdfLoadedDocument("scanned-invoice.pdf")
Using processor As New OCRProcessor(TessDataPath) ' throws if path does not resolve
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim text As New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
text.AppendLine(page.ExtractText())
Next
End Using
End Using' Syncfusion: tessdata path required — fails in any environment where this path is wrong
Private Const TessDataPath As String = "tessdata/"
Using document As New PdfLoadedDocument("scanned-invoice.pdf")
Using processor As New OCRProcessor(TessDataPath) ' throws if path does not resolve
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim text As New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
text.AppendLine(page.ExtractText())
Next
End Using
End UsingIronOCR不需要路徑配置。 語言資料與包一起捆綁:
// IronOCR: no tessdata path, no path configuration, no folder to deploy
var text = new IronTesseract().Read("scanned-invoice.pdf").Text;// IronOCR: no tessdata path, no path configuration, no folder to deploy
var text = new IronTesseract().Read("scanned-invoice.pdf").Text;' IronOCR: no tessdata path, no path configuration, no folder to deploy
Dim text = New IronTesseract().Read("scanned-invoice.pdf").Text' IronOCR: no tessdata path, no path configuration, no folder to deploy
Dim text = New IronTesseract().Read("scanned-invoice.pdf").TextIronOCR與Syncfusion OCR:功能對比
下表涵蓋了從Syncfusion OCR轉換過來的團隊說重要的能力。
| 功能 | Syncfusion OCR | IronOCR |
|---|---|---|
| NuGet包 | Syncfusion.PDF.OCR.Net.Core (套件) | IronOcr (獨立) |
| 需要 tessdata | 是的——手動下載和路徑配置 | 否——內部捆綁 |
| 直接圖像 OCR | 否——需要PDF轉換往返 | 是的——LoadImage() 或直接路徑 |
| 直接PDF OCR | 是的——主要輸入模型 | 是的——一級支持 |
| 自動預處理 | 否——需要外部程式庫 | 是的——矯正、去噪、對比度、二值化 |
| 可搜尋的 PDF 輸出 | 是的——在PerformOCR()之後保存 | 是的——result.SaveAsSearchablePdf() |
| 支持的語言 | 60+人工tessdata下載 | 125+ 通過 NuGet 語言包 |
| 多語言同步 | 是的——位運算標幟於Languages枚舉 | 是的——AddSecondaryLanguage() |
| 基於區域的OCR | 不是 | 是的——CropRectangle |
| 條形碼讀取 | 不是 | 是的——ocr.Configuration.ReadBarCodes = true |
| 結構化輸出 | 僅可通過page.ExtractText()取得頁面 | 頁面、段落、行、單詞、帶有坐標的字元 |
| 置信度評分 | 不是 | 是的——result.Confidence和按單詞計分 |
| hOCR輸出 | 不是 | 是 |
| 流輸入 | 僅通過PDF流 | 圖像和PDF的直接流輸入 |
| 執行緒安全 | 未記錄為執行緒安全 | 全面——每執行緒一個IronTesseract例項 |
| 跨平台 | 是的——但tessdata必須在每個平台上解析 | 是的——單個NuGet,無需路徑配置 |
| Docker部署 | 需要影像中的tessdata層 | 單個包,無額外層 |
| 授權模式 | 年度套件訂閱(每人每年995–1595美元) | 永久藝術品 (輕$999,專業版1,499美元,企業版2,999美元) |
| 社區授權限制 | 規模、員工及資金限制並有審計權 | 免費試用無限制 |
| OCR 引擎 | Tesseract 5 (標準包裝) | 優化Tesseract 5,具更高準確性 |
快速入門:Syncfusion OCR到IronOCR的遷移
步驟1:替換NuGet包
移除Syncfusion OCR以及任何僅因OCR功能而引入的其他Syncfusion包:
dotnet remove package Syncfusion.PDF.OCR.Net.Core
dotnet remove package Syncfusion.Pdf.Net.Core
dotnet remove package Syncfusion.Compression.Net.Core
從NuGet安裝IronOCR:
步驟2:更新命名空間
將Syncfusion命名空間導入替換為單個IronOCR命名空間:
// Before (Syncfusion)
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Parsing;
// After (IronOCR)
using IronOcr;// Before (Syncfusion)
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Parsing;
// After (IronOCR)
using IronOcr;Imports IronOcrImports IronOcr步驟3:初始化許可證
在應用啟動時並在任何OCR呼叫之前一次性新增授權初始化:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"不需要套件註冊。 不需檢查社區授權資格。 關鍵是一個分配給靜態屬性的普通字串。
程式碼遷移範例
Tessdata路徑消除和OCR初始化
Syncfusion程式碼庫通常包含tessdata驗證邏輯——檢查目錄是否存在,以及在嘗試OCR之前需要的.traineddata文件是否存在。 這些保護程式碼存在的原因是因為缺少的tessdata文件會引發運行時異常,而缺少語言文件引發的生產事故經常發生,因此團隊撰寫防禦性檢查。
Syncfusion OCR方法:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class DocumentOcrService
{
// Path hardcoded — different on every deployment target
private const string TessDataPath = @"tessdata/";
private bool ValidateTessdataBeforeUse(string languageCode)
{
// Guard required because missing files cause runtime exceptions
if (!Directory.Exists(TessDataPath))
throw new InvalidOperationException(
"tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best");
string filePath = Path.Combine(TessDataPath, $"{languageCode}.traineddata");
if (!File.Exists(filePath))
throw new InvalidOperationException(
$"{languageCode}.traineddata not found — file must be downloaded manually");
return true;
}
public string ExtractText(string pdfPath, string languageCode = "eng")
{
ValidateTessdataBeforeUse(languageCode); // defensive check before every call
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class DocumentOcrService
{
// Path hardcoded — different on every deployment target
private const string TessDataPath = @"tessdata/";
private bool ValidateTessdataBeforeUse(string languageCode)
{
// Guard required because missing files cause runtime exceptions
if (!Directory.Exists(TessDataPath))
throw new InvalidOperationException(
"tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best");
string filePath = Path.Combine(TessDataPath, $"{languageCode}.traineddata");
if (!File.Exists(filePath))
throw new InvalidOperationException(
$"{languageCode}.traineddata not found — file must be downloaded manually");
return true;
}
public string ExtractText(string pdfPath, string languageCode = "eng")
{
ValidateTessdataBeforeUse(languageCode); // defensive check before every call
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class DocumentOcrService
' Path hardcoded — different on every deployment target
Private Const TessDataPath As String = "tessdata/"
Private Function ValidateTessdataBeforeUse(languageCode As String) As Boolean
' Guard required because missing files cause runtime exceptions
If Not Directory.Exists(TessDataPath) Then
Throw New InvalidOperationException("tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best")
End If
Dim filePath As String = Path.Combine(TessDataPath, $"{languageCode}.traineddata")
If Not File.Exists(filePath) Then
Throw New InvalidOperationException($"{languageCode}.traineddata not found — file must be downloaded manually")
End If
Return True
End Function
Public Function ExtractText(pdfPath As String, Optional languageCode As String = "eng") As String
ValidateTessdataBeforeUse(languageCode) ' defensive check before every call
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class DocumentOcrService
' Path hardcoded — different on every deployment target
Private Const TessDataPath As String = "tessdata/"
Private Function ValidateTessdataBeforeUse(languageCode As String) As Boolean
' Guard required because missing files cause runtime exceptions
If Not Directory.Exists(TessDataPath) Then
Throw New InvalidOperationException("tessdata directory not found. Download from github.com/tesseract-ocr/tessdata_best")
End If
Dim filePath As String = Path.Combine(TessDataPath, $"{languageCode}.traineddata")
If Not File.Exists(filePath) Then
Throw New InvalidOperationException($"{languageCode}.traineddata not found — file must be downloaded manually")
End If
Return True
End Function
Public Function ExtractText(pdfPath As String, Optional languageCode As String = "eng") As String
ValidateTessdataBeforeUse(languageCode) ' defensive check before every call
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
End ClassIronOCR方法:
using IronOcr;
public class DocumentOcrService
{
//不是tessdata path — no validation logic — no defensive checks
public string ExtractText(string pdfPath)
{
return new IronTesseract().Read(pdfPath).Text;
}
}using IronOcr;
public class DocumentOcrService
{
//不是tessdata path — no validation logic — no defensive checks
public string ExtractText(string pdfPath)
{
return new IronTesseract().Read(pdfPath).Text;
}
}Imports IronOcr
Public Class DocumentOcrService
'不是tessdata path — no validation logic — no defensive checks
Public Function ExtractText(pdfPath As String) As String
Return New IronTesseract().Read(pdfPath).Text
End Function
End ClassImports IronOcr
Public Class DocumentOcrService
'不是tessdata path — no validation logic — no defensive checks
Public Function ExtractText(pdfPath As String) As String
Return New IronTesseract().Read(pdfPath).Text
End Function
End ClassTessDataPath常量整體刪除。 刪除複製tessdata文件夾的部署管道步驟。 刪除下載.traineddata文件的CI腳本。 刪除從Dockerfile中將tessdata複製進容器映像的層。 這些程式碼都不需要被替換——它們根本不再需要。 IronTesseract設置指南 涵蓋了所有如果配置需要超出預設值的初始化選項。
可搜尋的PDF生成管道
Syncfusion的可搜尋PDF輸出通過在載入的文件上調用PerformOCR()工作,這在位置上新增了一個隱藏的文字層,然後將修改過的文件保存到流中。 這種模式需要管理兩個流——輸入和輸出——並且OCR和保存步驟是對同一變質文件物件的分開操作。
Syncfusion OCR方法:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class SearchablePdfService
{
private const string TessDataPath = @"tessdata/";
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
// Load document — mutable: PerformOCR modifies it in place
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
// Step 1: OCR modifies the document object
processor.PerformOCR(document);
// Step 2: Save the modified document to a separate output file
using var outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write);
document.Save(outputStream);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
using var outputStream = new MemoryStream();
document.Save(outputStream);
return outputStream.ToArray();
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class SearchablePdfService
{
private const string TessDataPath = @"tessdata/";
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
// Load document — mutable: PerformOCR modifies it in place
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
// Step 1: OCR modifies the document object
processor.PerformOCR(document);
// Step 2: Save the modified document to a separate output file
using var outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write);
document.Save(outputStream);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var document = new PdfLoadedDocument(inputPdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
using var outputStream = new MemoryStream();
document.Save(outputStream);
return outputStream.ToArray();
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class SearchablePdfService
Private Const TessDataPath As String = "tessdata/"
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
' Load document — mutable: PerformOCR modifies it in place
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
' Step 1: OCR modifies the document object
processor.PerformOCR(document)
' Step 2: Save the modified document to a separate output file
Using outputStream = New FileStream(outputPdfPath, FileMode.Create, FileAccess.Write)
document.Save(outputStream)
End Using
End Using
End Using
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Using outputStream = New MemoryStream()
document.Save(outputStream)
Return outputStream.ToArray()
End Using
End Using
End Using
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class SearchablePdfService
Private Const TessDataPath As String = "tessdata/"
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
' Load document — mutable: PerformOCR modifies it in place
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
' Step 1: OCR modifies the document object
processor.PerformOCR(document)
' Step 2: Save the modified document to a separate output file
Using outputStream = New FileStream(outputPdfPath, FileMode.Create, FileAccess.Write)
document.Save(outputStream)
End Using
End Using
End Using
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using document = New PdfLoadedDocument(inputPdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Using outputStream = New MemoryStream()
document.Save(outputStream)
Return outputStream.ToArray()
End Using
End Using
End Using
End Function
End ClassIronOCR方法:
using IronOcr;
public class SearchablePdfService
{
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(inputPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
// SaveAsSearchablePdf also accepts a MemoryStream
using var ms = new MemoryStream();
result.SaveAsSearchablePdf(ms);
return ms.ToArray();
}
}using IronOcr;
public class SearchablePdfService
{
public void ConvertToSearchable(string inputPdfPath, string outputPdfPath)
{
var result = new IronTesseract().Read(inputPdfPath);
result.SaveAsSearchablePdf(outputPdfPath);
}
public byte[] ConvertToSearchableBytes(string inputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
// SaveAsSearchablePdf also accepts a MemoryStream
using var ms = new MemoryStream();
result.SaveAsSearchablePdf(ms);
return ms.ToArray();
}
}Imports IronOcr
Public Class SearchablePdfService
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
Dim result = New IronTesseract().Read(inputPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
' SaveAsSearchablePdf also accepts a MemoryStream
Using ms As New MemoryStream()
result.SaveAsSearchablePdf(ms)
Return ms.ToArray()
End Using
End Using
End Function
End ClassImports IronOcr
Public Class SearchablePdfService
Public Sub ConvertToSearchable(inputPdfPath As String, outputPdfPath As String)
Dim result = New IronTesseract().Read(inputPdfPath)
result.SaveAsSearchablePdf(outputPdfPath)
End Sub
Public Function ConvertToSearchableBytes(inputPdfPath As String) As Byte()
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
' SaveAsSearchablePdf also accepts a MemoryStream
Using ms As New MemoryStream()
result.SaveAsSearchablePdf(ms)
Return ms.ToArray()
End Using
End Using
End Function
End ClassSyncfusion使用的可變文件模型——PerformOCR()在保存前在地點修改載入的文件——由IronOCR的不可變讀取然後輸出模式取代。 OcrResult物件持有識別的文字,並能被保存為可搜尋的PDF,導出為純文字,或作為結構化資料遍歷,所有的都來自同樣的結果。 搜尋 PDF 如何指導以及搜尋 PDF 範例涵蓋了額外的輸出選項,包括PDF/A符合設定。
基於流的PDF OCR管道
接收PDF文件的生產服務通過HTTP上傳、消息隊列或blob儲存通常需要使用流而非文件路徑。 Syncfusion允許通過PdfLoadedDocument使用流,但tessdata路徑限制仍然適用——tessdata文件夾必須存在於處理流的伺服器上。
Syncfusion OCR方法:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StreamOcrService
{
private const string TessDataPath = @"tessdata/";
public string ExtractFromStream(Stream pdfStream)
{
// Stream input works, but tessdata path constraint remains
using var document = new PdfLoadedDocument(pdfStream);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
//不是native async — must wrap in Task.Run
return await Task.Run(() => ExtractFromStream(pdfStream));
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StreamOcrService
{
private const string TessDataPath = @"tessdata/";
public string ExtractFromStream(Stream pdfStream)
{
// Stream input works, but tessdata path constraint remains
using var document = new PdfLoadedDocument(pdfStream);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
return sb.ToString();
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
//不是native async — must wrap in Task.Run
return await Task.Run(() => ExtractFromStream(pdfStream));
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StreamOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractFromStream(pdfStream As Stream) As String
' Stream input works, but tessdata path constraint remains
Using document = New PdfLoadedDocument(pdfStream)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
'不是native async — must wrap in Task.Run
Return Await Task.Run(Function() ExtractFromStream(pdfStream))
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StreamOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractFromStream(pdfStream As Stream) As String
' Stream input works, but tessdata path constraint remains
Using document = New PdfLoadedDocument(pdfStream)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
Return sb.ToString()
End Using
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
'不是native async — must wrap in Task.Run
Return Await Task.Run(Function() ExtractFromStream(pdfStream))
End Function
End ClassIronOCR方法:
using IronOcr;
public class StreamOcrService
{
public string ExtractFromStream(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream); // accepts Stream directly
return new IronTesseract().Read(input).Text;
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream);
var ocr = new IronTesseract();
var result = await ocr.ReadAsync(input); // native async support
return result.Text;
}
}using IronOcr;
public class StreamOcrService
{
public string ExtractFromStream(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream); // accepts Stream directly
return new IronTesseract().Read(input).Text;
}
public async Task<string> ExtractFromStreamAsync(Stream pdfStream)
{
using var input = new OcrInput();
input.LoadPdf(pdfStream);
var ocr = new IronTesseract();
var result = await ocr.ReadAsync(input); // native async support
return result.Text;
}
}Imports IronOcr
Public Class StreamOcrService
Public Function ExtractFromStream(pdfStream As Stream) As String
Using input = New OcrInput()
input.LoadPdf(pdfStream) ' accepts Stream directly
Return New IronTesseract().Read(input).Text
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
Using input = New OcrInput()
input.LoadPdf(pdfStream)
Dim ocr = New IronTesseract()
Dim result = Await ocr.ReadAsync(input) ' native async support
Return result.Text
End Using
End Function
End ClassImports IronOcr
Public Class StreamOcrService
Public Function ExtractFromStream(pdfStream As Stream) As String
Using input = New OcrInput()
input.LoadPdf(pdfStream) ' accepts Stream directly
Return New IronTesseract().Read(input).Text
End Using
End Function
Public Async Function ExtractFromStreamAsync(pdfStream As Stream) As Task(Of String)
Using input = New OcrInput()
input.LoadPdf(pdfStream)
Dim ocr = New IronTesseract()
Dim result = Await ocr.ReadAsync(input) ' native async support
Return result.Text
End Using
End Function
End ClassStream,無需中間文件寫入。 IronOCR還提供Task.Run()包裝。 對於web API控制器、Azure Functions和其他異步服務模式來說,這是一個直接適合的API。 流 輸入指南記錄了所有流 載入選項,包括圖像流和多頁TIFF流。 異步 OCR 指南涵蓋取消令牌支持和進程回調以支持長時間運行的文件批次。
結構化段落和單詞提取
Syncfusion的文字提取模型提供了兩個層次:通過page.ExtractText()的每頁文字。 沒有子頁結構——沒有單詞坐標、沒有段落邊界,沒有每個令牌的信心分數。 需要按位置定位特定字段或過濾低置信度令牌的應用程式必須在合併字串的基礎上自己實施解析邏輯。
Syncfusion OCR方法:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StructuredExtractionService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<int, string> ExtractPerPage(string pdfPath)
{
var pageTexts = new Dictionary<int, string>();
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
// Page-level is the finest granularity available
int pageNum = 1;
foreach (PdfLoadedPage page in document.Pages)
{
pageTexts[pageNum] = page.ExtractText();
pageNum++;
}
return pageTexts;
//不是word coordinates, no paragraph boundaries, no per-token confidence
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class StructuredExtractionService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<int, string> ExtractPerPage(string pdfPath)
{
var pageTexts = new Dictionary<int, string>();
using var document = new PdfLoadedDocument(pdfPath);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
// Page-level is the finest granularity available
int pageNum = 1;
foreach (PdfLoadedPage page in document.Pages)
{
pageTexts[pageNum] = page.ExtractText();
pageNum++;
}
return pageTexts;
//不是word coordinates, no paragraph boundaries, no per-token confidence
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StructuredExtractionService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractPerPage(pdfPath As String) As Dictionary(Of Integer, String)
Dim pageTexts = New Dictionary(Of Integer, String)()
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
' Page-level is the finest granularity available
Dim pageNum As Integer = 1
For Each page As PdfLoadedPage In document.Pages
pageTexts(pageNum) = page.ExtractText()
pageNum += 1
Next
End Using
End Using
Return pageTexts
'不是word coordinates, no paragraph boundaries, no per-token confidence
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class StructuredExtractionService
Private Const TessDataPath As String = "tessdata/"
Public Function ExtractPerPage(pdfPath As String) As Dictionary(Of Integer, String)
Dim pageTexts = New Dictionary(Of Integer, String)()
Using document = New PdfLoadedDocument(pdfPath)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
' Page-level is the finest granularity available
Dim pageNum As Integer = 1
For Each page As PdfLoadedPage In document.Pages
pageTexts(pageNum) = page.ExtractText()
pageNum += 1
Next
End Using
End Using
Return pageTexts
'不是word coordinates, no paragraph boundaries, no per-token confidence
End Function
End ClassIronOCR方法:
using IronOcr;
public class StructuredExtractionService
{
public void ExtractWithStructure(string pdfPath)
{
var result = new IronTesseract().Read(pdfPath);
Console.WriteLine($"Overall confidence: {result.Confidence}%");
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
}
public IEnumerable<string> ExtractHighConfidenceWords(string pdfPath, int minConfidence = 80)
{
var result = new IronTesseract().Read(pdfPath);
// Per-word confidence filtering — not possible with Syncfusion's page-level model
return result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= minConfidence)
.Select(w => w.Text);
}
}using IronOcr;
public class StructuredExtractionService
{
public void ExtractWithStructure(string pdfPath)
{
var result = new IronTesseract().Read(pdfPath);
Console.WriteLine($"Overall confidence: {result.Confidence}%");
foreach (var page in result.Pages)
{
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):");
Console.WriteLine($" {paragraph.Text}");
}
}
}
public IEnumerable<string> ExtractHighConfidenceWords(string pdfPath, int minConfidence = 80)
{
var result = new IronTesseract().Read(pdfPath);
// Per-word confidence filtering — not possible with Syncfusion's page-level model
return result.Pages
.SelectMany(p => p.Words)
.Where(w => w.Confidence >= minConfidence)
.Select(w => w.Text);
}
}Imports IronOcr
Public Class StructuredExtractionService
Public Sub ExtractWithStructure(pdfPath As String)
Dim result = New IronTesseract().Read(pdfPath)
Console.WriteLine($"Overall confidence: {result.Confidence}%")
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words")
For Each paragraph In page.Paragraphs
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):")
Console.WriteLine($" {paragraph.Text}")
Next
Next
End Sub
Public Function ExtractHighConfidenceWords(pdfPath As String, Optional minConfidence As Integer = 80) As IEnumerable(Of String)
Dim result = New IronTesseract().Read(pdfPath)
' Per-word confidence filtering — not possible with Syncfusion's page-level model
Return result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= minConfidence) _
.Select(Function(w) w.Text)
End Function
End ClassImports IronOcr
Public Class StructuredExtractionService
Public Sub ExtractWithStructure(pdfPath As String)
Dim result = New IronTesseract().Read(pdfPath)
Console.WriteLine($"Overall confidence: {result.Confidence}%")
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words")
For Each paragraph In page.Paragraphs
Console.WriteLine($" Paragraph at ({paragraph.X}, {paragraph.Y}):")
Console.WriteLine($" {paragraph.Text}")
Next
Next
End Sub
Public Function ExtractHighConfidenceWords(pdfPath As String, Optional minConfidence As Integer = 80) As IEnumerable(Of String)
Dim result = New IronTesseract().Read(pdfPath)
' Per-word confidence filtering — not possible with Syncfusion's page-level model
Return result.Pages _
.SelectMany(Function(p) p.Words) _
.Where(Function(w) w.Confidence >= minConfidence) _
.Select(Function(w) w.Text)
End Function
End Class結構化輸出模型顯露段落、行、單詞及字元,帶有包圍盒座標及單獨置信度分數。 這對於發票字段提取、表單解析和文件分類特別有用——在知道文字說什麼的同時,知道文字出現於頁面上的哪處的工作流程。 讀取結果指南和OcrResult API 參考記錄了完整物件圖。
並行執行的批次文件處理
高產量OCR服務同時處理數十至數百份文件。 Syncfusion未將OCRProcessor記錄為執行緒安全,這導致了順序處理或要求開發者自行配置實例池。 IronOCR實例可安全地按執行緒建立,從而在Parallel.ForEach或PLINQ中直接使用,無需額外同步。
Syncfusion OCR方法:
using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class BatchOcrService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new Dictionary<string, string>();
// Sequential processing — OCRProcessor thread safety not guaranteed
foreach (var path in pdfPaths)
{
using var document = new PdfLoadedDocument(path);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
results[path] = sb.ToString();
}
return results;
}
}using Syncfusion.OCRProcessor;
using Syncfusion.Pdf.Parsing;
public class BatchOcrService
{
private const string TessDataPath = @"tessdata/";
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new Dictionary<string, string>();
// Sequential processing — OCRProcessor thread safety not guaranteed
foreach (var path in pdfPaths)
{
using var document = new PdfLoadedDocument(path);
using var processor = new OCRProcessor(TessDataPath);
processor.Settings.Language = Languages.English;
processor.PerformOCR(document);
var sb = new StringBuilder();
foreach (PdfLoadedPage page in document.Pages)
sb.AppendLine(page.ExtractText());
results[path] = sb.ToString();
}
return results;
}
}Imports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class BatchOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New Dictionary(Of String, String)()
' Sequential processing — OCRProcessor thread safety not guaranteed
For Each path In pdfPaths
Using document = New PdfLoadedDocument(path)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
results(path) = sb.ToString()
End Using
End Using
Next
Return results
End Function
End ClassImports Syncfusion.OCRProcessor
Imports Syncfusion.Pdf.Parsing
Public Class BatchOcrService
Private Const TessDataPath As String = "tessdata/"
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New Dictionary(Of String, String)()
' Sequential processing — OCRProcessor thread safety not guaranteed
For Each path In pdfPaths
Using document = New PdfLoadedDocument(path)
Using processor = New OCRProcessor(TessDataPath)
processor.Settings.Language = Languages.English
processor.PerformOCR(document)
Dim sb = New StringBuilder()
For Each page As PdfLoadedPage In document.Pages
sb.AppendLine(page.ExtractText())
Next
results(path) = sb.ToString()
End Using
End Using
Next
Return results
End Function
End ClassIronOCR方法:
using IronOcr;
public class BatchOcrService
{
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new ConcurrentDictionary<string, string>();
// Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, pdfPath =>
{
var ocr = new IronTesseract(); // one instance per thread
var text = ocr.Read(pdfPath).Text;
results[pdfPath] = text;
});
return new Dictionary<string, string>(results);
}
}using IronOcr;
public class BatchOcrService
{
public Dictionary<string, string> ProcessBatch(IEnumerable<string> pdfPaths)
{
var results = new ConcurrentDictionary<string, string>();
// Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, pdfPath =>
{
var ocr = new IronTesseract(); // one instance per thread
var text = ocr.Read(pdfPath).Text;
results[pdfPath] = text;
});
return new Dictionary<string, string>(results);
}
}Imports IronOcr
Imports System.Collections.Concurrent
Public Class BatchOcrService
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, Sub(pdfPath)
Dim ocr = New IronTesseract() ' one instance per thread
Dim text = ocr.Read(pdfPath).Text
results(pdfPath) = text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End ClassImports IronOcr
Imports System.Collections.Concurrent
Public Class BatchOcrService
Public Function ProcessBatch(pdfPaths As IEnumerable(Of String)) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' Parallel processing — IronTesseract is safe per-thread
Parallel.ForEach(pdfPaths, Sub(pdfPath)
Dim ocr = New IronTesseract() ' one instance per thread
Dim text = ocr.Read(pdfPath).Text
results(pdfPath) = text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Class每執行緒建立一個IronTesseract實例是並行處理的已記錄模式。 無共享狀態,無鎖爭用,無實例池基礎設施要求。 多執行緒範例展示了典型文件批次大小的吞吐量基準,以及速度優化指導說明涵蓋引擎配置選項以適應對延遲敏感的工作負載。
Syncfusion OCR API到IronOCR映射參考
| Syncfusion OCR | IronOCR 等效 | 注意事項 |
|---|---|---|
Syncfusion.PDF.OCR.Net.Core | IronOcr | 替換NuGet包 |
Syncfusion.OCRProcessor | IronOcr | 單一命名空間 |
Syncfusion.Pdf | 移除 | 不再需要 |
Syncfusion.Pdf.Parsing | 移除 | 不再需要 |
SyncfusionLicenseProvider.RegisterLicense() | IronOcr.License.LicenseKey = | 字串分配,無套件註冊 |
new OCRProcessor(tessdataPath) | new IronTesseract() | 沒有路徑參數 |
PdfLoadedDocument(filePath) | 把路徑直接傳到ocr.Read(path) | 或使用LoadPdf()連用 |
PdfLoadedDocument(stream) | input.LoadPdf(stream) | 流支持直接提供 |
processor.Settings.Language = Languages.English | ocr.Language = OcrLanguage.English | OcrLanguage 枚舉 |
Languages.English |Languages.French | ocr.Language = OcrLanguage.English; ocr.AddSecondaryLanguage(OcrLanguage.French) | 新增入樣模式取代位運算標幟 |
processor.PerformOCR(document) | ocr.Read(input) | 直接返回OcrResult |
page.ExtractText() | result.Pages[i].Text | 整篇無需迴路 |
document.Pages 迭代 | result.Pages[] 阵列 | 包括段落、單詞、字元 |
OCR之後的document.Save(outputStream) | result.SaveAsSearchablePdf(path) | 專用方法 |
| Tessdata驗證邏輯 | 完全移除 | 無需對tessdata進行驗證 |
| 手動tessdata路徑常量 | 完全移除 | IronOCR不要求 |
PdfBitmap 圖像到PDF轉換 | input.LoadImage(imagePath) | 圖像OCR無PDF往返 |
| 沒有預處理 API | input.Deskew(), input.DeNoise(), input.Contrast() | 內嵌於OcrInput |
常見的遷移問題与解決方案
問題 1:更換包後找不到Tessdata目錄
Syncfusion OCR: tessdata目錄驗證檢查在啟動或每次呼叫時作為一種防護編寫。 刪除Syncfusion並安裝IronOCR後,該驗證程式碼仍可編譯(使用System.IO,而不是Syncfusion命名空間),但現在防護的是不存在的操作。 把它放在原位是死程式碼,可能會困惑未來的開發者。
解決方案:完全刪除所有tessdata驗證邏輯。 移除File.Exists(Path.Combine(TessDataPath, ...))檢查,以及任何啟動驗證方法。 IronOCR不會引發tessdata相關異常,因為沒有tessdata會丟失:
// Delete these entirely — they have no equivalent in IronOCR
// private const string TessDataPath = @"tessdata/";
// private bool ValidateTessdata() { ... }
// The only error handling needed after migration:
try
{
return new IronTesseract().Read(pdfPath).Text;
}
catch (FileNotFoundException)
{
throw new ArgumentException($"PDF file not found: {pdfPath}");
}// Delete these entirely — they have no equivalent in IronOCR
// private const string TessDataPath = @"tessdata/";
// private bool ValidateTessdata() { ... }
// The only error handling needed after migration:
try
{
return new IronTesseract().Read(pdfPath).Text;
}
catch (FileNotFoundException)
{
throw new ArgumentException($"PDF file not found: {pdfPath}");
}Try
Return New IronTesseract().Read(pdfPath).Text
Catch ex As FileNotFoundException
Throw New ArgumentException($"PDF file not found: {pdfPath}")
End TryTry
Return New IronTesseract().Read(pdfPath).Text
Catch ex As FileNotFoundException
Throw New ArgumentException($"PDF file not found: {pdfPath}")
End Try問題2:運行時找不到語言文件
Syncfusion OCR:語言.csproj,並由構建系統複製。 在項目中刪除tessdata文件夾後,語言相關的CI步驟和.csproj條目可能仍然引用已刪除的文件,導致生成警告或管道失敗。
解決方案: 從.csproj文件和CI管道定義中移除所有tessdata相關的條目。 改用NuGet方式安裝語言包:
# Languages install as NuGet packages — no manual file management
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
// Language configuration after migration
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
ocr.AddSecondaryLanguage(OcrLanguage.German);
var result = ocr.Read("multilingual-report.pdf");// Language configuration after migration
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
ocr.AddSecondaryLanguage(OcrLanguage.German);
var result = ocr.Read("multilingual-report.pdf");' Language configuration after migration
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
ocr.AddSecondaryLanguage(OcrLanguage.German)
Dim result = ocr.Read("multilingual-report.pdf")' Language configuration after migration
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
ocr.AddSecondaryLanguage(OcrLanguage.German)
Dim result = ocr.Read("multilingual-report.pdf")多種語言指南涵蓋了語言包安裝和所有支持的125+語言的OcrLanguage枚舉值。
問題三:可搜尋PDF輸出位元組次序不同
Syncfusion OCR:可搜尋的PDF通過功調用PerformOCR()改變文件之後。 某些下游消費者可能已經寫好以期待Syncfusion的特定PDF結構、元資料字段或生產者字串。
解決方案: IronOCR的SaveAsSearchablePdf()生成標準PDF且含有文字層。 用您的下游消費者(PDF查看器、搜索索引、歸檔系統)測試輸出以驗證相容性。 如果需要逐字節相同的輸出,過渡測試比較文字可提取性(而不是原始字節)是合適的接受標準:
// Verify the searchable PDF contains the expected text
var result = new IronTesseract().Read("scanned.pdf");
result.SaveAsSearchablePdf("output-searchable.pdf");
// Validation: confirm text layer is present and readable
var verificationText = new IronTesseract().Read("output-searchable.pdf").Text;
Assert.True(verificationText.Contains("expected content"));// Verify the searchable PDF contains the expected text
var result = new IronTesseract().Read("scanned.pdf");
result.SaveAsSearchablePdf("output-searchable.pdf");
// Validation: confirm text layer is present and readable
var verificationText = new IronTesseract().Read("output-searchable.pdf").Text;
Assert.True(verificationText.Contains("expected content"));' Verify the searchable PDF contains the expected text
Dim result = New IronTesseract().Read("scanned.pdf")
result.SaveAsSearchablePdf("output-searchable.pdf")
' Validation: confirm text layer is present and readable
Dim verificationText = New IronTesseract().Read("output-searchable.pdf").Text
Assert.True(verificationText.Contains("expected content"))' Verify the searchable PDF contains the expected text
Dim result = New IronTesseract().Read("scanned.pdf")
result.SaveAsSearchablePdf("output-searchable.pdf")
' Validation: confirm text layer is present and readable
Dim verificationText = New IronTesseract().Read("output-searchable.pdf").Text
Assert.True(verificationText.Contains("expected content"))問題四:遷移嘗試後Docker映像尺寸增加
Syncfusion OCR:某些團隊在測試期間嘗試遷移同時保留了Docker映像中的tessdata文件作為預防措施。 這導致映像中同時存在tessdata層和IronOCR包,無必要地增加了映像尺寸。
解決方案:在生成遷移後的映像之前,從Dockerfile中刪除tessdataCOPY層。 IronOCR包是自包含的。 Docker部署指南提供通過驗證的基礎映像及針對Alpine、Debian、Ubuntu目標的配置:
# 移除this layer entirely after migration
# COPY tessdata/ /app/tessdata/
#IronOCRrequires only the standard .NET runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "YourService.dll"]
問題五:兩步執行OCR / 提取文字模式無直接對應項
Syncfusion OCR:某些呼叫程式碼在方法間傳遞ExtractText() - 依賴於文件物件的有狀態改變。 該模式不存於IronOCR中,因為Read()返回一個自包含的結果物件。
解決方案:重構任何分裂的OCR/提取模式至接受文件路徑或流並返回OcrResult的單一方法。 結果物件攜帶了一切——文字、頁面、段落、信心,以及保存為可搜尋PDF的能力:
// Replace split PerformOCR / ExtractText pattern
public OcrResult ProcessDocument(string pdfPath)
{
// One call, immutable result, all data available
return new IronTesseract().Read(pdfPath);
}
// Callers decide what they need from the result
var result = service.ProcessDocument("contract.pdf");
var fullText = result.Text;
var confidence = result.Confidence;
result.SaveAsSearchablePdf("contract-searchable.pdf");// Replace split PerformOCR / ExtractText pattern
public OcrResult ProcessDocument(string pdfPath)
{
// One call, immutable result, all data available
return new IronTesseract().Read(pdfPath);
}
// Callers decide what they need from the result
var result = service.ProcessDocument("contract.pdf");
var fullText = result.Text;
var confidence = result.Confidence;
result.SaveAsSearchablePdf("contract-searchable.pdf");Public Function ProcessDocument(pdfPath As String) As OcrResult
' One call, immutable result, all data available
Return New IronTesseract().Read(pdfPath)
End Function
' Callers decide what they need from the result
Dim result = service.ProcessDocument("contract.pdf")
Dim fullText = result.Text
Dim confidence = result.Confidence
result.SaveAsSearchablePdf("contract-searchable.pdf")Public Function ProcessDocument(pdfPath As String) As OcrResult
' One call, immutable result, all data available
Return New IronTesseract().Read(pdfPath)
End Function
' Callers decide what they need from the result
Dim result = service.ProcessDocument("contract.pdf")
Dim fullText = result.Text
Dim confidence = result.Confidence
result.SaveAsSearchablePdf("contract-searchable.pdf")問題六:遷移後社區授權註冊程式碼仍然存在
Syncfusion OCR: Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense()呼叫於應用啟動時註冊套件授權。 該呼叫通常於Startup.cs或靜態初始化器中。 移除Syncfusion包後,該行程式碼會導致編譯錯誤。
解決方案:刪除SyncfusionLicenseProvider.RegisterLicense()呼叫並用IronOCR授權初始化取代。 同時移除任何社區授權資格邏輯、合規文件引用或收入及員工閾值相關評論 - 這些概念皆不適用於IronOCR:
// 移除(causes compile error after package removal)
// Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
// Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY";// 移除(causes compile error after package removal)
// Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY");
// Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY";' 移除(causes compile error after package removal)
' Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY")
' Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY"' 移除(causes compile error after package removal)
' Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("SYNCFUSION-KEY")
' Add at application startup
IronOcr.License.LicenseKey = "YOUR-IRONOCR-KEY"Syncfusion OCR遷移檢查清單
遷移前
在進行更改前對程式碼庫進行審核以識別所有Syncfusion OCR用法:
# Find all Syncfusion namespace imports
grep -r "using Syncfusion" --include="*.cs" .
# Find OCRProcessor usage
grep -r "OCRProcessor\|PerformOCR\|PdfLoadedDocument\|ExtractText" --include="*.cs" .
# Find tessdata path references
grep -r "TessDataPath\|tessdata\|traineddata" --include="*.cs" .
# Find Syncfusion license registration
grep -r "SyncfusionLicenseProvider\|RegisterLicense" --include="*.cs" .
# Find csproj tessdata copy rules
grep -r "tessdata\|traineddata" --include="*.csproj" .
# Find Dockerfile tessdata layers
grep -r "tessdata" Dockerfile* docker-compose*.yml .
在編寫任何程式碼前清點結果。 註明哪些文件包含OCR呼叫,哪些文件包含tessdata驗證,以及哪些管道定義引用了tessdata文件夾。
程式碼遷移
- 從所有
Syncfusion.Pdf.Net.Core及相關包。 - 在每個執行OCR的專案中運行
dotnet add package IronOcr。 - 通過NuGet安裝任何非英語語言的語言包:
dotnet add package IronOcr.Languages.[Language]。 - 從所有服務類中刪除
private const string TessDataPath常量。 - 刪除所有的tessdata驗證方法(
ValidateTessdata()及類似防範)。 - 將
IronOcr.License.LicenseKey = "YOUR-KEY"於應用啟動時。 - Replace
using Syncfusion.OCRProcessor; using Syncfusion.Pdf; using Syncfusion.Pdf.Parsing;withusing IronOcr;. - 將每個
new IronTesseract()。 - 將
ocr.Read(path).Text。 - 將Syncfusion的位元語言旗標(
Languages.English|Languages.Frenchplusocr.AddSecondaryLanguage()呼叫替換。 - 將
result.SaveAsSearchablePdf(path)以取得可搜尋的PDF輸出。 - 用直接的
ocr.Read(imagePath)替換圖像到PDF轉換往返。 - 從所有
.csproj文件中移除tessdataCopyToOutputDirectory條目。 - 從所有CI/CD管道定義中移除tessdata下載步驟。
- 將整個tessdata
COPY層從所有Dockerfiles中移除。
遷移後
- 驗證PDF OCR在遷移前使用的相同樣本文件上生產期望的文字內容。
- 驗證圖像OCR(JPG, PNG, BMP)無需任何PDF轉換步驟。
- 確認多語言文件能藉由已安裝NuGet語言包正確識別。
- 通過在PDF檢視器中開啟生成的文件並確認文字選擇和搜尋功能正常來測試可搜尋的PDF輸出。
- 在從更新過的Dockerfile構建的新鮮Docker容器中運行應用以確認無tessdata相關啟動錯誤發生。
- 確認應用無需
Syncfusion.Licensing呼叫或Syncfusion命名空間參考即可啟動。 - 驗證
result.Confidence返回合理值(對於清晰文件通常為80-99%)以確認OCR引擎正常工作。 - 通過運行並行的OCR呼叫並驗證無執行緒異常或結果損壞來測試並行批量處理。
- 比較遷移前後對低質量或旋轉掃描的文字提取準確性,記錄自動預處理管道帶來的改善。
遷移至IronOCR的主要好處
部署複雜性減少到單個NuGet包。遷移後,每個環境——開發者工作站、CI運行器、預部署容器、生成伺服器——只需要一件東西:開發系統恢復的IronOcr NuGet包。 沒有tessdata文件夾。 無需配置文件系統路徑。 無需語言文件下載腳本。無需帶有100–500 MB二進制資料的Dockerfile層。 容器映像變小,CI管道簡化,新環境在初次構建時正確配置,無需人工干預。
授權成本變得可預測且不會重複出現。一次性永久授權購買替代了按開發者每年續約的周期。 一支五人開發團隊購買IronOCR Professional(2999美元)並擁有程式庫無期限,連同一年更新。 無需監控收入閾值,無需追蹤員工數量限制,無需審計條款,也無需維護合規文件。 增長事件——新承包商、大合同、融資回合——不會觸發授權審查。
OCR管道處理劣化的文件而無需外部依賴。 矯正、去噪、對比度增強、二值化和解析度縮放作為OcrInput法可用。 無需單獨的影像程式庫。 輕微旋轉、有掃描器雜訊或低對比度的文件以往需要用System.Drawing或SkiaSharp中預處理階段處理,如今都可以在同一個IronOCR呼叫中處理。 圖像質量校正指南及預處理功能頁記錄了所有可用的濾鏡及其對識別準確性的影響。
結構化輸出支持字段級文件智能。OcrResult物件揭露文件結構——頁面、段落、行、單詞、字元——擁有包圍盒坐標和每個令牌的信心分數。 之前通過合併文字字串來尋找字段邊界的應用程式現在可以直接使用段落和單詞坐標資料。 發票處理、表單提取和文件分類工作流獲得對空間資訊的存取,而Syncfusion的頁面級模型無法提供這些。PDF OCR 使用案例頁面註明了常見的文件智能模式。
並行批量處理無需基礎設施即能擴展。 每個執行緒建立一個IronTesseract實例是完整的執行緒戰略——無需實例池,無學習率控制,無序列化處理限制。 一個每小時處理500份文件的批服務,可以飽和可用CPU核心,搭配Parallel.ForEach和一條同步執行緒即可運行。 自包含的引擎架構意味著每個執行緒獨立運行,沒有共享可變狀態。
超過125種語言可用且無二進制文件管理。每種語言包以標準包管理器通過NuGet包安裝。 版本管理、更新獲取和依賴解析由同一工具管理,每個其他項目依賴。 新增日語或阿拉伯語OCR到服務只需一條dotnet add package命令,而不是從GitHub庫手動下載,隨即進行部署管道更新。 語言索引列出所有支持的編碼及安裝命令。
相關文章


