日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档

發布時間:2024/8/24 C# 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 C# 中使用 P/Invoke 調用 Mupdf 函數庫顯示 PDF 文檔

一直以來,我都想為 PDF 補丁丁添加一個 PDF 渲染引擎。可是,目前并沒有可以在 .NET 框架上運行的免費 PDF 渲染引擎。經過網上的搜索,有人使用 C++/CLI 調用 XPDF 或 Mupdf,實現了不安裝 Adobe 系列軟件而渲染出 PDF 文件的功能。

Mupdf 是一個開源的 PDF 渲染引擎,使用 C 語言編寫,可編譯成能讓 C# 調用的動態鏈接庫。因此,只要編寫合適的調用代碼,就能使用該渲染引擎,將 PDF 文檔轉換為一頁一頁的圖片,或者在程序界面顯示 PDF 文檔的內容。

要使用 Mupdf 渲染 PDF 文檔,有幾個步驟:

  • 獲取 Mupdf 的動態鏈接庫。
  • 了解該庫中的相關導出函數。
  • 為導出函數撰寫 P/Invoke 代碼。
  • 撰寫 C# 代碼,調用 Mupdf 的導出函數。將渲染后的數據(Pixmap)轉換為位圖,或直接在控件的設備句柄(HDC)繪制渲染后的文檔。
  • 獲取 Mupdf 動態鏈接庫

    Mupdf 的源代碼沒有提供直接編譯生成動態鏈接庫的 Make 文件。幸好,從另一個基于 Mupdf 的開源項目——SumatraPDF——能編譯生成 Mupdf 動態鏈接庫。在 SumatraPDF 的源代碼網站下載源代碼和工程文件,使用 Visual C++(免費的速成版就可以了)編譯該工程,生成配置選“Release”,就能生成 Mupdf 的動態鏈接庫。

    了解 Mupdf 的概念和導出函數

    Mupdf 的導出函數可通過查看 Mupdf 源代碼的頭文件得到。頭文件可在 Mupdf 官方網站的 Documentation 區在線查閱。

    Mupdf 最通用的函數放在頭文件“Fitz.h”里。如果只是使用 C# 函數來渲染 PDF 文檔,只使用 Fitz.h 文件中提供的結構和函數即可。在渲染 PDF 文檔時用到的結構主要有五個:

  • fz_context:存放渲染引擎所用的全局數據。
  • fz_document:存放文檔的信息。
  • fz_page:存放頁面的數據。
  • fz_device:用于放置渲染結果的目標設備。
  • fz_pixmap:存放渲染結果的畫布。
  • Fitz.h 文件中提供的函數均以“fz_”開頭,這些函數可用于處理上述五個結構。以上述五個結構為基礎,調用相應的函數,就能完成渲染 PDF 文檔的任務。

    沒有 C 語言基礎的開發人員請注意:部分預定義處理指令——即 #define 指令,也使用“fz_”開頭,這些處理指令并不是導出函數。在使用 P/Invoke 技術調用函數庫時不能使用 #define 指令定義的替換函數。例如,fz_try、fz_catch、fz_finally 就是這類型的預定義處理指令。

    為導出函數撰寫 P/Invoke 代碼

    Fitz.h 提供的導出函數中,下列函數在渲染 PDF 文檔時是必須使用的。

  • fz_new_context:創建渲染文檔時的上下文變量。
  • fz_free_context:釋放上下文變量所占用的資源。
  • fz_open_file_w:打開文件流(傳入的文件名變量為 Unicode)
  • fz_open_document_with_stream:打開文件流對應的文檔(PDF 或其它支持的文件格式)。
  • fz_close_document:關閉文檔。
  • fz_close:關閉文件流。
  • fz_count_pages:獲得文檔的頁數。
  • fz_load_page:加載文檔指定的頁面。
  • fz_free_page:釋放文檔頁面占用的資源。
  • fz_bound_page:確定文檔頁面的尺寸。
  • fz_new_pixmap:創建渲染頁面所用的圖形畫紙。
  • fz_clear_pixmap_with_value:清除畫紙(通常用于將 PDF 文檔的背景色設置為純白色)。
  • fz_new_draw_device:從畫紙創建繪圖設備。
  • fz_find_device_colorspace:獲取渲染頁面所用的顏色域(彩色或灰色)。
  • fz_run_page:將頁面渲染到指定的設備上。
  • fz_free_device:釋放設備所占用的資源。
  • fz_drop_pixmap:釋放畫紙占用的資源。
  • fz_pixmap_samples:獲取畫紙的數據(用于將已渲染的畫紙內容轉換為 Bitmap)。
  • 在撰寫 P/Invoke 代碼的過程中,我們還會遇到幾個結構,“BBox”表示邊框結構,包含 x0、y0、x1 和 y1 四個整數坐標變量;“Rectangle”與“BBox”類似,但坐標變量為浮點數;“Matrix”用于渲染過程中的拉伸、平移等操作(詳見 Mupdf 代碼中的頭文件)。最后,我們得到與下列代碼類似的 P/Invoke C# 代碼。

    public struct BBox {public int Left, Top, Right, Bottom; } public struct Rectangle {public float Left, Top, Right, Bottom; } public struct Matrix {public float A, B, C, D, E, F; } class NativeMethods {const string DLL = "libmupdf.dll";[DllImport (DLL, EntryPoint="fz_new_context")]public static extern IntPtr NewContext (IntPtr alloc, IntPtr locks, uint max_store);[DllImport (DLL, EntryPoint = "fz_free_context")]public static extern IntPtr FreeContext (IntPtr ctx);[DllImport (DLL, EntryPoint = "fz_open_file_w", CharSet = CharSet.Unicode)]public static extern IntPtr OpenFile (IntPtr ctx, string fileName);[DllImport (DLL, EntryPoint = "fz_open_document_with_stream")]public static extern IntPtr OpenDocumentStream (IntPtr ctx, string magic, IntPtr stm);[DllImport (DLL, EntryPoint = "fz_close")]public static extern IntPtr CloseStream (IntPtr stm);[DllImport (DLL, EntryPoint = "fz_close_document")]public static extern IntPtr CloseDocument (IntPtr doc);[DllImport (DLL, EntryPoint = "fz_count_pages")]public static extern int CountPages (IntPtr doc);[DllImport (DLL, EntryPoint = "fz_bound_page")]public static extern Rectangle BoundPage (IntPtr doc, IntPtr page);[DllImport (DLL, EntryPoint = "fz_clear_pixmap_with_value")]public static extern void ClearPixmap (IntPtr ctx, IntPtr pix, int byteValue);[DllImport (DLL, EntryPoint = "fz_find_device_colorspace")]public static extern IntPtr FindDeviceColorSpace (IntPtr ctx, string colorspace);[DllImport (DLL, EntryPoint = "fz_free_device")]public static extern void FreeDevice (IntPtr dev);[DllImport (DLL, EntryPoint = "fz_free_page")]public static extern void FreePage (IntPtr doc, IntPtr page);[DllImport (DLL, EntryPoint = "fz_load_page")]public static extern IntPtr LoadPage (IntPtr doc, int pageNumber);[DllImport (DLL, EntryPoint = "fz_new_draw_device")]public static extern IntPtr NewDrawDevice (IntPtr ctx, IntPtr pix);[DllImport (DLL, EntryPoint = "fz_new_pixmap")]public static extern IntPtr NewPixmap (IntPtr ctx, IntPtr colorspace, int width, int height);[DllImport (DLL, EntryPoint = "fz_run_page")]public static extern void RunPage (IntPtr doc, IntPtr page, IntPtr dev, Matrix transform, IntPtr cookie);[DllImport (DLL, EntryPoint = "fz_drop_pixmap")]public static extern void DropPixmap (IntPtr ctx, IntPtr pix);[DllImport (DLL, EntryPoint = "fz_pixmap_samples")]public static extern IntPtr GetSamples (IntPtr ctx, IntPtr pix);}

    撰寫代碼調用導出函數

    在上述 P/Invoke 代碼已經準備好之后,需要撰寫代碼調用導出函數并渲染出頁面。為簡單起見,示例中并不使用類封裝結構,而是直接調用上述 P/Invoke 函數。上述函數中,名稱中包含“close”、“drop”、“free”的函數是用來釋放資源的。在實際開發過程中,應撰寫相應的類來保存對這些資源的指針引用。而且,這些類應實現 IDisposable 接口,并將釋放資源的函數放在 Dispose 方法中。在完成操作后,應調用類實例的 Dispose 方法,釋放相關的資源。

    渲染頁面的流程如下,按步驟逐個調用上述的函數即可:

  • 加載文檔。
  • 加載頁面。
  • 預備好繪圖畫紙(Pixmap)。
  • 從繪圖畫紙創建繪圖設備。
  • 將頁面繪制到繪圖設備(即畫紙)上。
  • 將畫紙的數據轉換為 Bitmap。
  • 保存 Bitmap 或將 Bitmap 繪制到程序界面。
  • 釋放 Bitmap 的資源。
  • 釋放畫紙、繪圖設備、頁面和文檔的資源。
  • 代碼如下所示。

    static void Main (string[] args) {const uint FZ_STORE_DEFAULT = 256 << 20;IntPtr ctx = NativeMethods.NewContext (IntPtr.Zero, IntPtr.Zero, FZ_STORE_DEFAULT); // 創建上下文IntPtr stm = NativeMethods.OpenFile (ctx, "test.pdf"); // 打開 test.pdf 文件流IntPtr doc = NativeMethods.OpenDocumentStream (ctx, ".pdf", stm); // 從文件流創建文檔對象int pn = NativeMethods.CountPages (doc); // 獲取文檔的頁數for (int i = 0; i < pn; i++) { // 遍歷各頁IntPtr p = NativeMethods.LoadPage (doc, i); // 加載頁面(首頁為 0)Rectangle b = NativeMethods.BoundPage (doc, p); // 獲取頁面尺寸using (var bmp = RenderPage (ctx, doc, p, b)) { // 渲染頁面并轉換為 Bitmapbmp.Save ((i+1) + ".png"); // 將 Bitmap 保存為文件 }NativeMethods.FreePage (doc, p); // 釋放頁面所占用的資源 }NativeMethods.CloseDocument (doc); // 釋放其它資源 NativeMethods.CloseStream (stm);NativeMethods.FreeContext (ctx); }

    其中,RenderPage 方法用來渲染圖片,代碼如下。

    static Bitmap RenderPage (IntPtr context, IntPtr document, IntPtr page, Rectangle pageBound) {Matrix ctm = new Matrix ();IntPtr pix = IntPtr.Zero;IntPtr dev = IntPtr.Zero;int width = (int)(pageBound.Right - pageBound.Left); // 獲取頁面的寬度和高度int height = (int)(pageBound.Bottom - pageBound.Top);ctm.A = ctm.D = 1; // 設置單位矩陣 (1,0,0,1,0,0)// 創建與頁面相同尺寸的繪圖畫布(Pixmap)pix = NativeMethods.NewPixmap (context, NativeMethods.FindDeviceColorSpace (context, "DeviceRGB"), width, height);// 將 Pixmap 的背景設為白色NativeMethods.ClearPixmap (context, pix, 0xFF);// 創建繪圖設備dev = NativeMethods.NewDrawDevice (context, pix);// 將頁面繪制到以 Pixmap 生成的繪圖設備上 NativeMethods.RunPage (document, page, dev, ctm, IntPtr.Zero);NativeMethods.FreeDevice (dev); // 釋放繪圖設備對應的資源dev = IntPtr.Zero;// 創建與 Pixmap 相同尺寸的彩色 BitmapBitmap bmp = new Bitmap (width, height, PixelFormat.Format24bppRgb); var imageData = bmp.LockBits (new System.Drawing.Rectangle (0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);unsafe { // 將 Pixmap 的數據轉換為 Bitmap 數據// 獲取 Pixmap 的圖像數據byte* ptrSrc = (byte*)NativeMethods.GetSamples (context, pix);byte* ptrDest = (byte*)imageData.Scan0;for (int y = 0; y < height; y++) {byte* pl = ptrDest;byte* sl = ptrSrc;for (int x = 0; x < width; x++) {// 將 Pixmap 的色彩數據轉換為 Bitmap 的格式pl[2] = sl[0]; //b-rpl[1] = sl[1]; //g-gpl[0] = sl[2]; //r-b//sl[3] 是透明通道數據,在此忽略pl += 3;sl += 4;}ptrDest += imageData.Stride;ptrSrc += width * 4;}}NativeMethods.DropPixmap (context, pix); // 釋放 Pixmap 占用的資源return bmp; }

    好了,渲染 PDF 文檔的代碼雛形就此完成了。

    在實際項目開發中,我們還需要考慮以下幾個首要問題:

  • 處理 Mupdf 拋出的異常:捕獲 AccessViolationException 異常。
  • 記住釋放資源:可考慮將相應的資源封裝為實現 IDisposible 接口的類。
  • 擴展程序的功能:可參考使用 Mupdf 的開放源代碼項目,其中最著名的一個項目莫過于 SumatraPDF。
  • 在64位機器上運行:可將 .NET 項目的 CPU 平臺設置為 x86,強制程序用 32 位 .NET Framework 運行。
  • 本文及源代碼項目發布在 CodeProject 網站,有興趣的同好可閱讀《Rendering PDF Documents with Mupdf and P/Invoke in C#》。

    來自:http://www.cnblogs.com/pdfpatcher/archive/2012/11/25/2785154.html

    轉載于:https://www.cnblogs.com/lusunqing/p/3829082.html

    總結

    以上是生活随笔為你收集整理的转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。