Files
ctbjrj/成套报价软件/扒图/PdfiumNative.cs
T

229 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 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);
}
}
}