前段时间开发一个项目,需要用到PDF转图片,并在MFC开发界面中显示图片。经过google,找到几个可用的方法,分别是GhostScript、imageMagic和MuPDF,由于使用MFC做界面开发,因此需要在VC++中使用。
首先用到的是GhostScript,网络上有一些C#的使用方法,如下面的链接:
http://www.codeproject.com/Articles/317700/Convert-a-PDF-into-a-series-of-images-using-Csharp
http://www.codeproject.com/Articles/32274/How-To-Convert-PDF-to-Image-Using-Ghostscript-API
http://blog.csdn.net/ljsspace/article/details/6530046
参考以上的方法,可以在VC++中使用GhostScript的gsdll32.dll(在ghostscript的安装路径下可以找到)把PDF转为图片,但实际使用中会遇到一些问题,某些PDF转为图片后长宽比例出现错误,顶部和底部会出现额外的白边,用GhostScript去显示这些PDF,显示效果也一样会出现同样的问题。改用imageMagic去打开PDF,显示效果一样,顶部和底部也会出现额外的白边。我猜测两个软件使用了相同的算法,或者使用了同一个库。
后来我改用MuPDF,PDF转图片的效果非常好,而且速度比GhostScript更快。
同样,在codeproject上有C#的使用方法:http://www.codeproject.com/Articles/498317/Rendering-PDF-Documents-with-Mupdf-and-P-Invoke-in
英文不好的朋友,可以看这里,有人把文章翻译并提出了自己的见解:http://www.cnblogs.com/pdfpatcher/archive/2012/11/25/2785154.html
由于我是使用C++,对于c开源库的MuPDF来说,使用起来更直接方便。接下来重点介绍一下MuPDF转图片的方法。
获得MuPDF开源库,如果去官方下载MuPDF,编译后是一些exe及静态链接库,地址在这里:http://code.google.com/p/mupdf/downloads/list
很幸运,我们还是能方便能找到动态链接库,有人已经把MuPDF集成到另外一个开源的sumatrapdf中,在下面的地址下载,编译后,在obj-dbg文件夹下可以找到libmupdf.dll、libmupdf.lib、fitz.h。下载地址:http://code.google.com/p/sumatrapdf/downloads/list
为了方便大家下载DLL,我贴出CSDN下载地址,不需要积分可下载:http://download.csdn.net/detail/arthurfangmc/4948658
具体怎么要用哪些函数,及函数的介绍,我就不再重复介绍,上面2篇文章都做了介绍,我直接贴出几个重要的代码。
我的代码直接从MuPDF源代码中修改而来,写了一个简单的类,变量名基本上没改,成员变量没有改成类似“myValue”或“m_value”,大家随便看看吧。
MuPDFConvert.h:
#ifndef MY_MUPDF_CONVERT_PDF_PNG
#define MY_MUPDF_CONVERT_PDF_PNGextern "C"
{
#include "mupdf/fitz.h"
#include "mupdf/fitz-internal.h"
}class CMuPDFConvert
{
public:CMuPDFConvert(void);~CMuPDFConvert(void);bool Pdf2Png(const wchar_t* pdfFileName/*,const char* imageOutputPath*/,const char* imageName, int &pageNum);private:void drawpage(fz_context *ctx, fz_document *doc, int pagenum);private:static const fz_matrix fz_identity;static fz_colorspace k_device_rgb;fz_colorspace *fz_device_rgb;fz_document *m_doc;fz_stream *m_pStream;fz_context *m_ctx;int uselist;int alphabits;char output[1024];float resolution;float rotation;int res_specified;int width;int height;int fit;fz_colorspace *colorspace;int savealpha;int invert;float gamma_value;char filename[1024];
};#endif
MuPDFConvert.cpp:
#include "StdAfx.h"
#include "MuPDFConvert.h"void fz_free_colorspace_imp(fz_context *ctx, fz_storable *cs_)
{fz_colorspace *cs = (fz_colorspace *)cs_;if (cs->free_data && cs->data)cs->free_data(ctx, cs);fz_free(ctx, cs);
}static void rgb_to_rgb(fz_context *ctx, fz_colorspace *cs, float *rgb, float *xyz)
{xyz[0] = rgb[0];xyz[1] = rgb[1];xyz[2] = rgb[2];
}const fz_matrix CMuPDFConvert::fz_identity = { 1, 0, 0, 1, 0, 0 };
fz_colorspace CMuPDFConvert::k_device_rgb = { {-1, fz_free_colorspace_imp}, 0, "DeviceRGB", 3, rgb_to_rgb, rgb_to_rgb };CMuPDFConvert::CMuPDFConvert(void)
{fz_device_rgb = &k_device_rgb;uselist = 1;alphabits = 8;//output = NULL;resolution = 72;rotation = 0;res_specified = 0;width = 0;height = 0;fit = 0;colorspace = NULL;savealpha = 0;invert = 0;gamma_value = 1;m_doc = NULL;m_ctx = NULL;
}CMuPDFConvert::~CMuPDFConvert(void)
{if (m_pStream){fz_close(m_pStream);m_pStream = NULL;}if (m_doc){fz_close_document(m_doc);m_doc = NULL;}if (m_ctx){fz_free_context(m_ctx);m_ctx = NULL;}}bool CMuPDFConvert::Pdf2Png(const wchar_t* wcharPdfFile/*,const char* imageOutputPath*/,const char* imageName, int &pageNum)
{char tempPath[1024];//strcpy_s(tempPath, imageOutputPath);//strcat_s(tempPath, imageName);strcpy_s(tempPath, imageName);strcat_s(tempPath, "%d.png");strcpy_s(output, (strlen(tempPath)+1)*sizeof(char), tempPath);m_ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);if (!m_ctx){
#if _DEBUGfprintf(stderr, "mupdf cannot initialise context\n");
#endifreturn false;}fz_try(m_ctx){fz_set_aa_level(m_ctx, alphabits);colorspace = fz_device_rgb;m_pStream = fz_open_file_w(m_ctx, wcharPdfFile);if (m_pStream){m_doc = fz_open_document_with_stream(m_ctx, ".pdf", m_pStream);}if (!m_doc){
#if _DEBUGfprintf(stderr, "mupdf cannot open pdf\n");
#endifreturn false;}if (fz_needs_password(m_doc)){
#if _DEBUGfprintf(stderr, "mupdf cannot authenticate password\n");fz_throw(m_ctx, "mupdf cannot authenticate password: %s", filename);
#endifreturn false;}int pn = fz_count_pages(m_doc);pageNum = pn;for (int i=0; i<pn; ++i){fz_page *page = fz_load_page(m_doc, i);fz_rect bound = fz_bound_page(m_doc, page);//PDF-->PNGdrawpage(m_ctx, m_doc, i+1);fz_free_page(m_doc, page);page = NULL;} }fz_catch(m_ctx){return false;}if (m_pStream){fz_close(m_pStream);m_pStream = NULL;}if (m_doc){fz_close_document(m_doc);m_doc = NULL;}if (m_ctx){fz_free_context(m_ctx);m_ctx = NULL;}return true;
}void CMuPDFConvert::drawpage(fz_context *ctx, fz_document *doc, int pagenum)
{fz_page *page;fz_display_list *list = NULL;fz_device *dev = NULL;int start;fz_var(list);fz_var(dev);fz_try(ctx){page = fz_load_page(doc, pagenum - 1);}fz_catch(ctx){fz_throw(ctx, "cannot load page %d in file '%s'", pagenum, filename);}if (uselist){fz_try(ctx){list = fz_new_display_list(ctx);dev = fz_new_list_device(ctx, list);fz_run_page(doc, page, dev, fz_identity, NULL);}fz_catch(ctx){fz_free_device(dev);fz_free_display_list(ctx, list);fz_free_page(doc, page);fz_throw(ctx, "cannot draw page %d in file '%s'", pagenum, filename);}fz_free_device(dev);dev = NULL;}if (output){float zoom;fz_matrix ctm;fz_rect bounds, bounds2;fz_bbox bbox;fz_pixmap *pix = NULL;int w, h;fz_var(pix);bounds = fz_bound_page(doc, page);zoom = resolution / 72;ctm = fz_scale(zoom, zoom);ctm = fz_concat(ctm, fz_rotate(rotation));bounds2 = fz_transform_rect(ctm, bounds);bbox = fz_round_rect(bounds2);/* Make local copies of our width/height */w = width;h = height;/* If a resolution is specified, check to see whether w/h are* exceeded; if not, unset them. */if (res_specified){int t;t = bbox.x1 - bbox.x0;if (w && t <= w)w = 0;t = bbox.y1 - bbox.y0;if (h && t <= h)h = 0;}/* Now w or h will be 0 unless then need to be enforced. */if (w || h){float scalex = w/(bounds2.x1-bounds2.x0);float scaley = h/(bounds2.y1-bounds2.y0);if (fit){if (w == 0)scalex = 1.0f;if (h == 0)scaley = 1.0f;}else{if (w == 0)scalex = scaley;if (h == 0)scaley = scalex;}if (!fit){if (scalex > scaley)scalex = scaley;elsescaley = scalex;}ctm = fz_concat(ctm, fz_scale(scalex, scaley));bounds2 = fz_transform_rect(ctm, bounds);}bbox = fz_round_rect(bounds2);/* TODO: banded rendering and multi-page ppm */fz_try(ctx){pix = fz_new_pixmap_with_bbox(ctx, colorspace, bbox);if (savealpha)fz_clear_pixmap(ctx, pix);elsefz_clear_pixmap_with_value(ctx, pix, 255);dev = fz_new_draw_device(ctx, pix);if (list)fz_run_display_list(list, dev, ctm, bbox, NULL);elsefz_run_page(doc, page, dev, ctm, NULL);fz_free_device(dev);dev = NULL;if (invert)fz_invert_pixmap(ctx, pix);if (gamma_value != 1)fz_gamma_pixmap(ctx, pix, gamma_value);if (savealpha)fz_unmultiply_pixmap(ctx, pix);if (output){char buf[512];sprintf(buf, output, pagenum);if (strstr(output, ".png")){fz_write_png(ctx, pix, buf, savealpha);}}fz_drop_pixmap(ctx, pix);}fz_catch(ctx){fz_free_device(dev);fz_drop_pixmap(ctx, pix);fz_free_display_list(ctx, list);fz_free_page(doc, page);fz_rethrow(ctx);}}if (list)fz_free_display_list(ctx, list);fz_free_page(doc, page);fz_flush_warnings(ctx);
}
使用方法:
#include "stdafx.h"
#include "MuPDFConvert.h"int _tmain(int argc, _TCHAR* argv[])
{CMuPDFConvert pdfConvert;int nNum = 0;pdfConvert.Pdf2Png(L"test.pdf", "test", nNum);return 0;
}
这里生成的图片是PNG格式,如果你需要其他格式的图片,可以再选择其它库,如果编译器版本支持CImage,可以使用CImage来转其它图片格式,
另外,在MFC中可以直接使用CImage来显示PNG及其它格式图片,使用方法非常简单。
定义一个变量
CImage m_img;
导入图片
if(!m_img.IsNull())
{ m_img.Destroy();
}
m_img.Load(strImagePath);
在OnPaint()函数中:
if(!m_img.IsNull())
{ CPaintDC dc(GetDlgItem(IDC_CHILD_PIC)); dc.SetStretchBltMode(HALFTONE);m_img.Draw(dc.m_hDC, 0, 0, m_nFitWidth, m_nFitHeight, 0, 0, m_nRegionWidth, m_nRegionHeight);//透明显示PNG图片//m_img.TransparentBlt(dc.m_hDC, 0, 0, m_nFitWidth, m_nFitHeight, // 0, 0, m_nRegionWidth, m_nRegionHeight, RGB(255,255,255));
}
使用过程中,PDF转图片还是需要花费一定的时间,如果你需要实时显示图片,而PDF页数又非常多,最好另开一个线程来做PDF转图片的工作。ghostScript和MuPDF我都在项目中使用过,PDF页数很多或者图片分辨率很大的时候,MuPDF的速度优势还是十分明显的。
附PDF转PNG图片下载代码
https://download.csdn.net/download/ArthurFangmc/12718742