基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
// ============================================================
|
||||
// PdfiumNative:pdfium.dll 原生库 P/Invoke 封装
|
||||
// dll 来源:pypdfium2 win32 wheel(pypdfium2_raw/pdfium.dll,x86)
|
||||
// 调用约定:cdecl(pypdfium2 ctypes.CDLL 已验证)
|
||||
// ============================================================
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace 成套报价软件
|
||||
{
|
||||
/// <summary>pdfium C API 最小封装(仅本项目用到的函数)。</summary>
|
||||
internal static class PdfiumNative
|
||||
{
|
||||
public const string DllName = "pdfium.dll";
|
||||
|
||||
// 渲染标志:渲染注释/批注
|
||||
public const int FPDF_ANNOT = 0x01;
|
||||
|
||||
// FPDF_ERR_* 错误码(GetLastError 返回)
|
||||
public const int FPDF_ERR_SUCCESS = 0; // 无错误
|
||||
public const int FPDF_ERR_UNKNOWN = 1; // 未知错误
|
||||
public const int FPDF_ERR_FILE = 2; // 文件无法打开或读取
|
||||
public const int FPDF_ERR_FORMAT = 3; // 文件不是 PDF 或已损坏
|
||||
public const int FPDF_ERR_PASSWORD = 4; // 需要密码或密码错误
|
||||
public const int FPDF_ERR_SECURITY = 5; // 不支持的安全方案
|
||||
public const int FPDF_ERR_PAGE = 6; // 页面无法加载
|
||||
|
||||
private static bool _inited;
|
||||
private static readonly object _initLock = new object();
|
||||
|
||||
/// <summary>初始化 pdfium(进程内只需一次,线程安全)。</summary>
|
||||
public static void InitLibrary()
|
||||
{
|
||||
lock (_initLock)
|
||||
{
|
||||
if (_inited) return;
|
||||
FPDF_InitLibrary();
|
||||
_inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDF_InitLibrary();
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDF_DestroyLibrary();
|
||||
|
||||
[DllImport(DllName, CharSet = CharSet.Ansi)]
|
||||
private static extern IntPtr FPDF_LoadDocument(string file_path, string password);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDF_CloseDocument(IntPtr document);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern uint FPDF_GetLastError();
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern int FPDF_GetPageCount(IntPtr document);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern IntPtr FPDF_LoadPage(IntPtr document, int page_index);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDF_ClosePage(IntPtr page);
|
||||
|
||||
// ★ 新版 GetPageSizeByIndexF 是 FS_SIZEF* 参数(易错);用稳定的旧版 double API
|
||||
[DllImport(DllName)]
|
||||
private static extern int FPDF_GetPageSizeByIndex(IntPtr document, int page_index,
|
||||
out double width, out double height);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern IntPtr FPDFBitmap_Create(int width, int height, int alpha);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDFBitmap_FillRect(IntPtr bitmap, int left, int top,
|
||||
int width, int height, uint color);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDFBitmap_Destroy(IntPtr bitmap);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern IntPtr FPDFBitmap_GetBuffer(IntPtr bitmap);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern int FPDFBitmap_GetStride(IntPtr bitmap);
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page,
|
||||
int start_x, int start_y, int size_x, int size_y,
|
||||
int rotate, int flags);
|
||||
|
||||
|
||||
[DllImport(DllName)]
|
||||
private static extern IntPtr FPDF_LoadMemDocument(byte[] data, int size, string password);
|
||||
|
||||
public static IntPtr LoadDocumentFromBytes(byte[] data, string password)
|
||||
{
|
||||
InitLibrary();
|
||||
if (data == null || data.Length == 0) return IntPtr.Zero;
|
||||
return FPDF_LoadMemDocument(data, data.Length, string.IsNullOrEmpty(password) ? null : password);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 上层直接使用的安全包装
|
||||
// ------------------------------------------------------------
|
||||
|
||||
/// <summary>打开 PDF 文件。失败返回 IntPtr.Zero,可用 LastErrorText 查原因。</summary>
|
||||
public static IntPtr LoadDocument(string path, string password)
|
||||
{
|
||||
InitLibrary();
|
||||
if (string.IsNullOrEmpty(path)) return IntPtr.Zero;
|
||||
return FPDF_LoadDocument(path, string.IsNullOrEmpty(password) ? null : password);
|
||||
}
|
||||
|
||||
public static void CloseDocument(IntPtr doc)
|
||||
{
|
||||
if (doc != IntPtr.Zero) FPDF_CloseDocument(doc);
|
||||
}
|
||||
|
||||
public static int GetPageCount(IntPtr doc)
|
||||
{
|
||||
if (doc == IntPtr.Zero) return 0;
|
||||
return FPDF_GetPageCount(doc);
|
||||
}
|
||||
|
||||
/// <summary>取指定页尺寸(PDF 点,1/72 英寸)。失败返回 false。</summary>
|
||||
public static bool GetPageSize(IntPtr doc, int pageIndex, out float widthPt, out float heightPt)
|
||||
{
|
||||
widthPt = 0; heightPt = 0;
|
||||
if (doc == IntPtr.Zero) return false;
|
||||
double w, h;
|
||||
bool ok = FPDF_GetPageSizeByIndex(doc, pageIndex, out w, out h) != 0;
|
||||
widthPt = (float)w;
|
||||
heightPt = (float)h;
|
||||
return ok;
|
||||
}
|
||||
|
||||
public static IntPtr LoadPage(IntPtr doc, int pageIndex)
|
||||
{
|
||||
if (doc == IntPtr.Zero) return IntPtr.Zero;
|
||||
return FPDF_LoadPage(doc, pageIndex);
|
||||
}
|
||||
|
||||
public static void ClosePage(IntPtr page)
|
||||
{
|
||||
if (page != IntPtr.Zero) FPDF_ClosePage(page);
|
||||
}
|
||||
|
||||
/// <summary>把整页渲染到 GDI+ 位图(白底不透明)。</summary>
|
||||
public static System.Drawing.Bitmap RenderPageToBitmap(IntPtr page, int widthPx, int heightPx)
|
||||
{
|
||||
return RenderRegionToBitmap(page, 0, 0, widthPx, heightPx, widthPx, heightPx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把页面局部区域渲染到位图(白底不透明)。
|
||||
/// 页面整图尺寸 = fullW×fullH 像素;只取 (startX,startY,wPx,hPx) 矩形。
|
||||
/// 用于:OCR 框选区域按任意分辨率高清重渲染(与显示缩放无关)。
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap RenderRegionToBitmap(IntPtr page,
|
||||
int startX, int startY, int wPx, int hPx, int fullW, int fullH)
|
||||
{
|
||||
if (page == IntPtr.Zero || wPx <= 0 || hPx <= 0) return null;
|
||||
if (wPx > 16384 || hPx > 16384) return null;
|
||||
|
||||
IntPtr bmp = FPDFBitmap_Create(wPx, hPx, 1);
|
||||
if (bmp == IntPtr.Zero) return null;
|
||||
try
|
||||
{
|
||||
// 白底不透明(0xAARRGGBB)
|
||||
FPDFBitmap_FillRect(bmp, 0, 0, wPx, hPx, 0xFFFFFFFF);
|
||||
// start_x/start_y 为负偏移:位图原点相对页面原点
|
||||
FPDF_RenderPageBitmap(bmp, page, -startX, -startY, fullW, fullH, 0, FPDF_ANNOT);
|
||||
|
||||
IntPtr buffer = FPDFBitmap_GetBuffer(bmp);
|
||||
int stride = FPDFBitmap_GetStride(bmp);
|
||||
if (buffer == IntPtr.Zero || stride <= 0) return null;
|
||||
|
||||
// pdfium BGRA(top-down) → GDI+ Format32bppArgb(BGRA) 逐行拷贝
|
||||
var result = new System.Drawing.Bitmap(wPx, hPx,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
var rect = new System.Drawing.Rectangle(0, 0, wPx, hPx);
|
||||
var data = result.LockBits(rect,
|
||||
System.Drawing.Imaging.ImageLockMode.WriteOnly,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
try
|
||||
{
|
||||
for (int y = 0; y < hPx; y++)
|
||||
{
|
||||
IntPtr src = new IntPtr(buffer.ToInt64() + (long)y * stride);
|
||||
IntPtr dst = new IntPtr(data.Scan0.ToInt64() + (long)y * data.Stride);
|
||||
NativeMethods.CopyMemory(dst, src, (uint)(wPx * 4));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
result.UnlockBits(data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
FPDFBitmap_Destroy(bmp);
|
||||
}
|
||||
}
|
||||
|
||||
public static string LastErrorText()
|
||||
{
|
||||
uint err = FPDF_GetLastError();
|
||||
switch (err)
|
||||
{
|
||||
case FPDF_ERR_SUCCESS: return "成功";
|
||||
case FPDF_ERR_FILE: return "文件无法打开或读取";
|
||||
case FPDF_ERR_FORMAT: return "文件不是有效 PDF 或已损坏";
|
||||
case FPDF_ERR_PASSWORD: return "PDF 已加密,需要密码";
|
||||
case FPDF_ERR_SECURITY: return "不支持的安全方案";
|
||||
case FPDF_ERR_PAGE: return "页面内容无法解析";
|
||||
default: return "未知错误(" + err + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private static class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
|
||||
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user