ABBYY FineReader vs Tesseract: Comparação de OCR
OCR.space não possui um pacote NuGet . Esse único fato define cada decisão de integração que um desenvolvedor .NET faz ao escolhê-lo: você escreve um HttpClient personalizado, codifica seus documentos em base64, POST para https://api.ocr.space/parse/image, analisa manualmente a resposta JSON, captura HTTP 429s, implementa backoff exponencial, constrói registro de limite de taxa e define seus próprios tipos de exceção — tudo isso antes de um único caractere de texto chegar à sua aplicação. Este artigo examina o custo real dessa implementação "faça você mesmo" em termos de código, tempo e confiabilidade em produção, e a compara diretamente com o IronOCR , uma biblioteca nativa do .NET fornecida como um único pacote NuGet .
Entendendo o OCR.space
OCR.space é uma API REST freemium que processa imagens e PDFs em servidores remotos operados pela OCR.space. O serviço se posiciona em torno de seu plano gratuito: 25.000 solicitações por mês sem necessidade de cartão de crédito, o que atrai desenvolvedores que estão experimentando OCR ou criando projetos pessoais. Não existe pacote NuGet , nenhum SDK oficial do .NET e nenhuma biblioteca cliente fortemente tipada em qualquer linguagem. Toda integração .NET é construída manualmente em cima de HttpClient.
A realidade arquitetônica para desenvolvedores .NET :
- Sem pacote NuGet : Suporte nativo zero para .NET . Sem IntelliSense, sem modelos tipados, sem tratamento de erros integrado.
- Processamento exclusivamente em nuvem: todos os documentos saem da sua infraestrutura. Não existe opção para instalação local nem caminho de implantação auto-hospedada.
- Integração REST manual: Os desenvolvedores codificam arquivos em base64, constroem
FormUrlEncodedContentouMultipartFormDataContent, e analisam respostas JSON brutas. - Limitação de taxa personalizada: O plano gratuito impõe um limite de 60 requisições por minuto e um limite rígido de 500 requisições por dia por IP. Aplicações em produção devem implementar essa lógica por conta própria.
- Restrições de tamanho de arquivo: O plano gratuito rejeita arquivos com mais de 5 MB. PDFs com várias páginas geralmente excedem esse limite.
- Saída em PDF com marca d'água: A geração de PDFs pesquisáveis no plano gratuito incorpora marcas d'água do OCR.space, tornando a saída inutilizável para entrega ao cliente ou arquivamento de documentos.
- Sem SLA: O nível gratuito não inclui nenhuma garantia de tempo de atividade. Os planos pagos oferecem limites maiores, mas as mesmas restrições arquitetônicas.
O que os desenvolvedores de integração REST devem escrever
A documentação do OCR.space direciona os desenvolvedores para o endpoint REST e os deixa responsáveis por construir todo o resto. O cliente .NET mínimo viável — antes de qualquer consideração sobre reforço de segurança para produção — tem a seguinte aparência:
// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter;
private const string ApiEndpoint = "https://api.ocr.space/parse/image";
private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
private const int RateLimitPerMinute = 60;
public OcrSpaceApiClient(string apiKey)
{
_apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
}
public async Task<OcrResult> ExtractTextFromFileAsync(
string filePath,
string language = "eng",
CancellationToken cancellationToken = default)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("Image file not found", filePath);
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > MaxFileSizeFree)
{
throw new InvalidOperationException(
$"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
}
await _rateLimiter.WaitAsync(cancellationToken);
try
{
// Document leaves your infrastructure here
byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
string base64Image = Convert.ToBase64String(imageBytes);
string mimeType = GetMimeType(filePath);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
new KeyValuePair<string, string>("language", language),
new KeyValuePair<string, string>("isOverlayRequired", "false"),
new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
new KeyValuePair<string, string>("detectOrientation", "true"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("OCREngine", "2")
});
var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);
if (!response.IsSuccessStatusCode)
{
string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
throw new OcrSpaceException(
$"OCR.space API returned {response.StatusCode}: {errorBody}",
(int)response.StatusCode);
}
string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
return ParseOcrResponse(jsonResponse);
}
finally
{
_ = Task.Run(async () =>
{
await Task.Delay(60000 / RateLimitPerMinute);
_rateLimiter.Release();
});
}
}
private OcrResult ParseOcrResponse(string jsonResponse)
{
using var doc = JsonDocument.Parse(jsonResponse);
var root = doc.RootElement;
if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
{
string errorMessage = "OCR processing failed";
if (root.TryGetProperty("ErrorMessage", out var errorMessages))
{
// Parse the array of error strings manually
var messages = new List<string>();
if (errorMessages.ValueKind == JsonValueKind.Array)
{
foreach (var msg in errorMessages.EnumerateArray())
messages.Add(msg.GetString() ?? "Unknown error");
}
errorMessage = string.Join("; ", messages);
}
throw new OcrSpaceException(errorMessage, 500);
}
var result = new OcrResult();
if (root.TryGetProperty("ParsedResults", out var parsedResults))
{
foreach (var parsedResult in parsedResults.EnumerateArray())
{
if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
result.ParsedText += parsedText.GetString();
if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
result.ExitCodes.Add(exitCode.GetInt32());
}
}
return result;
}
private string GetMimeType(string filePath) =>
Path.GetExtension(filePath).ToLowerInvariant() switch
{
".png" => "image/png",
".jpg" or ".jpeg" => "image/jpeg",
".bmp" => "image/bmp",
".tiff" or ".tif" => "image/tiff",
".pdf" => "application/pdf",
_ => "application/octet-stream"
};
public void Dispose()
{
_httpClient?.Dispose();
_rateLimiter?.Dispose();
}
}
// Custom models — no SDK means you define these yourself
public class OcrResult
{
public string ParsedText { get; set; } = string.Empty;
public List<string> Warnings { get; } = new();
public List<int> ExitCodes { get; } = new();
}
public class OcrSpaceException : Exception
{
public int StatusCode { get; }
public OcrSpaceException(string message, int statusCode) : base(message)
=> StatusCode = statusCode;
}
// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter;
private const string ApiEndpoint = "https://api.ocr.space/parse/image";
private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
private const int RateLimitPerMinute = 60;
public OcrSpaceApiClient(string apiKey)
{
_apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
}
public async Task<OcrResult> ExtractTextFromFileAsync(
string filePath,
string language = "eng",
CancellationToken cancellationToken = default)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("Image file not found", filePath);
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > MaxFileSizeFree)
{
throw new InvalidOperationException(
$"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
}
await _rateLimiter.WaitAsync(cancellationToken);
try
{
// Document leaves your infrastructure here
byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
string base64Image = Convert.ToBase64String(imageBytes);
string mimeType = GetMimeType(filePath);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
new KeyValuePair<string, string>("language", language),
new KeyValuePair<string, string>("isOverlayRequired", "false"),
new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
new KeyValuePair<string, string>("detectOrientation", "true"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("OCREngine", "2")
});
var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);
if (!response.IsSuccessStatusCode)
{
string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
throw new OcrSpaceException(
$"OCR.space API returned {response.StatusCode}: {errorBody}",
(int)response.StatusCode);
}
string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
return ParseOcrResponse(jsonResponse);
}
finally
{
_ = Task.Run(async () =>
{
await Task.Delay(60000 / RateLimitPerMinute);
_rateLimiter.Release();
});
}
}
private OcrResult ParseOcrResponse(string jsonResponse)
{
using var doc = JsonDocument.Parse(jsonResponse);
var root = doc.RootElement;
if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
{
string errorMessage = "OCR processing failed";
if (root.TryGetProperty("ErrorMessage", out var errorMessages))
{
// Parse the array of error strings manually
var messages = new List<string>();
if (errorMessages.ValueKind == JsonValueKind.Array)
{
foreach (var msg in errorMessages.EnumerateArray())
messages.Add(msg.GetString() ?? "Unknown error");
}
errorMessage = string.Join("; ", messages);
}
throw new OcrSpaceException(errorMessage, 500);
}
var result = new OcrResult();
if (root.TryGetProperty("ParsedResults", out var parsedResults))
{
foreach (var parsedResult in parsedResults.EnumerateArray())
{
if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
result.ParsedText += parsedText.GetString();
if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
result.ExitCodes.Add(exitCode.GetInt32());
}
}
return result;
}
private string GetMimeType(string filePath) =>
Path.GetExtension(filePath).ToLowerInvariant() switch
{
".png" => "image/png",
".jpg" or ".jpeg" => "image/jpeg",
".bmp" => "image/bmp",
".tiff" or ".tif" => "image/tiff",
".pdf" => "application/pdf",
_ => "application/octet-stream"
};
public void Dispose()
{
_httpClient?.Dispose();
_rateLimiter?.Dispose();
}
}
// Custom models — no SDK means you define these yourself
public class OcrResult
{
public string ParsedText { get; set; } = string.Empty;
public List<string> Warnings { get; } = new();
public List<int> ExitCodes { get; } = new();
}
public class OcrSpaceException : Exception
{
public int StatusCode { get; }
public OcrSpaceException(string message, int statusCode) : base(message)
=> StatusCode = statusCode;
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports System.Threading.Tasks
' OCR.space: what you build before the first line of your actual application
Public Class OcrSpaceApiClient
Implements IDisposable
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _apiKey As String
Private ReadOnly _rateLimiter As SemaphoreSlim
Private Const ApiEndpoint As String = "https://api.ocr.space/parse/image"
Private Const MaxFileSizeFree As Integer = 5 * 1024 * 1024 ' 5MB — hard limit on free tier
Private Const RateLimitPerMinute As Integer = 60
Public Sub New(apiKey As String)
_apiKey = If(apiKey, Throw New ArgumentNullException(NameOf(apiKey)))
_httpClient = New HttpClient()
_httpClient.Timeout = TimeSpan.FromSeconds(120)
_rateLimiter = New SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute)
End Sub
Public Async Function ExtractTextFromFileAsync(filePath As String, Optional language As String = "eng", Optional cancellationToken As CancellationToken = Nothing) As Task(Of OcrResult)
If Not File.Exists(filePath) Then
Throw New FileNotFoundException("Image file not found", filePath)
End If
Dim fileInfo = New FileInfo(filePath)
If fileInfo.Length > MaxFileSizeFree Then
Throw New InvalidOperationException($"File size {fileInfo.Length \ 1024 \ 1024}MB exceeds free tier limit of 5MB.")
End If
Await _rateLimiter.WaitAsync(cancellationToken)
Try
' Document leaves your infrastructure here
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(filePath, cancellationToken)
Dim base64Image As String = Convert.ToBase64String(imageBytes)
Dim mimeType As String = GetMimeType(filePath)
Dim formContent = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:{mimeType};base64,{base64Image}"),
New KeyValuePair(Of String, String)("language", language),
New KeyValuePair(Of String, String)("isOverlayRequired", "false"),
New KeyValuePair(Of String, String)("filetype", Path.GetExtension(filePath).TrimStart("."c)),
New KeyValuePair(Of String, String)("detectOrientation", "true"),
New KeyValuePair(Of String, String)("scale", "true"),
New KeyValuePair(Of String, String)("OCREngine", "2")
})
Dim response = Await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken)
If Not response.IsSuccessStatusCode Then
Dim errorBody = Await response.Content.ReadAsStringAsync(cancellationToken)
Throw New OcrSpaceException($"OCR.space API returned {response.StatusCode}: {errorBody}", CInt(response.StatusCode))
End If
Dim jsonResponse = Await response.Content.ReadAsStringAsync(cancellationToken)
Return ParseOcrResponse(jsonResponse)
Finally
_ = Task.Run(Async Function()
Await Task.Delay(60000 \ RateLimitPerMinute)
_rateLimiter.Release()
End Function)
End Try
End Function
Private Function ParseOcrResponse(jsonResponse As String) As OcrResult
Using doc = JsonDocument.Parse(jsonResponse)
Dim root = doc.RootElement
If root.TryGetProperty("IsErroredOnProcessing", ByRef isErrored) AndAlso isErrored.GetBoolean() Then
Dim errorMessage As String = "OCR processing failed"
If root.TryGetProperty("ErrorMessage", ByRef errorMessages) Then
' Parse the array of error strings manually
Dim messages = New List(Of String)()
If errorMessages.ValueKind = JsonValueKind.Array Then
For Each msg In errorMessages.EnumerateArray()
messages.Add(msg.GetString() OrElse "Unknown error")
Next
End If
errorMessage = String.Join("; ", messages)
End If
Throw New OcrSpaceException(errorMessage, 500)
End If
Dim result = New OcrResult()
If root.TryGetProperty("ParsedResults", ByRef parsedResults) Then
For Each parsedResult In parsedResults.EnumerateArray()
If parsedResult.TryGetProperty("ParsedText", ByRef parsedText) Then
result.ParsedText &= parsedText.GetString()
End If
If parsedResult.TryGetProperty("FileParseExitCode", ByRef exitCode) Then
result.ExitCodes.Add(exitCode.GetInt32())
End If
Next
End If
Return result
End Using
End Function
Private Function GetMimeType(filePath As String) As String
Return Path.GetExtension(filePath).ToLowerInvariant() Select Case
Case ".png" : Return "image/png"
Case ".jpg", ".jpeg" : Return "image/jpeg"
Case ".bmp" : Return "image/bmp"
Case ".tiff", ".tif" : Return "image/tiff"
Case ".pdf" : Return "application/pdf"
Case Else : Return "application/octet-stream"
End Select
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_httpClient?.Dispose()
_rateLimiter?.Dispose()
End Sub
End Class
' Custom models — no SDK means you define these yourself
Public Class OcrResult
Public Property ParsedText As String = String.Empty
Public ReadOnly Property Warnings As New List(Of String)()
Public ReadOnly Property ExitCodes As New List(Of Integer)()
End Class
Public Class OcrSpaceException
Inherits Exception
Public ReadOnly Property StatusCode As Integer
Public Sub New(message As String, statusCode As Integer)
MyBase.New(message)
Me.StatusCode = statusCode
End Sub
End Class
Trata-se de mais de 90 linhas de código de infraestrutura que existem antes do primeiro método de lógica de negócios. Todas as integrações com o OCR.space incluem esse código padrão (ou algo equivalente), e todas as equipes que o escrevem estão resolvendo o mesmo problema que o OCR.space optou por não resolver.
Entendendo o IronOCR
IronOCR é uma biblioteca comercial de OCR for .NET, instalada via o pacote NuGet IronOcr. Ele integra um mecanismo Tesseract 5 otimizado com pré-processamento automático, entrada nativa de PDF, saída de PDF pesquisável, mais de 125 pacotes de idiomas e extração de resultados estruturados — tudo sem dependência da nuvem, chaves de API ou cobranças por solicitação. Os documentos são processados localmente, independentemente da infraestrutura que execute a aplicação .NET .
Principais características:
- Pacote NuGet único:
dotnet add package IronOcré a instalação completa. Sem binários nativos para gerenciar, sem diretórios tessdata, sem variáveis de ambiente. - API fortemente tipada:
IronTesseract,OcrInput,OcrResult, eCropRectanglesão tipos de primeira classe .NET com suporte completo ao IntelliSense. - Pré-processamento automático: os filtros de correção de desalinhamento, redução de ruído, ajuste de contraste, binarização e aprimoramento de resolução são aplicados automaticamente ou sob demanda, sem a necessidade de bibliotecas externas.
- Suporte nativo a PDF: Tanto a leitura de PDFs digitalizados quanto a geração de PDFs pesquisáveis são recursos integrados. Sem limite de tamanho além da memória disponível.
- Execução local: Nenhuma chamada de rede ocorre durante o OCR. A biblioteca é executada inteiramente dentro do processo. Ambientes isolados da internet funcionam sem alterações de configuração.
- Thread-safe: Várias instâncias de
IronTesseractexecutam simultaneamente sem bloqueio ou gerenciamento de contenção. - Licenciamento perpétuo: $999 Lite / $1,499 Plus / $2,999 Professional. Sem cobrança por solicitação, independentemente do volume.
Comparação de recursos
| Recurso | OCR.space | IronOCR |
|---|---|---|
| Pacote NuGet | Nenhuma — somente API REST | IronOcr — suporte completo .NET |
| Local de processamento | Servidores em nuvem OCR.space | Local — sua infraestrutura |
| Nível gratuito | 25.000 requisições/mês (vagas limitadas) | Teste disponível |
| Preços pagos | Contate OCR.space para preços atuais | $2,999 único perpétuo |
| Limitação de taxa | 60 requisições/min, 500/dia (gratuito) | None |
| Capacidade offline | Não | Sim — totalmente isolado com ar. |
| Privacidade de dados | Documentos enviados a terceiros | Os documentos permanecem em seus servidores. |
Comparação Detalhada de Recursos
| Recurso | OCR.space | IronOCR |
|---|---|---|
| Integração | ||
| Pacote NuGet | None | Sim (IronOcr) |
| Modelos fortemente tipados | Nenhuma — análise manual de JSON | Sim (OcrResult, OcrInput) |
| Suporte ao IntelliSense | None | Completo |
| Tipos de exceção personalizados | Você precisa se definir. | Embutido |
| Lógica de repetição | Você precisa se construir sozinho | Embutido |
| Processamento | ||
| Local de processamento | Servidores em nuvem | Processo local |
| É necessário ter acesso à internet. | Sim — todas as chamadas | Não |
| Suporte com espaço de ar | Não | Sim |
| Limites de taxa | Sim — todos os planos | None |
| Limites de tamanho de arquivo | 5 MB (nível gratuito) | Apenas memória |
| Capacidade de OCR | ||
| OCR de imagem | Sim | Sim |
| OCR de PDF | Sim (com limites) | Sim (nativo, sem limite de tamanho) |
| Saída em PDF pesquisável | Com marca d'água na versão gratuita. | Saída limpa, sem marcas d'água. |
| Pré-processamento automático | Nenhum (somente no servidor) | Correção de distorção, redução de ruído, contraste, binarização, aumento da resolução |
| Controle de pré-processamento manual | None | API de filtro completa |
| Suporte linguístico | aproximadamente 25 idiomas | Mais de 125 idiomas disponíveis através dos pacotes NuGet. |
| Vários idiomas por documento | Limitado | Sim — línguas primárias e secundárias |
| Saída estruturada (palavras/linhas/páginas) | Não — somente texto simples | Sim — páginas, parágrafos, linhas, palavras com coordenadas. |
| Pontuações de confiança | Não | Sim — por resultado e por palavra |
| OCR baseado em região | Não | Sim — CropRectangle |
| Leitura de código de barras durante OCR | Não | Sim — ReadBarCodes = true |
| Implantação | ||
| Docker | Requer conexão de internet de saída. | Funciona perfeitamente assim que é conectado. |
| Linux | Requer conexão de internet de saída. | Suporte completo |
| Compatível com HIPAA | Risco — os documentos saem do seu controle. | Sim — sem transmissão externa |
| Compatível com o RGPD | Risco — Transferência de dados da UE incerta | Sim — não há tratamento de dados por terceiros. |
| Preços | ||
| Modelo de preços | Assinatura mensal | Licença perpétua única |
| Preço de entrada | Contate OCR.space para preços atuais | $999 único |
| Custo por documento em escala | Sim | None |
| SLA | Nenhum (gratuito), varia (pago) | SLA Enterprise disponível |
Profundidade do SDK versus HTTP bruto: o custo de integração
A diferença entre o OCR.space e um SDK nativo não é uma questão de preferência de estilo. É medido em horas de tempo de desenvolvimento, área de superfície para bugs e carga de manutenção em cada implantação.
Abordagem OCR.space
Todo desenvolvedor .NET que utiliza o OCR.space cria seu próprio cliente HTTP. A implementação mais simples possível — extração básica de texto sem lógica de repetição, sem suporte a lotes e sem pré-processamento — ainda possui mais de 50 linhas:
// OCR.space: minimum viable extraction —50+lines before business logic
public class OcrSpaceBasicExtraction
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
public OcrSpaceBasicExtraction(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
}
public async Task<string> ExtractText(string imagePath)
{
// Read file and encode — document leaves your infrastructure
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng")
});
// Send to cloud
var response = await _httpClient.PostAsync(
"https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
throw new Exception($"API error: {response.StatusCode}");
// Parse JSON manually — no typed models
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
}
// OCR.space: minimum viable extraction —50+lines before business logic
public class OcrSpaceBasicExtraction
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
public OcrSpaceBasicExtraction(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
}
public async Task<string> ExtractText(string imagePath)
{
// Read file and encode — document leaves your infrastructure
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng")
});
// Send to cloud
var response = await _httpClient.PostAsync(
"https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
throw new Exception($"API error: {response.StatusCode}");
// Parse JSON manually — no typed models
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
}
Imports System.Net.Http
Imports System.IO
Imports System.Threading.Tasks
Imports System.Text.Json
Public Class OcrSpaceBasicExtraction
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _apiKey As String
Public Sub New(apiKey As String)
_apiKey = apiKey
_httpClient = New HttpClient()
End Sub
Public Async Function ExtractText(imagePath As String) As Task(Of String)
' Read file and encode — document leaves your infrastructure
Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim content = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
New KeyValuePair(Of String, String)("language", "eng")
})
' Send to cloud
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
If Not response.IsSuccessStatusCode Then
Throw New Exception($"API error: {response.StatusCode}")
End If
' Parse JSON manually — no typed models
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Function
End Class
Ao adicionar o processamento em lote, a contagem de linhas sobe para mais de 80, porque cada solicitação em lote precisa de limitação de taxa, lógica de repetição por solicitação com recuo exponencial e tratamento explícito para respostas HTTP 429. Ao adicionar processamento de PDF, você implementa uma verificação de tamanho de arquivo de 5 MB antes de cada chamada, pois o plano gratuito rejeitará arquivos maiores com um erro em vez de um aviso.
Abordagem IronOCR
A instalação do IronOCR a partir do NuGet substitui todo esse código de infraestrutura por chamadas de método que já existem:
// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);
// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);
A classe IronTesseract lida com a leitura de arquivos, pré-processamento, execução do mecanismo e construção de resultados. Nenhum cliente HTTP. Sem codificação base64. Sem análise JSON. Sem chave de API. Sem limitador de taxa. O tutorial sobre leitura de texto em imagens aborda diversas variações de entrada — caminhos de arquivos, matrizes de bytes, fluxos, bitmaps — que seguem o mesmo padrão de chamada única.
Os números de complexidade do código extraídos dos arquivos de origem não são hipotéticos:
| Cenário | Linhas OCR.space | Linhas IronOCR |
|---|---|---|
| Extração básica | 50+ | 3 |
| Processamento de PDF | 60+ | 12 |
| Processamento em lote | 80+ | 15 |
| Tratamento de erros | 70+ | 15 |
| Infraestrutura completa do cliente | 200+ | 0 (O NuGet fornece isso) |
Processamento em lote e limites de taxa
O problema do limite de taxa é onde o plano gratuito do OCR.space apresenta as falhas mais visíveis em condições reais de produção.
Abordagem OCR.space
O processamento em lote sob OCR.space requer a construção e o gerenciamento de um SemaphoreSlim para permanecer dentro do limite de 60 solicitações por minuto, além da lógica de novo tentativa com backoff exponencial para respostas HTTP 429 que chegam quando a lógica do semáforo é imprecisa ou quando a infraestrutura compartilhada compartilha um endereço IP:
// OCR.space batch:80+lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter;
public OcrSpaceBatchProcessing(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
}
public async Task<Dictionary<string, string>> ProcessBatch(string[] imagePaths)
{
var results = new Dictionary<string, string>();
foreach (var imagePath in imagePaths)
{
await _rateLimiter.WaitAsync();
try
{
string text = await ProcessWithRetry(imagePath);
results[imagePath] = text;
}
finally
{
_ = Task.Run(async () =>
{
await Task.Delay(1000); // 1 second spacing
_rateLimiter.Release();
});
}
}
return results;
}
private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng")
});
var response = await _httpClient.PostAsync(
"https://api.ocr.space/parse/image", content);
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
// Rate limited — exponential backoff
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
continue;
}
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
catch (HttpRequestException) when (attempt < maxRetries - 1)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
}
}
// OCR.space batch:80+lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter;
public OcrSpaceBatchProcessing(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
}
public async Task<Dictionary<string, string>> ProcessBatch(string[] imagePaths)
{
var results = new Dictionary<string, string>();
foreach (var imagePath in imagePaths)
{
await _rateLimiter.WaitAsync();
try
{
string text = await ProcessWithRetry(imagePath);
results[imagePath] = text;
}
finally
{
_ = Task.Run(async () =>
{
await Task.Delay(1000); // 1 second spacing
_rateLimiter.Release();
});
}
}
return results;
}
private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng")
});
var response = await _httpClient.PostAsync(
"https://api.ocr.space/parse/image", content);
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
// Rate limited — exponential backoff
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
continue;
}
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
catch (HttpRequestException) when (attempt < maxRetries - 1)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports System.Threading.Tasks
Public Class OcrSpaceBatchProcessing
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _apiKey As String
Private ReadOnly _rateLimiter As SemaphoreSlim
Public Sub New(apiKey As String)
_apiKey = apiKey
_httpClient = New HttpClient()
_rateLimiter = New SemaphoreSlim(60, 60) ' Free tier: 60/minute
End Sub
Public Async Function ProcessBatch(imagePaths As String()) As Task(Of Dictionary(Of String, String))
Dim results As New Dictionary(Of String, String)()
For Each imagePath In imagePaths
Await _rateLimiter.WaitAsync()
Try
Dim text As String = Await ProcessWithRetry(imagePath)
results(imagePath) = text
Finally
_ = Task.Run(Async Function()
Await Task.Delay(1000) ' 1 second spacing
_rateLimiter.Release()
End Function)
End Try
Next
Return results
End Function
Private Async Function ProcessWithRetry(imagePath As String, Optional maxRetries As Integer = 3) As Task(Of String)
For attempt As Integer = 0 To maxRetries - 1
Try
Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim content = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
New KeyValuePair(Of String, String)("language", "eng")
})
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
If response.StatusCode = System.Net.HttpStatusCode.TooManyRequests Then
' Rate limited — exponential backoff
Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
Continue For
End If
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
Catch ex As HttpRequestException When attempt < maxRetries - 1
Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
End Try
Next
Throw New Exception($"Failed to process {imagePath} after {maxRetries} attempts")
End Function
End Class
O limite de 500 solicitações por dia no plano gratuito restringe ainda mais isso. Um aplicativo que processa 600 documentos em um único dia falhará no meio do processo, sem que o processamento seja retomado automaticamente até o próximo dia útil.
Abordagem IronOCR
IronOCR não possui limites de taxa. O processamento em lote é um ciclo:
//IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new Dictionary<string, string>();
var ocr = new IronTesseract();
foreach (var imagePath in imagePaths)
{
// Não rate limits, no retries, no cloud dependency
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
}
return results;
}
//IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new Dictionary<string, string>();
var ocr = new IronTesseract();
foreach (var imagePath in imagePaths)
{
// Não rate limits, no retries, no cloud dependency
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
}
return results;
}
Imports System.Collections.Generic
Public Class Example
'IronOCR batch: 8 lines, no rate limits, no retries needed
Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
Dim results As New Dictionary(Of String, String)()
Dim ocr As New IronTesseract()
For Each imagePath In imagePaths
' Não rate limits, no retries, no cloud dependency
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
Next
Return results
End Function
End Class
Reutilizar a instância de IronTesseract em todo o lote evita a sobrecarga de inicialização repetida do mecanismo. A velocidade de processamento é limitada pela CPU local e pela E/S de disco, não pela política de limitação de uma API remota. O exemplo de multithreading mostra como paralelizar entre núcleos quando a taxa de transferência é importante.
Processamento de PDF
O suporte a PDF ilustra a segunda grande lacuna estrutural.
Abordagem OCR.space
O OCR.space aceita uploads de PDFs nos planos pagos e parcialmente no plano gratuito, mas o limite de tamanho de arquivo de 5 MB nas contas gratuitas exclui a maioria dos documentos com várias páginas. A versão gratuita em PDF com recurso de busca inclui uma marca d'água. A implementação requer a extração de resultados por página e a verificação explícita do tamanho do arquivo antes de cada solicitação:
// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
var pageTexts = new List<string>();
// Reject before wasting a request quota entry
var fileInfo = new FileInfo(pdfPath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");
byte[] pdfBytes = File.ReadAllBytes(pdfPath);
string base64 = Convert.ToBase64String(pdfBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("filetype", "PDF"),
new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
throw new Exception($"API error: {response.StatusCode}");
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
{
foreach (var result in results.EnumerateArray())
{
if (result.TryGetProperty("ParsedText", out var text))
pageTexts.Add(text.GetString() ?? string.Empty);
}
}
return pageTexts;
}
// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
var pageTexts = new List<string>();
// Reject before wasting a request quota entry
var fileInfo = new FileInfo(pdfPath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");
byte[] pdfBytes = File.ReadAllBytes(pdfPath);
string base64 = Convert.ToBase64String(pdfBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("filetype", "PDF"),
new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
throw new Exception($"API error: {response.StatusCode}");
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
{
foreach (var result in results.EnumerateArray())
{
if (result.TryGetProperty("ParsedText", out var text))
pageTexts.Add(text.GetString() ?? string.Empty);
}
}
return pageTexts;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRSpaceClient
Private ReadOnly _apiKey As String
Private ReadOnly _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
Public Async Function ExtractTextFromPdf(pdfPath As String) As Task(Of List(Of String))
Dim pageTexts As New List(Of String)()
' Reject before wasting a request quota entry
Dim fileInfo As New FileInfo(pdfPath)
If fileInfo.Length > 5 * 1024 * 1024 Then
Throw New InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.")
End If
Dim pdfBytes As Byte() = File.ReadAllBytes(pdfPath)
Dim base64 As String = Convert.ToBase64String(pdfBytes)
Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:application/pdf;base64,{base64}"),
New KeyValuePair(Of String, String)("language", "eng"),
New KeyValuePair(Of String, String)("filetype", "PDF"),
New KeyValuePair(Of String, String)("isCreateSearchablePdf", "false") ' Watermarked on free tier
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
If Not response.IsSuccessStatusCode Then
Throw New Exception($"API error: {response.StatusCode}")
End If
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
If doc.RootElement.TryGetProperty("ParsedResults", results) Then
For Each result In results.EnumerateArray()
If result.TryGetProperty("ParsedText", text) Then
pageTexts.Add(If(text.GetString(), String.Empty))
End If
Next
End If
End Using
Return pageTexts
End Function
End Class
Abordagem IronOCR
O IronOCR lê PDFs nativamente. O limite é a memória disponível, não um limite arbitrário de tamanho de arquivo. A saída em PDF pesquisável gera arquivos limpos, sem marcas d'água, em qualquer nível de licença:
//IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
var pageTexts = new List<string>();
using var input = new OcrInput();
input.LoadPdf(pdfPath); // Não 5MB ceiling
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
pageTexts.Add(page.Text);
return pageTexts;
}
// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath);
}
//IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
var pageTexts = new List<string>();
using var input = new OcrInput();
input.LoadPdf(pdfPath); // Não 5MB ceiling
var result = new IronTesseract().Read(input);
foreach (var page in result.Pages)
pageTexts.Add(page.Text);
return pageTexts;
}
// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath);
}
Imports System.Collections.Generic
'IronOCR PDF: native support, no size limit, no watermarks
Public Function ExtractTextFromPdf(pdfPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
Using input As New OcrInput()
input.LoadPdf(pdfPath) ' Não 5MB ceiling
Dim result = New IronTesseract().Read(input)
For Each page In result.Pages
pageTexts.Add(page.Text)
Next
End Using
Return pageTexts
End Function
' Generate searchable PDF — no watermarks
Public Sub CreateSearchablePdf(inputPath As String, outputPath As String)
Dim result = New IronTesseract().Read(inputPath)
result.SaveAsSearchablePdf(outputPath)
End Sub
PDFs protegidos por senha são um único parâmetro: input.LoadPdf("encrypted.pdf", Password: "secret"). O guia prático de OCR em PDF aborda detalhadamente a seleção de intervalo de páginas e o gerenciamento de senhas. Para padrões de geração de PDFs pesquisáveis, consulte o guia de instruções para PDFs pesquisáveis .
Tratamento de erros
OCR.space fornece erros por meio de dois canais separados: códigos de status HTTP e uma bandeira JSON IsErroredOnProcessing. O código de produção deve verificar ambos, analisar a matriz de erros JSON quando IsErroredOnProcessing é verdadeiro, e inspecionar valores FileParseExitCode por página para falhas parciais. Nada disso é tipado — tudo é análise de string de JsonDocument:
// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
try
{
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
});
var response = await client.PostAsync("https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
{
switch (response.StatusCode)
{
case System.Net.HttpStatusCode.Unauthorized:
throw new Exception("Invalid API key");
case System.Net.HttpStatusCode.TooManyRequests:
throw new Exception("Rate limit exceeded — wait and retry");
case System.Net.HttpStatusCode.PaymentRequired:
throw new Exception("Quota exceeded — upgrade plan");
default:
throw new Exception($"API error: {response.StatusCode}");
}
}
// Second error layer: JSON-level failures
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
&& isError.GetBoolean())
{
var messages = new List<string>();
if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
{
foreach (var e in errors.EnumerateArray())
messages.Add(e.GetString() ?? "Unknown error");
}
throw new Exception(string.Join("; ", messages));
}
// Third layer: per-page exit codes
if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
{
foreach (var result in results.EnumerateArray())
{
if (result.TryGetProperty("FileParseExitCode", out var exitCode)
&& exitCode.GetInt32() != 1)
throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
}
}
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
catch (JsonException ex)
{
throw new Exception($"Invalid API response: {ex.Message}");
}
catch (HttpRequestException ex)
{
throw new Exception($"Network error: {ex.Message}");
}
}
// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
try
{
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
});
var response = await client.PostAsync("https://api.ocr.space/parse/image", content);
if (!response.IsSuccessStatusCode)
{
switch (response.StatusCode)
{
case System.Net.HttpStatusCode.Unauthorized:
throw new Exception("Invalid API key");
case System.Net.HttpStatusCode.TooManyRequests:
throw new Exception("Rate limit exceeded — wait and retry");
case System.Net.HttpStatusCode.PaymentRequired:
throw new Exception("Quota exceeded — upgrade plan");
default:
throw new Exception($"API error: {response.StatusCode}");
}
}
// Second error layer: JSON-level failures
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
&& isError.GetBoolean())
{
var messages = new List<string>();
if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
{
foreach (var e in errors.EnumerateArray())
messages.Add(e.GetString() ?? "Unknown error");
}
throw new Exception(string.Join("; ", messages));
}
// Third layer: per-page exit codes
if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
{
foreach (var result in results.EnumerateArray())
{
if (result.TryGetProperty("FileParseExitCode", out var exitCode)
&& exitCode.GetInt32() != 1)
throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
}
}
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
catch (JsonException ex)
{
throw new Exception($"Invalid API response: {ex.Message}");
}
catch (HttpRequestException ex)
{
throw new Exception($"Network error: {ex.Message}");
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRSpace
Public Async Function SafeExtract(client As HttpClient, apiKey As String, imagePath As String) As Task(Of String)
Try
Dim imageBytes As Byte() = File.ReadAllBytes(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}")
})
Dim response As HttpResponseMessage = Await client.PostAsync("https://api.ocr.space/parse/image", content)
If Not response.IsSuccessStatusCode Then
Select Case response.StatusCode
Case System.Net.HttpStatusCode.Unauthorized
Throw New Exception("Invalid API key")
Case System.Net.HttpStatusCode.TooManyRequests
Throw New Exception("Rate limit exceeded — wait and retry")
Case System.Net.HttpStatusCode.PaymentRequired
Throw New Exception("Quota exceeded — upgrade plan")
Case Else
Throw New Exception($"API error: {response.StatusCode}")
End Select
End If
' Second error layer: JSON-level failures
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
If doc.RootElement.TryGetProperty("IsErroredOnProcessing", ByRef isError) AndAlso isError.GetBoolean() Then
Dim messages As New List(Of String)()
If doc.RootElement.TryGetProperty("ErrorMessage", ByRef errors) Then
For Each e In errors.EnumerateArray()
messages.Add(e.GetString() ?? "Unknown error")
Next
End If
Throw New Exception(String.Join("; ", messages))
End If
' Third layer: per-page exit codes
If doc.RootElement.TryGetProperty("ParsedResults", ByRef results) Then
For Each result In results.EnumerateArray()
If result.TryGetProperty("FileParseExitCode", ByRef exitCode) AndAlso exitCode.GetInt32() <> 1 Then
Throw New Exception($"Page parse failed: exit code {exitCode.GetInt32()}")
End If
Next
End If
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() ?? String.Empty
End Using
Catch ex As JsonException
Throw New Exception($"Invalid API response: {ex.Message}")
Catch ex As HttpRequestException
Throw New Exception($"Network error: {ex.Message}")
End Try
End Function
End Class
O IronOCR gera exceções padrão do .NET com mensagens claras. Sem análise sintática de JSON, sem troca de códigos de status HTTP, sem extração de erros em camadas:
// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException)
{
throw; // File not found — straightforward
}
catch (Exception ex) when (ex.Message.Contains("corrupt"))
{
throw new Exception($"Image file is corrupt: {imagePath}");
}
// Não JSON errors. Não HTTP errors. Não rate limit errors.
}
// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException)
{
throw; // File not found — straightforward
}
catch (Exception ex) when (ex.Message.Contains("corrupt"))
{
throw new Exception($"Image file is corrupt: {imagePath}");
}
// Não JSON errors. Não HTTP errors. Não rate limit errors.
}
Imports IronOcr
Public Function SafeExtract(imagePath As String) As String
Try
Dim result = New IronTesseract().Read(imagePath)
Return result.Text
Catch ex As FileNotFoundException
Throw ' File not found — straightforward
Catch ex As Exception When ex.Message.Contains("corrupt")
Throw New Exception($"Image file is corrupt: {imagePath}")
End Try
' Não JSON errors. Não HTTP errors. Não rate limit errors.
End Function
A referência da API IronTesseract e a referência da API OcrResult documentam a estrutura de resultado tipada, incluindo a propriedade Confidence para avaliar a qualidade da extração.
Referência de Mapeamento de API
| Conceito OCR.space | Equivalente de IronOCR |
|---|---|
POST https://api.ocr.space/parse/image |
new IronTesseract().Read(path) |
FormUrlEncodedContent / MultipartFormDataContent |
OcrInput |
base64Image parâmetro |
input.LoadImage(path) ou input.LoadImage(bytes) |
language parâmetro |
ocr.Language = OcrLanguage.English |
OCREngine parâmetro |
Motor gerenciado internamente |
isOverlayRequired parâmetro |
result.Words / result.Lines (sempre disponível) |
isCreateSearchablePdf parâmetro |
result.SaveAsSearchablePdf(outputPath) |
filetype=PDF parâmetro |
input.LoadPdf(path) |
ParsedResults[0].ParsedText |
result.Text |
ParsedResults[n] (por página) |
result.Pages[n].Text |
IsErroredOnProcessing bandeira JSON |
Exceção padrão do .NET |
FileParseExitCode bandeira por página |
Exceção padrão do .NET |
ProcessingTimeInMilliseconds |
Implícito — nenhuma análise adicional é necessária. |
| HTTP 429 / limite de taxa | Não aplicável — sem limites de taxa |
POCO OcrResult customizado (definido pelo usuário) |
IronOcr.OcrResult (fornecido por NuGet) |
OcrSpaceException personalizado (definido pelo usuário) |
Tipos de exceção padrão do .NET |
limitador de taxa SemaphoreSlim (construído pelo usuário) |
Não é necessário |
| Chave de API em cada requisição | IronOcr.License.LicenseKey (uma vez na inicialização) |
Quando as equipes consideram migrar do OCR.space para o IronOCR
Atingir o nível gratuito durante o teste de carga
O limite de 500 requisições por dia por IP imposto pelo OCR.space é uma regra rígida, não uma recomendação flexível. As equipes descobrem isso durante testes de carga ou implantações de teste, onde vários desenvolvedores compartilham um endereço IP do escritório. Um pipeline CI/CD compartilhado que executa testes de integração no OCR.space esgotará a cota diária antes do fim do expediente. Nesse ponto, o aplicativo falha não porque o código esteja errado, mas porque um contador de terceiros atingiu um limite arbitrário. A mudança para o processamento local elimina completamente essa categoria de falha — não há cota a ser esgotada porque o processamento é executado em tempo real.
Revisões de Conformidade e Classificação de Dados
As análises de segurança que classificam documentos como PII, PHI ou informações financeiras sensíveis criam um problema binário para o OCR.space: o serviço não possui uma opção de implantação local. Não existe um equivalente documentado ao Acordo de Parceiro Comercial (BAA) para cenários HIPAA, nenhuma estrutura de Acordo de Processamento de Dados que regule claramente as transferências de dados da UE sob o GDPR, e nenhum mecanismo técnico para impedir a transmissão de documentos, mesmo com controles contratuais em vigor. As equipes que recebem notificações de não conformidade relacionadas ao processamento de documentos transmitidos pela nuvem precisam de uma solução local. O IronOCR atende a esse requisito por princípio — os documentos nunca saem do servidor de aplicativos. A página de licenciamento documenta a estrutura da licença perpétua, o que também simplifica a documentação de conformidade, eliminando a revisão do fornecedor de nuvem do escopo.
Ônus de manutenção do código de integração
As integrações do OCR.space acumulam dívida técnica proporcionalmente aos seus requisitos de funcionalidade. O cliente HTTP inicial é gerenciável. Em seguida, a equipe adiciona a lógica de repetição. Em seguida, adicionam o rastreamento de limite de taxa por IP, porque vários serviços compartilham um endereço. Em seguida, adicionam uma etapa de pré-validação do tamanho do arquivo. Depois, descobrem que a estrutura de erro JSON difere entre as respostas do Motor 1 e do Motor 2 e adicionam uma ramificação. Seis meses depois, a integração com o OCR.space se transformou em 400 linhas de código de infraestrutura que todo novo membro da equipe precisa entender antes de mexer em qualquer coisa relacionada a OCR. Quando essa equipe avalia o custo de manutenção em relação a uma licença única de IronOCR$999, a aritmética é direta.
Requisitos de saída estruturada
OCR.space retorna texto simples e nada mais. Não há coordenadas de palavras, limites de linha, segmentação de parágrafos nem pontuações de confiança por palavra na resposta. Aplicações que precisam extrair campos específicos de faturas — nome do fornecedor em uma região conhecida, valor total no canto inferior direito — não possuem uma base estruturada para se basear a partir da saída do OCR.space. O IronOCR expõe uma hierarquia completa do documento com coordenadas e valores de confiança em todos os níveis de granularidade: páginas, parágrafos, linhas e palavras individuais. O processamento baseado em regiões permite direcionar zonas específicas do documento sem a necessidade de pós-processamento de toda a página. O guia prático de leitura de resultados e o guia de OCR baseado em regiões abordam esses padrões em detalhes.
Crescimento de volume além do nível gratuito
Com 25.000 solicitações por mês, o plano gratuito é funcional para cenários de baixo volume. Além disso, as camadas pagas se aplicam — contate OCR.space para preços atuais. Esses custos se acumulam ao longo do tempo até que a comparação seja feita com uma licença perpétua $999 sem cobrança por solicitação. Equipes que projetam um crescimento no volume de documentos superior a 25.000 por mês têm uma justificativa de custo simples para o processamento local.
Considerações Comuns de Migração
Substituindo o cliente HTTP por uma chamada de método
A migração do OCR.space para o IronOCR é arquiteturalmente simples, pois ambos retornam texto dos documentos. O cliente HTTP, o analisador JSON, o limitador de taxa e os tipos de exceção personalizados desaparecem. O que os substitui é um único pacote NuGet e chamadas de método. O antes e o depois no limite de serviço:
// Before: OCR.space service (simplified — actual implementation is200+lines)
public class OcrSpaceService : IDisposable
{
private readonly HttpClient _client = new();
private readonly string _apiKey;
public OcrSpaceService(string apiKey) => _apiKey = apiKey;
public async Task<string> ProcessDocument(string path)
{
byte[] bytes = File.ReadAllBytes(path);
string base64 = Convert.ToBase64String(bytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
});
var response = await _client.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
}
public void Dispose() => _client.Dispose();
}
// After:IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
private readonly IronTesseract _ocr = new();
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text;
}
// Não Dispose — no external connections to close
}
// Before: OCR.space service (simplified — actual implementation is200+lines)
public class OcrSpaceService : IDisposable
{
private readonly HttpClient _client = new();
private readonly string _apiKey;
public OcrSpaceService(string apiKey) => _apiKey = apiKey;
public async Task<string> ProcessDocument(string path)
{
byte[] bytes = File.ReadAllBytes(path);
string base64 = Convert.ToBase64String(bytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
});
var response = await _client.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
}
public void Dispose() => _client.Dispose();
}
// After:IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
private readonly IronTesseract _ocr = new();
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text;
}
// Não Dispose — no external connections to close
}
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.Collections.Generic
Imports System.Text.Json
Public Class OcrSpaceService
Implements IDisposable
Private ReadOnly _client As New HttpClient()
Private ReadOnly _apiKey As String
Public Sub New(apiKey As String)
_apiKey = apiKey
End Sub
Public Async Function ProcessDocument(path As String) As Task(Of String)
Dim bytes As Byte() = File.ReadAllBytes(path)
Dim base64 As String = Convert.ToBase64String(bytes)
Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}")
})
Dim response As HttpResponseMessage = Await _client.PostAsync("https://api.ocr.space/parse/image", content)
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
Return doc.RootElement.GetProperty("ParsedResults")(0).GetProperty("ParsedText").GetString()
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_client.Dispose()
End Sub
End Class
Public Class IronOcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function ProcessDocument(path As String) As String
Return _ocr.Read(path).Text
End Function
End Class
A implementação de IDisposable desaparece porque não há nada de HttpClient para gerenciar.
Remoção da chave de API
O OCR.space exige uma chave de API em cada requisição HTTP. Essa chave deve ser armazenada com segurança, rotacionada quando exposta e excluída do controle de versão. O IronOCR utiliza uma chave de licença definida uma única vez na inicialização do aplicativo, geralmente a partir de uma variável de ambiente ou sistema de configuração:
// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
Após a migração, o processo de rotação de chaves de API, a configuração de gerenciamento de segredos e a lógica de injeção de chave por requisição tornam-se desnecessários.
Pré-processamento após a migração
O OCR.space realiza processamento no servidor antes de retornar os resultados, mas os desenvolvedores não têm controle sobre o que esse processamento inclui ou não. O IronOCR expõe métodos explícitos de pré-processamento. Se a qualidade do documento for uma preocupação — digitalizações distorcidas, fotocópias com baixo contraste, fax com ruído — o pipeline de pré-processamento consiste em três a cinco chamadas de método:
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);
Imports IronOcr
Using input As New OcrInput()
input.LoadImage("low-quality-scan.jpg")
input.Deskew()
input.DeNoise()
input.Contrast()
input.Binarize()
input.EnhanceResolution(300)
Dim result = New IronTesseract().Read(input)
End Using
O guia de correção da qualidade da imagem e o guia de correção da cor da imagem documentam todos os filtros disponíveis, com exemplos que mostram as diferenças de precisão antes e depois da aplicação dos filtros.
Assíncrono vs. Síncrono
As chamadas OCR.space são inerentemente assíncronas porque envolvem requisições HTTP de ida e volta. As chamadas do IronOCR são síncronas por padrão, o que simplifica o código em contextos que não sejam da web. Para ASP.NET e cenários de servidor que exigem execução não bloqueadora,IronOCR suporta operação assíncrona via encapsulamento Task.Run ou API assíncrona direta. O guia de OCR assíncrono aborda os padrões. Remover await de um método de serviço que anteriormente esperava por uma chamada na nuvem simplifica a pilha de chamadas em contextos onde assíncrono foi adotado apenas porque OCR.space o exigia.
Funcionalidades adicionais do IronOCR
Funcionalidades não abordadas nas seções anteriores, cada uma representando uma funcionalidade sem equivalente no OCR.space:
- Exportação hOCR:
result.SaveAsHocrFile("output.hocr")produz saída hOCR compatível com padrões para pipelines de processamento de documentos posteriores. - Acompanhamento do progresso : Inscreva-se em eventos de progresso para operações em lote de longa duração e com várias páginas, habilitando barras de progresso e cálculos de tempo estimado de conclusão (ETA) em aplicativos de interface do usuário.
- Extração de tabelas : Os dados estruturados da tabela são acessíveis por meio do modelo de resultados, oferecendo suporte a casos de uso como a extração de itens de linha de faturas que exigem alinhamento de colunas.
- Leitura de documentos especializada: Zonas MRZ de passaportes, placas de automóveis, linhas de cheque MICR e reconhecimento de escrita são capacidades desenvolvidas especificamente acessíveis através da mesma API
IronTesseract.
Compatibilidade com .NET e Preparação para o Futuro
O IronOCR é compatível com .NET 6, .NET 7, .NET 8 e .NET 9, com suporte ativo para cada versão LTS e STS. A biblioteca funciona em Windows x64, Windows x86,Linuxx64, macOS, Docker, AWS Lambda e Azure App Service — tudo a partir do mesmo pacote NuGet , sem necessidade de configuração específica para cada plataforma. O OCR.space exige HTTPS de saída em todos os ambientes de implantação, o que exclui redes isoladas (air-gapped), proxies corporativos restritivos e infraestrutura onde o tráfego de saída para APIs de terceiros é bloqueado por política. O IronOCR não possui requisitos de rede em tempo de execução; Ele é executado inteiramente dentro do processo. Com o lançamento do .NET 10 previsto para o final de 2026, o histórico do IronOCR em manter a compatibilidade entre as principais versões do .NET garante a continuidade sem alterações de integração.
Conclusão
O OCR.space ocupa um nicho real e útil: desenvolvedores que precisam prototipar um recurso de OCR em um fim de semana, estudantes explorando conceitos de extração de texto ou ferramentas pessoais de baixo volume (menos de 25.000 documentos por mês) sem requisitos de conformidade. Para esse público, a versão gratuita oferece um valor real.
O problema é que os desenvolvedores .NET normalmente não estão criando protótipos de fim de semana. Eles estão criando sistemas de faturamento, processadores de registros médicos, fluxos de arquivamento de documentos e aplicativos de negócios que exigem conformidade regulatória. Nesses contextos, a ausência do pacote NuGet do OCR.space não é um inconveniente — é uma incompatibilidade estrutural. Cada hora gasta construindo o cliente HTTP, o limitador de taxa, o analisador JSON e a infraestrutura de repetição é uma hora que não é dedicada aos requisitos reais da aplicação. Esse custo é concentrado no início, mas o custo de manutenção continua durante toda a vida útil da integração.
O IronOCR aborda a falha específica que define todas as integrações do OCR.space: a ausência de um SDK real. Um pacote NuGet , uma chamada de método, sem infraestrutura HTTP, sem limites de taxa, sem documentos transmitidos para servidores externos. O preço de entrada de $999 é um custo único; os custos de assinatura recorrentes do OCR.space irão superá-lo ao longo do tempo, especialmente à medida que o volume aumenta. Para equipes com requisitos de conformidade, o processamento local é a única opção independentemente do custo.
A comparação se resume, em última análise, a uma única pergunta: o aplicativo requer OCR como uma dependência REST externa ou como uma função de biblioteca? Para aplicações .NET de produção, a resposta é quase sempre a segunda opção.
Perguntas frequentes
O que é a API OCR.space?
A API OCR.space é uma solução de OCR usada por desenvolvedores e empresas para extrair texto de imagens e documentos. É uma das várias opções de OCR avaliadas juntamente com o IronOCR para desenvolvimento de aplicações .NET.
Como o IronOCR se compara à API OCR.space para desenvolvedores .NET?
IronOCR é uma biblioteca OCR .NET nativa do NuGet que utiliza o IronTesseract como mecanismo principal. Comparada à API do OCR.space, oferece implantação mais simples (sem instaladores de SDK), preço fixo e uma API C# limpa, sem interoperabilidade COM ou dependências de nuvem.
O IronOCR é mais fácil de configurar do que a API do OCR.space?
O IronOCR é instalado por meio de um único pacote NuGet. Não há instaladores de SDK, arquivos de licença para copiar, componentes COM para registrar ou binários de tempo de execução separados para gerenciar. Todo o mecanismo de OCR está incluído no pacote.
Quais são as diferenças de precisão entre a API do OCR.space e o IronOCR?
O IronOCR alcança alta precisão de reconhecimento para documentos comerciais padrão, faturas, recibos e formulários digitalizados. Para documentos muito degradados ou com escritas incomuns, a precisão varia de acordo com a qualidade da fonte. O IronOCR inclui filtros de pré-processamento de imagem para melhorar o reconhecimento em entradas de baixa qualidade.
O IronOCR suporta extração de texto de PDFs?
Sim. O IronOCR extrai texto tanto de PDFs nativos quanto de imagens digitalizadas de PDFs em uma única chamada. Ele também suporta arquivos TIFF com várias páginas, imagens e fluxos de dados. Para PDFs digitalizados, o OCR é aplicado página por página, com objetos de resultado por página.
Como se compara o licenciamento da API do OCR.space ao do IronOCR?
O IronOCR utiliza uma licença perpétua com preço fixo, sem cobranças por página ou por digitalização. Organizações que processam grandes volumes de documentos pagam o mesmo valor de licença, independentemente do volume. Detalhes e preços por volume estão disponíveis na página de licenciamento do IronOCR.
Quais idiomas o IronOCR suporta?
O IronOCR suporta 127 idiomas através de pacotes de idiomas NuGet separados. Adicionar um idioma requer um único comando 'dotnet add package IronOcr.Languages.{Idioma}'. Não é necessário inserir arquivos manualmente nem configurar caminhos.
Como faço para instalar o IronOCR em um projeto .NET ?
Instale via NuGet: 'Install-Package IronOcr' no Console do Gerenciador de Pacotes ou 'dotnet add package IronOcr' na CLI. Pacotes de idiomas adicionais são instalados da mesma forma. Não é necessário instalar o SDK nativo.
O IronOCR é adequado para implantações em Docker e contêineres, ao contrário do OCR.space?
Sim. O IronOCR funciona em contêineres Docker por meio de seu pacote NuGet. A chave de licença é definida por meio de uma variável de ambiente. Não são necessários arquivos de licença, caminhos de SDK ou montagens de volume para o próprio mecanismo de OCR.
Posso experimentar o IronOCR antes de comprar, em comparação com o OCR.space?
Sim. O modo de avaliação do IronOCR processa documentos e retorna resultados de OCR com uma marca d'água sobreposta. Você pode verificar a precisão em seus próprios documentos antes de adquirir uma licença.
O IronOCR suporta leitura de código de barras juntamente com extração de texto?
O IronOCR concentra-se na extração de texto e OCR. Para leitura de código de barras, a Iron Software fornece o IronBarcode como uma biblioteca complementar. Ambos estão disponíveis individualmente ou como parte do pacote Iron Suite.
É fácil migrar da API do OCR.space para o IronOCR?
A migração da API OCR.space para o IronOCR normalmente envolve a substituição das sequências de inicialização pela instanciação do IronTesseract, a remoção do gerenciamento do ciclo de vida COM e a atualização das chamadas da API. A maioria das migrações reduz significativamente a complexidade do código.

