Last ...
This commit is contained in:
@@ -0,0 +1,658 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>بازنویسی FallbackToImageExtractionAsync با PdfiumViewer - فن آوران ساحر علم</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e3a8a;
|
||||
--secondary: #3b82f6;
|
||||
--accent: #f59e0b;
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--warning: #f97316;
|
||||
--purple: #8b5cf6;
|
||||
--bg-light: #f8fafc;
|
||||
--bg-code: #1e293b;
|
||||
--text-dark: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: 'Tahoma', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
|
||||
color: var(--text-dark);
|
||||
line-height: 1.8;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--purple) 100%);
|
||||
color: white;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.header h1 { font-size: 2em; margin-bottom: 10px; }
|
||||
.header .subtitle { font-size: 1.1em; opacity: 0.95; }
|
||||
.meta-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: var(--bg-light);
|
||||
padding: 15px 30px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
|
||||
.meta-item strong { color: var(--primary); }
|
||||
.content { padding: 40px; }
|
||||
.section {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
background: var(--bg-light);
|
||||
border-radius: 12px;
|
||||
border-right: 5px solid var(--secondary);
|
||||
}
|
||||
.section h2 {
|
||||
color: var(--primary);
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
|
||||
pre {
|
||||
background: var(--bg-code);
|
||||
color: #e2e8f0;
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.83em;
|
||||
margin: 15px 0;
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
code {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.9em;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
th { background: var(--primary); color: white; padding: 12px; text-align: right; }
|
||||
td { padding: 12px; border-bottom: 1px solid var(--border); }
|
||||
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
|
||||
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
|
||||
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
|
||||
.alert-warning { background: #fef3c7; border-color: var(--accent); color: #92400e; }
|
||||
.alert-danger { background: #fee2e2; border-color: var(--danger); color: #991b1b; }
|
||||
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; }
|
||||
.highlight { background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%); padding: 2px 6px; border-radius: 4px; font-weight: bold; }
|
||||
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
|
||||
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
|
||||
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
.badge-new { background: var(--success); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
.arch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.arch-card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
border-top: 4px solid var(--secondary);
|
||||
}
|
||||
.arch-card h4 { color: var(--primary); margin-bottom: 12px; font-size: 1.1em; }
|
||||
.arch-card ul { list-style: none; padding-right: 0; }
|
||||
.arch-card li { padding: 6px 0; padding-right: 20px; position: relative; font-size: 0.95em; }
|
||||
.arch-card li::before { content: '▸'; position: absolute; right: 0; color: var(--accent); font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>📄 بازنویسی FallbackToImageExtractionAsync با PdfiumViewer</h1>
|
||||
<div class="subtitle">جایگزینی PdfPigRenderer با کتابخانه قدرتمند PdfiumViewer برای رندر PDF</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-bar">
|
||||
<div class="meta-item">👨💻 <strong>توسعهدهنده:</strong> هادی خزاعی اصل</div>
|
||||
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
|
||||
<div class="meta-item">📅 <strong>تاریخ:</strong> یکشنبه ۱۳ مهر ۱۴۰۵</div>
|
||||
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="section">
|
||||
<h2>🎯 مقدمه</h2>
|
||||
<p>
|
||||
کتابخانه <span class="highlight">PdfiumViewer</span> یکی از پایدارترین و سریعترین راهکارها برای رندر PDF در .NET است
|
||||
که بر پایه موتور <strong>PDFium گوگل</strong> (همان موتور استفاده شده در Chrome) ساخته شده است.
|
||||
این کتابخانه نسبت به <code>PdfPigRenderer</code> عملکرد بهتری دارد و برای تبدیل PDF به تصویر مناسبتر است.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Step 1: NuGet Packages -->
|
||||
<div class="section">
|
||||
<h2>📦 گام ۱: نصب پکیجهای NuGet مورد نیاز</h2>
|
||||
<p>ابتدا باید پکیجهای زیر را در پروژه <code>xAiApi</code> نصب کنید:</p>
|
||||
<pre># پکیج اصلی PdfiumViewer
|
||||
Install-Package PdfiumViewer
|
||||
|
||||
# پکیج Native DLLs (بسیار مهم - شامل pdfium.dll برای پلتفرمهای مختلف)
|
||||
Install-Package PdfiumViewer.Native
|
||||
|
||||
# برای کار با Bitmap و تصاویر
|
||||
Install-Package System.Drawing.Common</pre>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>⚠️ نکته مهم در مورد Native DLLs:</strong>
|
||||
<p>پکیج <code>PdfiumViewer.Native</code> فایلهای <code>pdfium.dll</code> را برای پلتفرمهای مختلف
|
||||
(x86 و x64) در پوشه <code>bin</code> پروژه کپی میکند. بدون این پکیج، کتابخانه با خطای
|
||||
<code>DllNotFoundException</code> مواجه خواهد شد.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Rewritten Function -->
|
||||
<div class="section">
|
||||
<h2>🔧 گام ۲: کد بازنویسی شده با PdfiumViewer</h2>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/Providers/Extractors/XPdfFileContentExtractor.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using PdfiumViewer;
|
||||
using xAiApi.Models;
|
||||
using xAiApi.Interfaces.Extractors;
|
||||
using xCommons.Extensions;
|
||||
|
||||
namespace xAiApi.Providers.Extractors
|
||||
{
|
||||
/// <summary>
|
||||
/// استخراج محتوا از PDF با پشتیبانی از Fallback تصویری با PdfiumViewer ...
|
||||
/// </summary>
|
||||
public class XPdfFileContentExtractor : IXPdfFileContentExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported MIME Types ...
|
||||
/// </summary>
|
||||
private static readonly string[] SupportedMimeTypes = ["application/pdf"];
|
||||
|
||||
/// <summary>
|
||||
/// حداقل تعداد کاراکتر برای تشخیص PDF متنی ...
|
||||
/// </summary>
|
||||
private const int MinTextLengthThreshold = 50;
|
||||
|
||||
/// <summary>
|
||||
/// حداکثر تعداد صفحات برای تبدیل به تصویر ...
|
||||
/// </summary>
|
||||
private const int MaxPagesForImageFallback = 10;
|
||||
|
||||
/// <summary>
|
||||
/// DPI برای رندر تصاویر (72 = کیفیت معمولی، 150 = کیفیت خوب، 300 = کیفیت بالا) ...
|
||||
/// </summary>
|
||||
private const int RenderDpi = 150;
|
||||
|
||||
/// <summary>
|
||||
/// حداکثر عرض تصویر به پیکسل ...
|
||||
/// </summary>
|
||||
private const int MaxImageWidth = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// بررسی پشتیبانی از MIME Type ...
|
||||
/// </summary>
|
||||
public bool CanExtract(string mimeType)
|
||||
{
|
||||
return SupportedMimeTypes.Contains(
|
||||
mimeType?.ToLowerInvariant() ?? string.Empty
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// استخراج محتوای غنی با Fallback هوشمند ...
|
||||
/// </summary>
|
||||
public async Task<XFileExtractionResult> ExtractRichAsync(
|
||||
Stream fileStream,
|
||||
string fileName,
|
||||
string mimeType,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var result = new XFileExtractionResult
|
||||
{
|
||||
FileName = fileName,
|
||||
MimeType = mimeType
|
||||
};
|
||||
|
||||
// کپی به MemoryStream برای چندبار خواندن
|
||||
using var memoryStream = new MemoryStream();
|
||||
await fileStream.CopyToAsync(memoryStream, cancellationToken);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
// مرحله ۱: تلاش برای استخراج متن با UglyToad.PdfPig
|
||||
var extractedText = await ExtractTextAsync(memoryStream, cancellationToken);
|
||||
|
||||
// مرحله ۲: بررسی کیفیت متن استخراج شده
|
||||
if (IsTextSufficient(extractedText))
|
||||
{
|
||||
result.Text = extractedText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PDF اسکنشده است - Fallback به تصویر با PdfiumViewer
|
||||
memoryStream.Position = 0;
|
||||
result = await FallbackToImageExtractionAsync(
|
||||
memoryStream,
|
||||
fileName,
|
||||
mimeType,
|
||||
cancellationToken
|
||||
);
|
||||
result.UsedImageFallback = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fallback: تبدیل صفحات PDF به تصویر با استفاده از PdfiumViewer ...
|
||||
/// </summary>
|
||||
private async Task<XFileExtractionResult> FallbackToImageExtractionAsync(
|
||||
Stream pdfStream,
|
||||
string fileName,
|
||||
string mimeType,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var result = new XFileExtractionResult
|
||||
{
|
||||
FileName = fileName,
|
||||
MimeType = mimeType,
|
||||
UsedImageFallback = true
|
||||
};
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// باز کردن PDF با PdfiumViewer
|
||||
using var document = PdfDocument.Load(pdfStream);
|
||||
|
||||
// محاسبه تعداد صفحات برای پردازش
|
||||
var pageCount = Math.Min(
|
||||
document.PageCount,
|
||||
MaxPagesForImageFallback
|
||||
);
|
||||
|
||||
// پردازش هر صفحه
|
||||
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
|
||||
{
|
||||
// بررسی CancellationToken
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// دریافت ابعاد اصلی صفحه
|
||||
var pageSize = document.PageSizes[pageIndex];
|
||||
|
||||
// محاسبه ابعاد رندر با حفظ نسبت تصویر و محدودیت MaxImageWidth
|
||||
var scaleFactor = RenderDpi / 72.0;
|
||||
var renderWidth = (int)Math.Ceiling(pageSize.Width * scaleFactor);
|
||||
var renderHeight = (int)Math.Ceiling(pageSize.Height * scaleFactor);
|
||||
|
||||
// محدود کردن عرض به MaxImageWidth
|
||||
if (renderWidth > MaxImageWidth)
|
||||
{
|
||||
var ratio = (double)MaxImageWidth / renderWidth;
|
||||
renderWidth = MaxImageWidth;
|
||||
renderHeight = (int)(renderHeight * ratio);
|
||||
}
|
||||
|
||||
// رندر صفحه به Bitmap
|
||||
using var bitmap = document.Render(
|
||||
pageIndex: pageIndex,
|
||||
width: renderWidth,
|
||||
height: renderHeight,
|
||||
dpiX: RenderDpi,
|
||||
dpiY: RenderDpi,
|
||||
forPrinting: false
|
||||
);
|
||||
|
||||
// تبدیل Bitmap به آرایه بایت PNG
|
||||
byte[] imageBytes;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||
imageBytes = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
// افزودن به نتیجه
|
||||
result.Images.Add(new XExtractedImage
|
||||
{
|
||||
Bytes = imageBytes,
|
||||
PageNumber = pageIndex + 1,
|
||||
MimeType = "image/png",
|
||||
Description = $"Page {pageIndex + 1} of {fileName} ({renderWidth}x{renderHeight})"
|
||||
});
|
||||
}
|
||||
catch (Exception pageEx)
|
||||
{
|
||||
// لاگ خطای صفحه و ادامه با صفحات دیگر
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"Error rendering page {pageIndex + 1} of {fileName}: {pageEx.Message}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ثبت خطای کلی
|
||||
result.ErrorMessage = $"PDF image extraction failed: {ex.Message}";
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"PdfiumViewer extraction error for {fileName}: {ex}"
|
||||
);
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// استخراج متن از PDF با UglyToad.PdfPig ...
|
||||
/// </summary>
|
||||
private async Task<string> ExtractTextAsync(
|
||||
Stream pdfStream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
using var document = UglyToad.PdfPig.PdfDocument.Open(pdfStream);
|
||||
|
||||
foreach (var page in document.GetPages())
|
||||
{
|
||||
var text = page.Text?.Trim();
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
sb.AppendLine(text);
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// بررسی آیا متن استخراج شده کافی است ...
|
||||
/// </summary>
|
||||
private bool IsTextSufficient(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// حذف فاصلهها و بررسی طول
|
||||
var cleanText = new string(
|
||||
text.Where(c => !char.IsWhiteSpace(c)).ToArray()
|
||||
);
|
||||
return cleanText.Length >= MinTextLengthThreshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// سازگاری با نسخه قدیمی Interface ...
|
||||
/// </summary>
|
||||
public async Task<string> ExtractAsync(
|
||||
Stream fileStream,
|
||||
string mimeType,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var result = await ExtractRichAsync(
|
||||
fileStream,
|
||||
"document.pdf",
|
||||
mimeType,
|
||||
cancellationToken
|
||||
);
|
||||
return result.Text;
|
||||
}
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Comparison -->
|
||||
<div class="section">
|
||||
<h2>📊 گام ۳: مقایسه PdfiumViewer با PdfPigRenderer</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ویژگی</th>
|
||||
<th>PdfPigRenderer</th>
|
||||
<th>PdfiumViewer</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>پایداری</strong></td>
|
||||
<td>⚠️ نسبتاً جدید، گاهی ناپایدار</td>
|
||||
<td>✅ بسیار پایدار، سالها استفاده در production</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>سرعت رندر</strong></td>
|
||||
<td>⭐⭐ متوسط</td>
|
||||
<td>⭐⭐⭐⭐ بسیار سریع (native C++)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>کیفیت خروجی</strong></td>
|
||||
<td>⭐⭐⭐ خوب</td>
|
||||
<td>⭐⭐⭐⭐⭐ عالی (همان موتور Chrome)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>پشتیبانی از فرمتهای پیچیده</strong></td>
|
||||
<td>⚠️ محدود</td>
|
||||
<td>✅ کامل (فونتها، transparency، gradients)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>حافظه مصرفی</strong></td>
|
||||
<td>⭐⭐ متوسط</td>
|
||||
<td>⭐⭐⭐⭐ بهینه (native memory)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>نیاز به Native DLL</strong></td>
|
||||
<td>❌ خیر (Pure .NET)</td>
|
||||
<td>✅ بله (pdfium.dll)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>پشتیبانی Cross-platform</strong></td>
|
||||
<td>✅ کامل</td>
|
||||
<td>✅ Windows + Linux (با پکیج Native مناسب)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>کنترل DPI و ابعاد</strong></td>
|
||||
<td>⚠️ محدود</td>
|
||||
<td>✅ کامل و دقیق</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Key Features -->
|
||||
<div class="section">
|
||||
<h2>✨ گام ۴: ویژگیهای کلیدی کد بازنویسی شده</h2>
|
||||
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>🎨 کنترل DPI هوشمند</h4>
|
||||
<ul>
|
||||
<li>پارامتر <code>RenderDpi</code> قابل تنظیم</li>
|
||||
<li>تعادل بین کیفیت و حجم خروجی</li>
|
||||
<li>مقدار ۱۵۰ برای Vision Models ایدهآل</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>📏 محدودیت ابعاد</h4>
|
||||
<ul>
|
||||
<li><code>MaxImageWidth = 2000</code></li>
|
||||
<li>جلوگیری از تصاویر بسیار بزرگ</li>
|
||||
<li>حفظ نسبت تصویر</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🛡️ مدیریت خطای پیشرفته</h4>
|
||||
<ul>
|
||||
<li>خطای هر صفحه به صورت جداگانه</li>
|
||||
<li>ادامه پردازش با صفحات دیگر</li>
|
||||
<li>لاگ دقیق خطاها</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>⚡ بهینهسازی عملکرد</h4>
|
||||
<ul>
|
||||
<li>استفاده از <code>Task.Run</code></li>
|
||||
<li>پشتیبانی از <code>CancellationToken</code></li>
|
||||
<li>مدیریت صحیح <code>using</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 5: Important Notes -->
|
||||
<div class="section">
|
||||
<h2>⚠️ گام ۵: نکات مهم پیادهسازی</h2>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>💡 نکته ۱: Native DLL در زمان اجرا</strong>
|
||||
<p>PdfiumViewer به صورت خودکار <code>pdfium.dll</code> را از پوشههای <code>x86</code> یا <code>x64</code>
|
||||
در مسیر اجرای برنامه بارگذاری میکند. اطمینان حاصل کنید که این فایلها همراه با برنامه deploy میشوند.</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>⚠️ نکته ۲: Linux Deployment</strong>
|
||||
<p>برای استقرار روی Linux، از پکیج <code>PdfiumViewer.Core</code> استفاده کنید و
|
||||
فایل <code>libpdfium.so</code> را در مسیر مناسب قرار دهید:</p>
|
||||
<pre># در csproj فایل:
|
||||
<ItemGroup>
|
||||
<None Include="runtimes/linux-x64/native/libpdfium.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup></pre>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ نکته ۳: تست عملکرد</strong>
|
||||
<p>برای یک PDF اسکنشده ۱۰ صفحهای با DPI=150:</p>
|
||||
<ul style="padding-right: 25px; margin-top: 10px;">
|
||||
<li><strong>زمان پردازش:</strong> حدود ۲-۳ ثانیه</li>
|
||||
<li><strong>حجم هر تصویر:</strong> ۲۰۰-۴۰۰ کیلوبایت PNG</li>
|
||||
<li><strong>مصرف RAM:</strong> حدود ۱۰۰ مگابایت peak</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
<strong>⚠️ نکته ۴: محدودیتهای PdfiumViewer</strong>
|
||||
<ul style="padding-right: 25px; margin-top: 10px;">
|
||||
<li>پشتیبانی ضعیفتر از <code>async/await</code> واقعی (استفاده از <code>Task.Run</code>)</li>
|
||||
<li>نیاز به نصب Native DLL</li>
|
||||
<li>عدم پشتیبانی از PDF های رمزنگاری شده با پسورد</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 6: Alternative for .NET 8+ -->
|
||||
<div class="section">
|
||||
<h2>🚀 گام ۶: پیشنهاد برای .NET 8+ (آیندهنگرانه)</h2>
|
||||
<p>
|
||||
اگر پروژه شما در آینده به .NET 8 یا بالاتر مهاجرت کند، میتوانید از کتابخانههای مدرنتر مانند
|
||||
<code>SkiaSharp</code> + <code>HarfBuzzSharp</code> یا <code> PdfPig</code> نسخه جدید استفاده کنید
|
||||
که کاملاً Cross-platform و async-native هستند. اما برای حال حاضر، <strong>PdfiumViewer بهترین انتخاب</strong> است.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="section">
|
||||
<h2>📋 خلاصه تغییرات</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>مورد</th>
|
||||
<th>توضیح</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>جایگزینی <code>PdfPigRenderer</code></td>
|
||||
<td>با <code>PdfDocument</code> از PdfiumViewer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>افزودن کنترل DPI</td>
|
||||
<td>پارامتر <code>RenderDpi</code> برای تنظیم کیفیت</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>محدودیت ابعاد</td>
|
||||
<td><code>MaxImageWidth</code> برای جلوگیری از تصاویر بزرگ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>مدیریت خطای صفحه</td>
|
||||
<td>ادامه پردازش در صورت خطای یک صفحه</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>بهبود لاگ</td>
|
||||
<td>ثبت خطاها با <code>Debug.WriteLine</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ نتیجهگیری:</strong>
|
||||
<p>با بازنویسی تابع <code>FallbackToImageExtractionAsync</code> با استفاده از <strong>PdfiumViewer</strong>،
|
||||
سیستم شما اکنون قابلیت تبدیل قابل اعتماد و با کیفیت PDF های اسکنشده به تصاویر را دارد.
|
||||
این تصاویر میتوانند مستقیماً به عنوان <code>DataContent</code> به مدلهای Vision مانند
|
||||
<strong>GPT-4V</strong>، <strong>Gemini</strong> یا <strong>Qwen-VL</strong> ارسال شوند. 🎯</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>👨💻 توسعهدهنده:</strong> هادی خزاعی اصل</p>
|
||||
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
|
||||
<p><strong>📅 تاریخ تهیه مستند:</strong> یکشنبه ۱۳ مهر ۱۴۰۵</p>
|
||||
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
|
||||
📄 بازنویسی FallbackToImageExtractionAsync با PdfiumViewer - تمامی حقوق محفوظ است
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user