GdPicture.NET OCR vs IronOCR: .NET OCR Library
GdPicture.NET requires a core license before you pay a single dollar for the OCR plugin — and if you want to read PDFs or produce searchable output, you need additional PDF plugins too. Contact GdPicture for current pricing. For a team that needs to extract text from images and PDFs, the combined cost of multiple plugins plus annual maintenance fees can add up significantly. The billing is a direct consequence of GdPicture's architecture: it is a full document imaging platform covering scanning, DICOM, annotations, barcode processing, and PDF editing, and OCR is one plugin among many. Teams with OCR-only requirements absorb the cost of an entire document imaging SDK they will never fully use.
The API reflects the same philosophy. GdPicture uses integer image IDs to track resources in memory — every image you load returns an int, you call methods against that integer handle, and then you must explicitly release it or accept a memory leak. This pattern predates IDisposable and using statements. It works, but it puts the burden of every cleanup decision on the developer, every single time. This comparison examines those two tensions — plugin-based pricing and the image ID lifecycle — alongside API complexity, preprocessing, and PDF handling, with IronOCR as the counterpoint.
Understanding GdPicture.NET
GdPicture.NET is a commercial document imaging SDK developed by ORPALIS. It has been built over many years into a wide-reaching platform covering image processing, PDF creation and editing, barcode reading and generation, TWAIN/WIA scanner integration, DICOM medical imaging, annotation and review workflows, and document archiving with PDF/A compliance. OCR is one capability within this ecosystem, delivered through a dedicated plugin.
The SDK organizes its functionality into distinct modules, each with its own primary class:
- GdPictureImaging — image loading, processing, format conversion, and the source of all integer image IDs
- GdPicturePDF — PDF creation, editing, rendering, and page-level OCR operations (separate plugin license required)
- GdPictureOCR — text recognition and extraction; consumes image IDs produced by GdPictureImaging
- GdPicture1DBarcode / GdPicture2DBarcode — linear and 2D barcode reading and writing (separate plugin licenses)
- GdPictureDocumentImaging — advanced document cleanup filters including deskew, despeckle, border removal, and hole punch removal (separate plugin)
- GdPictureAnnotations — document markup, review, and redaction
Even basic OCR requires instantiating two components — GdPictureImaging for loading the image and GdPictureOCR for recognizing text — and managing both their lifecycles. OCR from a PDF requires a third: GdPicturePDF. The namespace itself includes a version number (GdPicture14), which means every major SDK upgrade forces a find-and-replace across the entire codebase.
The plugin dependency tree for common OCR scenarios looks like this:
| If you need | You must license |
|---|---|
| OCR from images | Base license + OCR Plugin |
| OCR from PDFs | Base + OCR Plugin + PDF Plugin |
| Searchable PDF output | Base + OCR Plugin + PDF Plugin |
| Document cleanup before OCR | Base + OCR Plugin + Document Imaging Plugin |
The Image ID Lifecycle
The defining architectural characteristic of GdPicture is integer image ID management. Loading any image allocates memory inside the GdPicture runtime and returns an int handle. You use that integer to reference the image in all subsequent calls. When you are done, you call ReleaseGdPictureImage with that integer. If you do not, the memory is not released until the process ends.
using GdPicture14;
// Every image load returns an integer handle
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
throw new Exception($"Failed to load image: {_imaging.GetStat()}");
}
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
{
throw new Exception($"OCR failed: {_ocr.GetStat()}");
}
return _ocr.GetOCRResultText(resultId);
}
finally
{
// CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId);
}
using GdPicture14;
// Every image load returns an integer handle
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
throw new Exception($"Failed to load image: {_imaging.GetStat()}");
}
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
{
throw new Exception($"OCR failed: {_ocr.GetStat()}");
}
return _ocr.GetOCRResultText(resultId);
}
finally
{
// CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId);
}
Imports GdPicture14
' Every image load returns an integer handle
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
Throw New Exception($"Failed to load image: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
Return _ocr.GetOCRResultText(resultId)
Finally
' CRITICAL: omitting this line causes a memory leak
_imaging.ReleaseGdPictureImage(imageId)
End Try
When processing multi-page PDFs, each rendered page produces a separate image ID. A 100-page document at 200 DPI can allocate 1–5 GB of memory if cleanup is missed. The correct pattern requires collecting all IDs and releasing them in a finally block — but that discipline is entirely the developer's responsibility.
Understanding IronOCR
IronOCR is a commercial OCR library for .NET built as a single NuGet package. Its design goal is accuracy without configuration overhead: install the package, set a license key, call Read. The library wraps an optimized Tesseract 5 engine with automatic preprocessing, native PDF handling, and structured result access, all exposed through standard .NET patterns.
Key characteristics:
- Single NuGet package —
dotnet add package IronOcrinstalls everything including English language support; no external resource folders, no native binary management - Standard .NET resource management —
OcrInputimplementsIDisposable; ausingstatement handles all cleanup regardless of exceptions - Automatic preprocessing — Deskew, DeNoise, Contrast, Binarize, and EnhanceResolution are one-line method calls on
OcrInput; the engine also applies automatic corrections without explicit instruction - Native PDF input — scanned PDFs and native PDFs both feed directly into
IronTesseract.Readwithout a separate rendering step - Thread-safe by design — a single
IronTesseractinstance handles concurrent requests;Parallel.ForEachworks without additional synchronization - 125+ languages — distributed as separate NuGet packages (
IronOcr.Languages.French, etc.), no tessdata folder to manage - Structured result access —
OcrResultexposes.Pages,.Lines,.Words,.Characters, and.Barcodesas typed collections with coordinate data
Pricing is perpetual: $999 Lite, $1,499 Plus, $2,999 Professional, $5,999 Unlimited. All features are available at every tier; the difference is the number of developers and projects covered.
Feature Comparison
| Feature | GdPicture.NET | IronOCR |
|---|---|---|
| Pricing model | Plugin-based perpetual + 20%/yr maintenance | Flat perpetual, optional annual updates |
| Entry cost (OCR from PDFs) | Multiple plugins required (contact GdPicture for pricing) | $999–$2,999 (one package, all features) |
| Resource management | Manual integer ID tracking and release | IDisposable / using statement |
| Namespace stability | Version number in namespace (GdPicture14) |
Version-agnostic (IronOcr) |
| PDF OCR | Requires separate PDF plugin | Built-in, no additional license |
| Installation | Multiple NuGet packages + external resource folder | Single NuGet, no external files |
| Cross-platform | Windows, Linux, macOS | Windows, Linux, macOS, Docker, AWS, Azure |
Detailed Feature Comparison
| Feature | GdPicture.NET | IronOCR |
|---|---|---|
| Setup and Installation | ||
| NuGet installation | Multiple packages required | dotnet add package IronOcr |
| External resource folder | Required (tessdata-style language files) | Not required (bundled) |
| License activation | LicenseManager.RegisterKEY() |
IronOcr.License.LicenseKey = "..." |
| Component initialization | 3–4 classes for PDF OCR workflow | new IronTesseract() |
| Namespace on major upgrade | Must update every source file | No change required |
| OCR Capabilities | ||
| Image OCR | Yes | Yes |
| PDF OCR | Yes (PDF plugin required) | Yes (built-in) |
| Password-protected PDF | Yes | Yes |
| Multi-page PDF | Manual page loop + ID cleanup | Automatic |
| Searchable PDF output | OcrPage + SaveToFile |
result.SaveAsSearchablePdf() |
| hOCR export | No | Yes |
| Preprocessing | ||
| Deskew | Via GdPictureDocumentImaging plugin | input.Deskew() |
| Noise removal | Via GdPictureDocumentImaging plugin | input.DeNoise() |
| Contrast enhancement | Via GdPictureImaging | input.Contrast() |
| Binarization | Via GdPictureImaging | input.Binarize() |
| DPI enhancement | Via GdPictureImaging | input.EnhanceResolution(300) |
| Automatic correction (no code) | No | Yes (built-in to Read) |
| Resource Management | ||
| Memory management model | Manual integer ID tracking | IDisposable / GC |
| Memory leak risk | High (missing ReleaseGdPictureImage) | None (using statement) |
| Thread safety | Manual instance management | Thread-safe by design |
| Results and Data | ||
| Plain text | GetOCRResultText(resultId) |
result.Text |
| Confidence score | GetOCRResultConfidence(resultId) |
result.Confidence |
| Word-level data | Nested block/line/word iteration | result.Words collection |
| Coordinate access | Via block/line/word index calls | Direct .X, .Y, .Width, .Height |
| Barcode reading during OCR | Via separate barcode plugin | ocr.Configuration.ReadBarCodes = true |
| Languages | ||
| Language count | Tesseract-based, similar coverage | 125+ via NuGet packages |
| Language distribution | Resource folder files | NuGet packages |
| Multi-language in one pass | Yes | Yes (AddSecondaryLanguage) |
| Deployment | ||
| Docker | Supported | Supported (documented) |
| Linux | Supported | Supported |
| AWS Lambda | Possible | Documented guide |
| Azure App Service | Possible | Documented guide |
Image ID Lifecycle vs. Disposable OcrInput
The most consequential architectural difference between GdPicture and IronOCR is not a feature gap — it is how each library handles memory for the images it processes.
GdPicture Approach
Every image in GdPicture exists as an integer handle in a runtime-managed pool. You acquire the handle by loading an image, pass that integer to OCR and other operations, then release it when done. The pattern is explicit and correct when followed. The problem is that it is easy to get wrong, and the consequence is unbounded memory growth.
The PDF OCR workflow demonstrates the risk most clearly. Each call to pdf.RenderPageToGdPictureImage() allocates a new raster image in memory and returns a new integer ID. A 100-page document creates 100 separate allocations. Miss one ReleaseGdPictureImage call and the process accumulates leaked memory:
using GdPicture14;
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(pdfPath, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Failed to load PDF: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// Each page render = new memory allocation + new integer ID
int imageId = pdf.RenderPageToGdPictureImage(200, false);
if (imageId == 0) continue;
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
text.AppendLine(_ocr.GetOCRResultText(resultId));
}
finally
{
// Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId);
}
}
return text.ToString();
}
using GdPicture14;
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(pdfPath, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Failed to load PDF: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// Each page render = new memory allocation + new integer ID
int imageId = pdf.RenderPageToGdPictureImage(200, false);
if (imageId == 0) continue;
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
text.AppendLine(_ocr.GetOCRResultText(resultId));
}
finally
{
// Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId);
}
}
return text.ToString();
}
Imports GdPicture14
Imports System.Text
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Dim text As New StringBuilder()
Using pdf As New GdPicturePDF()
Dim status As GdPictureStatus = pdf.LoadFromFile(pdfPath, False)
If status <> GdPictureStatus.OK Then
Throw New Exception($"Failed to load PDF: {status}")
End If
Dim pageCount As Integer = pdf.GetPageCount()
For i As Integer = 1 To pageCount
pdf.SelectPage(i)
' Each page render = new memory allocation + new integer ID
Dim imageId As Integer = pdf.RenderPageToGdPictureImage(200, False)
If imageId = 0 Then Continue For
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If Not String.IsNullOrEmpty(resultId) Then
text.AppendLine(_ocr.GetOCRResultText(resultId))
End If
Finally
' Without this: ~10–50MB leaked per page
_imaging.ReleaseGdPictureImage(imageId)
End Try
Next
End Using
Return text.ToString()
End Function
Batch processing across multiple PDFs compounds this. The documentation for each for loop must carry a note reminding maintainers to collect IDs in a List<int> and release them all in finally. That is infrastructure work, not OCR work.
IronOCR Approach
OcrInput is a standard IDisposable. A using statement guarantees cleanup whether the method returns normally or throws. There are no integer IDs to track, no separate imaging component to initialize, and no external DPI rendering step for PDF pages.
using IronOcr;
public string ExtractTextFromPdf(string pdfPath)
{
// LoadPdf handles page rendering internally
using var input = new OcrInput();
input.LoadPdf(pdfPath);
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text}");
return result.Text;
// using block exits — all internal resources released automatically
}
using IronOcr;
public string ExtractTextFromPdf(string pdfPath)
{
// LoadPdf handles page rendering internally
using var input = new OcrInput();
input.LoadPdf(pdfPath);
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text}");
return result.Text;
// using block exits — all internal resources released automatically
}
Imports IronOcr
Public Function ExtractTextFromPdf(pdfPath As String) As String
' LoadPdf handles page rendering internally
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Dim result = New IronTesseract().Read(input)
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Text}")
Next
Return result.Text
' using block exits — all internal resources released automatically
End Using
End Function
For password-protected PDFs, the parameter is a named argument rather than a separate overload that requires additional status checking:
using var input = new OcrInput();
input.LoadPdf("encrypted.pdf", Password: "secret");
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf("searchable-output.pdf");
using var input = new OcrInput();
input.LoadPdf("encrypted.pdf", Password: "secret");
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf("searchable-output.pdf");
Imports IronOcr
Using input As New OcrInput()
input.LoadPdf("encrypted.pdf", Password:="secret")
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf("searchable-output.pdf")
End Using
The IronOCR PDF input guide covers additional options including page-range selection. For teams already familiar with IDisposable patterns across Entity Framework, HttpClient, and stream handling, OcrInput requires no new mental model.
Plugin Bundling Cost
For a server application that extracts text from images and PDFs and produces searchable output, the GdPicture license stack requires multiple plugins. Contact GdPicture for current pricing on the Professional core license, OCR plugin, and PDF plugin. Annual maintenance fees (typically a percentage of the total license cost) add up over time.
IronOCR Professional covers the same workflow in a single package:
IronOCR Professional: $professionalLicense (one-time)
Optional annual updates: $plusLicense/year
3-year total (perpetual, no updates): $professionalLicense
The multi-year cost delta can be significant depending on update preferences. That gap exists because GdPicture's plugin model is priced for a full document imaging platform, not for OCR-focused workloads. The OCR plugin is required before the PDF plugin that makes it usable for the most common OCR input format.
Teams evaluating GdPicture for OCR pay for capabilities that rarely appear in OCR-focused backlogs: TWAIN/WIA scanner integration, DICOM medical imaging, annotation and redaction workflows, and advanced PDF editing beyond text extraction. See the IronOCR licensing page for a pricing model where all features are available at every tier — no plugins, no addons, no per-feature licensing decisions.
API Complexity and Preprocessing
GdPicture exposes preprocessing through GdPictureImaging (contrast, binarization, resolution scaling) and through a separate GdPictureDocumentImaging plugin (deskew, despeckle, border removal). Applying deskew before OCR requires the Document Imaging plugin license, creating a third cost in the stack for any workflow that processes scanned documents of variable quality.
GdPicture Approach
The full workflow for a low-quality scanned PDF — load, preprocess, OCR each page, release — spans three component classes and requires careful status checking at each step:
using GdPicture14;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(inputPdf, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Load failed: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// OcrPage adds text layer to each page
// Requires: OCR plugin + PDF plugin + resource folder path
GdPictureStatus ocrStatus = pdf.OcrPage(
"eng",
@"C:\GdPicture\Resources\OCR", // External resource folder required
"",
200
);
if (ocrStatus != GdPictureStatus.OK)
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}");
}
pdf.SaveToFile(outputPdf, true);
}
using GdPicture14;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
using var pdf = new GdPicturePDF();
GdPictureStatus status = pdf.LoadFromFile(inputPdf, false);
if (status != GdPictureStatus.OK)
throw new Exception($"Load failed: {status}");
int pageCount = pdf.GetPageCount();
for (int i = 1; i <= pageCount; i++)
{
pdf.SelectPage(i);
// OcrPage adds text layer to each page
// Requires: OCR plugin + PDF plugin + resource folder path
GdPictureStatus ocrStatus = pdf.OcrPage(
"eng",
@"C:\GdPicture\Resources\OCR", // External resource folder required
"",
200
);
if (ocrStatus != GdPictureStatus.OK)
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}");
}
pdf.SaveToFile(outputPdf, true);
}
Imports GdPicture14
Public Sub CreateSearchablePdf(inputPdf As String, outputPdf As String)
Using pdf As New GdPicturePDF()
Dim status As GdPictureStatus = pdf.LoadFromFile(inputPdf, False)
If status <> GdPictureStatus.OK Then
Throw New Exception($"Load failed: {status}")
End If
Dim pageCount As Integer = pdf.GetPageCount()
For i As Integer = 1 To pageCount
pdf.SelectPage(i)
' OcrPage adds text layer to each page
' Requires: OCR plugin + PDF plugin + resource folder path
Dim ocrStatus As GdPictureStatus = pdf.OcrPage(
"eng",
"C:\GdPicture\Resources\OCR", ' External resource folder required
"",
200
)
If ocrStatus <> GdPictureStatus.OK Then
Console.WriteLine($"Warning: Page {i} OCR failed: {ocrStatus}")
End If
Next
pdf.SaveToFile(outputPdf, True)
End Using
End Sub
The ResourceFolder path is not optional. It must resolve to a directory containing .traineddata language files at runtime. That path works on the development machine, then fails in production when the service account cannot see C:\GdPicture\Resources\OCR, or when the application is deployed to Linux, or when Docker images do not include that directory structure. Debugging these failures often means chasing generic GdPictureStatus error codes that say little about root cause.
IronOCR Approach
The equivalent workflow in IronOCR — including preprocessing for deskew and noise removal — is five lines:
using IronOcr;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(inputPdf);
input.Deskew(); // No separate plugin required
input.DeNoise();
var result = ocr.Read(input);
result.SaveAsSearchablePdf(outputPdf);
}
using IronOcr;
public void CreateSearchablePdf(string inputPdf, string outputPdf)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(inputPdf);
input.Deskew(); // No separate plugin required
input.DeNoise();
var result = ocr.Read(input);
result.SaveAsSearchablePdf(outputPdf);
}
Imports IronOcr
Public Sub CreateSearchablePdf(inputPdf As String, outputPdf As String)
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(inputPdf)
input.Deskew() ' No separate plugin required
input.DeNoise()
Dim result = ocr.Read(input)
result.SaveAsSearchablePdf(outputPdf)
End Using
End Sub
English language support is embedded in the NuGet package. Additional languages install as NuGet packages (IronOcr.Languages.French) rather than .traineddata files in a filesystem path. The image quality correction guide and image orientation correction guide cover the full set of preprocessing options available on OcrInput.
For confidence-aware processing — filtering results below a threshold before writing to a database, for instance — the result object exposes confidence directly:
var result = new IronTesseract().Read("scanned.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Per-word confidence for fine-grained filtering
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85)
.Select(w => w.Text);
var result = new IronTesseract().Read("scanned.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Per-word confidence for fine-grained filtering
var highConfidenceWords = result.Words
.Where(w => w.Confidence > 85)
.Select(w => w.Text);
Imports System
Imports System.Linq
Dim result = (New IronTesseract()).Read("scanned.jpg")
Console.WriteLine($"Confidence: {result.Confidence}%")
' Per-word confidence for fine-grained filtering
Dim highConfidenceWords = result.Words _
.Where(Function(w) w.Confidence > 85) _
.Select(Function(w) w.Text)
GdPicture requires three nested loops — blocks, lines, words — to reach the same data, with index-based method calls at each level (GetOCRResultBlockLineWordConfidence(resultId, b, l, w)). The confidence scores how-to demonstrates the structured access model for common use cases.
Structured Data and Result Access
Extracting per-word coordinates and confidence values is a common requirement for document understanding workflows — invoice field extraction, form processing, and layout analysis all need it.
GdPicture Approach
GdPicture returns a resultId string from RunOCR(), and all result data is queried through that ID via method calls on the GdPictureOCR instance. Reaching per-word confidence requires iterating through three nested levels:
using GdPicture14;
public List<(string word, float confidence)> GetWordConfidences(int imageId)
{
var words = new List<(string, float)>();
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
string word = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w);
float conf = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w);
words.Add((word, conf));
}
}
}
return words;
}
using GdPicture14;
public List<(string word, float confidence)> GetWordConfidences(int imageId)
{
var words = new List<(string, float)>();
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
string word = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w);
float conf = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w);
words.Add((word, conf));
}
}
}
return words;
}
Imports GdPicture14
Public Function GetWordConfidences(imageId As Integer) As List(Of (word As String, confidence As Single))
Dim words As New List(Of (String, Single))()
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)
For b As Integer = 0 To blockCount - 1
Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)
For l As Integer = 0 To lineCount - 1
Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)
For w As Integer = 0 To wordCount - 1
Dim word As String = _ocr.GetOCRResultBlockLineWordText(resultId, b, l, w)
Dim conf As Single = _ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w)
words.Add((word, conf))
Next
Next
Next
Return words
End Function
This is not wrong — the data is accessible. But it requires understanding and maintaining a three-dimensional index into the result structure for every project that needs word-level data.
IronOCR Approach
OcrResult exposes .Words as a typed collection. LINQ works directly against it:
using IronOcr;
var result = new IronTesseract().Read("invoice.jpg");
// All words with position and confidence
foreach (var word in result.Words)
{
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}");
}
// Lines, paragraphs, and pages follow the same pattern
foreach (var line in result.Lines)
Console.WriteLine(line.Text);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters");
using IronOcr;
var result = new IronTesseract().Read("invoice.jpg");
// All words with position and confidence
foreach (var word in result.Words)
{
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}");
}
// Lines, paragraphs, and pages follow the same pattern
foreach (var line in result.Lines)
Console.WriteLine(line.Text);
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters");
Imports IronOcr
Dim result = New IronTesseract().Read("invoice.jpg")
' All words with position and confidence
For Each word In result.Words
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) — {word.Confidence:P0}")
Next
' Lines, paragraphs, and pages follow the same pattern
For Each line In result.Lines
Console.WriteLine(line.Text)
Next
For Each page In result.Pages
Console.WriteLine($"Page {page.PageNumber}: {page.Text.Length} characters")
Next
Region-based OCR — extracting text from a defined rectangle of a document — is similarly direct:
var region = new CropRectangle(0, 0, 600, 100); // header area
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
var headerText = new IronTesseract().Read(input).Text;
var region = new CropRectangle(0, 0, 600, 100); // header area
using var input = new OcrInput();
input.LoadImage("invoice.jpg", region);
var headerText = new IronTesseract().Read(input).Text;
Imports IronOcr
Dim region As New CropRectangle(0, 0, 600, 100) ' header area
Using input As New OcrInput()
input.LoadImage("invoice.jpg", region)
Dim headerText As String = New IronTesseract().Read(input).Text
End Using
The read results guide and region-based OCR guide show how these patterns extend to multi-page documents and structured field extraction workflows.
API Mapping Reference
| GdPicture.NET | IronOCR Equivalent |
|---|---|
LicenseManager.RegisterKEY("key") |
IronOcr.License.LicenseKey = "key" |
new GdPictureImaging() |
Not required — handled internally |
new GdPictureOCR() |
new IronTesseract() |
new GdPicturePDF() |
Not required — OcrInput.LoadPdf() handles this |
ocr.ResourceFolder = path |
Not required — resources bundled in NuGet |
imaging.CreateGdPictureImageFromFile(path) |
input.LoadImage(path) on OcrInput |
imaging.ReleaseGdPictureImage(imageId) |
using var input = new OcrInput() — automatic |
ocr.SetImage(imageId) |
Not required — OcrInput holds the image |
ocr.Language = "eng" |
ocr.Language = OcrLanguage.English |
ocr.RunOCR() → resultId |
ocr.Read(input) → OcrResult |
ocr.GetOCRResultText(resultId) |
result.Text |
ocr.GetOCRResultConfidence(resultId) |
result.Confidence |
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) |
result.Words[i].Text |
ocr.GetOCRResultBlockLineWordConfidence(resultId, b, l, w) |
result.Words[i].Confidence |
pdf.LoadFromFile(path, false) → GdPictureStatus |
input.LoadPdf(path) — throws on failure |
pdf.LoadFromFile(path, password) |
input.LoadPdf(path, Password: password) |
pdf.RenderPageToGdPictureImage(200, false) |
Not required — IronOCR renders internally |
pdf.SelectPage(i) |
Not required — all pages processed by default |
pdf.GetPageCount() |
Not required — or result.Pages.Count |
pdf.OcrPage("eng", resourcePath, "", 200) |
result.SaveAsSearchablePdf(outputPath) |
pdf.SaveToFile(outputPath, true) |
result.SaveAsSearchablePdf(outputPath) |
imaging.GetStat() / ocr.GetStat() |
Standard .NET exceptions |
GdPictureStatus.OK check after each call |
Not required — exceptions propagate normally |
using GdPicture14; |
using IronOcr; |
When Teams Consider Moving from GdPicture.NET to IronOCR
OCR Is the Only Requirement
Teams implementing document ingestion pipelines — intake of scanned invoices, processing of medical forms, extraction of contract text — often evaluate GdPicture because it appears in enterprise software directories alongside LEADTOOLS and Kofax. Once the plugin dependency table becomes clear, the picture changes. The PDF Plugin is not optional if PDF input is required. The Document Imaging Plugin is not optional if preprocessing is required. A team that needs OCR from scanned PDFs with deskew correction ends up with three plugin licenses at significant Professional entry cost. When the requirement is text extraction and nothing in the GdPicture feature list beyond that is needed, that pricing represents pure overhead. IronOCR at $2,999 Professional covers the identical workflow — image input, PDF input, preprocessing, searchable PDF output — from a single package with no plugin decisions to make.
Memory Leak Incidents in Production
The integer ID lifecycle becomes a production incident waiting to happen in high-throughput document processing services. A web API endpoint that processes uploaded PDFs under load will eventually have a code path where the image release call is not reached — an early return in an error branch, an unhandled exception before the cleanup block, or simply a new developer unfamiliar with the pattern adding a helper method that takes an integer image handle as a parameter and forgets to release it. The memory grows, the service slows, and the root cause is not obvious in a heap dump. Teams that have debugged one of these incidents typically want a model where the runtime handles cleanup. The standard disposal pattern in IronOCR is that model — structured cleanup enforced by the compiler rather than by developer discipline at every call site.
Version Upgrade Friction
GdPicture embeds the major version number in its namespace. An upgrade from version 14 to version 15 requires updating that namespace directive in every source file that references GdPicture classes. In a large application with OCR, PDF handling, and image processing spread across dozens of services and utility classes, that is a non-trivial migration task — and it provides no functional benefit. Every modern .NET library from Microsoft.EntityFrameworkCore to Newtonsoft.Json uses version-agnostic namespaces, managing versioning through the NuGet package version number instead. IronOCR follows that convention: the same namespace import applies from version 1 to the current release.
Microservice and Container Deployment
GdPicture OCR requires an external resource folder containing language data files to be present at a specific path at runtime. In a containerized deployment, that means either baking those files into the Docker image, mounting a volume, or configuring a startup script to download them. The typical result is a Docker image that is several hundred megabytes larger than necessary, a deployment checklist item that gets missed, and production failures on clean container restarts where the volume is not mounted. IronOCR bundles English support in the package itself; additional languages install as NuGet packages that are part of the build output. The container gets what it needs from the restore step, and there are no paths to configure. The Docker deployment guide and Linux deployment guide cover the specific configuration for each target environment.
Development Teams Without Document Imaging Expertise
GdPicture's surface area is large by necessity — it covers a full document imaging platform. For a developer joining a team to maintain an OCR microservice, understanding the imaging component, the OCR component, the PDF component, the integer ID lifecycle, status return code handling, and the resource folder configuration is a steep initial investment. IronOCR's surface area for OCR is three classes: the engine, the input container, and the result object. That is the entire mental model for 90% of use cases. Onboarding a new developer means pointing them at IronTesseract setup rather than at a multi-module SDK guide.
Common Migration Considerations
Replacing Component Initialization
GdPicture requires a four-step initialization sequence: register the license key through LicenseManager, initialize GdPictureImaging, initialize GdPictureOCR, and set the resource folder path. IronOCR reduces this to a single property assignment that belongs in Program.cs or Startup.cs:
// Remove
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR";
// Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY";
// IronTesseract instances are instantiated per-use or as a singleton
// Remove
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR";
// Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY";
// IronTesseract instances are instantiated per-use or as a singleton
' Remove
Dim lm As New LicenseManager()
lm.RegisterKEY("GDPICTURE-LICENSE-KEY")
_imaging = New GdPictureImaging()
_ocr = New GdPictureOCR()
_ocr.ResourceFolder = "C:\GdPicture\Resources\OCR"
' Replace with
IronOcr.License.LicenseKey = "IRONOCR-LICENSE-KEY"
' IronTesseract instances are instantiated per-use or as a singleton
Eliminating Image ID Tracking
Every CreateGdPictureImageFromFile call and every RenderPageToGdPictureImage call has a corresponding ReleaseGdPictureImage call somewhere in the existing codebase. Those cleanup calls are removed entirely in IronOCR — OcrInput handles disposal. The migration is mechanical: find each int imageId = ..., find its paired ReleaseGdPictureImage(imageId), wrap the loading code in using var input = new OcrInput(), and replace the GdPicture method calls with their IronOCR equivalents.
// Remove
int imageId = _imaging.CreateGdPictureImageFromFile(path);
if (imageId == 0) throw new Exception(_imaging.GetStat().ToString());
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId)) throw new Exception(_ocr.GetStat().ToString());
string text = _ocr.GetOCRResultText(resultId);
_imaging.ReleaseGdPictureImage(imageId);
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
var result = new IronTesseract().Read(input);
string text = result.Text;
// Remove
int imageId = _imaging.CreateGdPictureImageFromFile(path);
if (imageId == 0) throw new Exception(_imaging.GetStat().ToString());
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId)) throw new Exception(_ocr.GetStat().ToString());
string text = _ocr.GetOCRResultText(resultId);
_imaging.ReleaseGdPictureImage(imageId);
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
var result = new IronTesseract().Read(input);
string text = result.Text;
Imports System
' Remove
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
If imageId = 0 Then Throw New Exception(_imaging.GetStat().ToString())
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then Throw New Exception(_ocr.GetStat().ToString())
Dim text As String = _ocr.GetOCRResultText(resultId)
_imaging.ReleaseGdPictureImage(imageId)
' Replace with
Using input As New OcrInput()
input.LoadImage(path)
Dim result = New IronTesseract().Read(input)
Dim text As String = result.Text
End Using
Adapting Error Handling
GdPicture uses GdPictureStatus return codes checked after every significant operation. IronOCR throws standard .NET exceptions. The migration replaces if (status != GdPictureStatus.OK) guards with try/catch blocks that handle IronOcr.Exceptions.OcrException for OCR-specific failures and standard IOException and FileNotFoundException for input failures. The reading text from images tutorial covers error handling patterns for common scenarios.
Language Pack Deployment
GdPicture language files are .traineddata files in a filesystem folder. IronOCR language packs are NuGet packages. The migration removes the resource folder from the deployment manifest and adds NuGet package references for each required language:
# Remove filesystem dependency for each language
# Add NuGet package instead
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
# Remove filesystem dependency for each language
# Add NuGet package instead
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
dotnet add package IronOcr.Languages.ChineseSimplified
The multiple languages guide covers configuration for multi-language documents where both primary and secondary languages are specified on the IronTesseract instance.
Additional IronOCR Capabilities
Beyond the areas covered in this comparison, IronOCR includes features that do not have direct GdPicture equivalents in a pure OCR context:
- Async OCR —
ReadAsyncfor non-blocking operation in ASP.NET Core request handlers and background services - Page rotation detection — automatic detection and correction of rotated pages without manual preprocessing
- Progress tracking — callback-based progress events for long-running batch jobs exposed to UI progress bars
- Stream input —
input.LoadImage(stream)acceptsSystem.IO.Streamdirectly, avoiding temporary file writes in pipeline architectures - Specialized document reading — purpose-built handling for passports, license plates, MICR cheques, and handwritten text through the same
IronTesseractinterface
.NET Compatibility and Future Readiness
IronOCR targets .NET 8, .NET 9, .NET Standard 2.0, and .NET Framework 4.6.2 and later. The single NuGet package deploys identically on Windows x64, Windows x86, Linux x64, and macOS. GdPicture.NET also supports cross-platform targets, but its plugin architecture means that Linux and container deployments must account for each plugin's native dependencies individually. IronOCR's namespace has remained IronOcr across all major version changes, meaning upgrade paths do not require codebase-wide find-and-replace operations. As .NET 10 arrives in late 2026, IronOCR's track record of maintaining compatibility across successive .NET releases without breaking API changes provides a stable foundation for applications with long maintenance cycles.
Conclusion
GdPicture.NET is a legitimate choice for teams that need a consolidated document imaging platform — scanner integration, PDF editing, annotation, DICOM support, and OCR in one SDK from one vendor. That use case exists. The plugin model makes sense when most of those capabilities are actually in scope.
The mismatch appears when the requirement is OCR and the answer is GdPicture. The PDF Plugin is not separable from the OCR-from-PDF use case. The Document Imaging Plugin is not separable from preprocessing. The integer ID lifecycle is not optional — it is the fundamental resource management model for the entire SDK. A team that needs to extract text from scanned PDFs absorbs all of that complexity and all of that cost, and none of the additional capabilities justify it for their workload.
The pricing gap over multiple years can be substantial for a single-developer Professional workflow. The code complexity gap is measurable: 25+ lines for basic OCR with cleanup versus one. The operational gap — external resource folders, version-specific namespaces, multi-component initialization — shows up as deployment friction and onboarding time rather than as line count, but it is real.
IronOCR at $999–$2,999 perpetual delivers the same core OCR outcomes — image text extraction, scanned PDF processing, searchable PDF output, 125+ languages, confidence scoring, structured data access — through standard .NET patterns that require no specialized SDK knowledge to maintain. For teams where OCR is the objective and not a component in a broader document management platform, that is the more appropriate fit.
Frequently Asked Questions
What is GdPicture.NET OCR?
GdPicture.NET OCR is an OCR solution used by developers and enterprises to extract text from images and documents. It is one of several OCR options evaluated alongside IronOCR for .NET application development.
How does IronOCR compare to GdPicture.NET OCR for .NET developers?
IronOCR is a NuGet-native .NET OCR library using IronTesseract as its core engine. Compared to GdPicture.NET OCR, it offers simpler deployment (no SDK installers), flat-rate pricing, and a clean C# API without COM interop or cloud dependencies.
Is IronOCR easier to set up than GdPicture.NET OCR?
IronOCR installs via a single NuGet package. There are no SDK installers, license files to copy, COM components to register, or separate runtime binaries to manage. The entire OCR engine is bundled in the package.
What accuracy differences exist between GdPicture.NET OCR and IronOCR?
IronOCR achieves high recognition accuracy for standard business documents, invoices, receipts, and scanned forms. For highly degraded documents or uncommon scripts, accuracy varies by source quality. IronOCR includes image preprocessing filters to improve recognition on low-quality inputs.
Does IronOCR support PDF text extraction?
Yes. IronOCR extracts text from both native PDFs and scanned PDF images in a single call. It also supports multi-page TIFF files, images, and streams. For scanned PDFs, OCR is applied page-by-page with per-page result objects.
How does GdPicture.NET OCR licensing compare to IronOCR?
IronOCR uses a flat-rate perpetual license with no per-page or per-scan charges. Organizations processing high document volumes pay the same license cost regardless of volume. Details and volume pricing are on the IronOCR licensing page.
What languages does IronOCR support?
IronOCR supports 127 languages via separate NuGet language packs. Adding a language requires a single 'dotnet add package IronOcr.Languages.{Language}' command. No manual file placement or path configuration is needed.
How do I install IronOCR in a .NET project?
Install via NuGet: 'Install-Package IronOcr' in Package Manager Console or 'dotnet add package IronOcr' in the CLI. Additional language packs are installed the same way. No native SDK installer is required.
Is IronOCR suitable for Docker and containerized deployments, unlike GdPicture.NET?
Yes. IronOCR works in Docker containers via its NuGet package. The license key is set via an environment variable. No license files, SDK paths, or volume mounts are required for the OCR engine itself.
Can I try IronOCR before purchasing, compared to GdPicture.NET?
Yes. IronOCR trial mode processes documents and returns OCR results with a watermark overlay on output. You can verify accuracy on your own documents before purchasing a license.
Does IronOCR support barcode reading alongside text extraction?
IronOCR focuses on text extraction and OCR. For barcode reading, Iron Software provides IronBarcode as a companion library. Both are available individually or as part of the Iron Suite bundle.
Is it easy to migrate from GdPicture.NET OCR to IronOCR?
Migration from GdPicture.NET OCR to IronOCR typically involves replacing initialization sequences with IronTesseract instantiation, removing COM lifecycle management, and updating API calls. Most migrations reduce code complexity significantly.

