admin管理员组

文章数量:1648939

手搓代码,运行良好,控制台、服务类程序同样适用。

基本原理是 RTF 文件利用 RICHEDIT 控件的打印功能,打印到图片,再使用 pdflib 库,转换成 PDF.。

那么问题来了:

1、由于使用了 RICHEDIT 控件,就只能在 Windows 下使用,不能跨平台,有没有可以类似代码不需要经过 RICHEDIT 控件的。

2、有没有不使用 pdflib 库的代码,毕竟 pdflib 虽然对个人免费,但对商业使用是收费的。

/* 
#include "PDFLib.h"
#ifdef _M_X64
#pragma comment(lib, "..\\lib\\x64\\PDFLib.lib")
#else
#pragma comment(lib, "..\\lib\\x86\\PDFLib.lib")
#endif

#include <atlimage.h>
*/
///<summary>RTF 转 PDF</summary>
///<param name="sRtf">RTF内容</param>
///<param name="fPaperW">纸张宽度 mm</param>
///<param name="fPaperH">纸张高度 mm</param>
///<param name="nMarginL">纸张左边距 mm</param>
///<param name="nMarginT">纸张上边距 mm</param>
///<param name="nMarginR">纸张右边距 mm</param>
///<param name="nMarginB">纸张下边距 mm</param>
///<returns>PDF 内容</returns>
std::string Rtf2Pdf(std::string sRtf, double fPaperW, double fPaperH, int nMarginL, int nMarginT, int nMarginR, int nMarginB)
{
	if (fPaperW <= 0 || fPaperH <= 0)
		return "";

	::CoInitialize(NULL); // 初始化 COM 库
	ULONG_PTR uGdiplusToken = 0;
	Gdiplus::GdiplusStartup(&uGdiplusToken, &Gdiplus::GdiplusStartupInput({ 0 }), NULL); // 初始化 GDI++
	HMODULE hLibRich = ::LoadLibrary(L"Msftedit.dll");	// 加载 RichEdit 库
	HWND hWndRich = ::CreateWindow(MSFTEDIT_CLASS, L"", ES_MULTILINE, 0, 0, 0, 0, NULL, NULL, ::GetModuleHandle(NULL), NULL); // 创建打印使用的 RichEdit 控件

	int nPxPaperW = MM2PX(fPaperW, 96); // 像素宽度,#define MM2PX(mm, dpi) int((mm) * dpi / 25.4)	,mm 转换成 px (像素),需要提供当前分辨率,一般是 96
	int nPxPaperH = MM2PX(fPaperH, 96); // 像素高度,#define MM2PX(mm, dpi) int((mm) * dpi / 25.4)	,mm 转换成 px (像素),需要提供当前分辨率,一般是 96

	std::string sPdf;

	if (::SetEditRtfCode(hWndRich, sRtf))
	{
		std::deque<CHARRANGE> lsPage; // 每页的开始位置和结束位置

		lsPage.push_back(CHARRANGE{ 0, -1 }); // 新一页位置

		FINDTEXTEX ftPage = { { 0, -1 }, L"[PAGEBREAK]", { -1, -1 } }; // [PAGEBREAK] 为分页标记

		while ((long)::SendMessage((HWND)hWndRich, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&ftPage) != -1)
		{
			lsPage.back().cpMax = ftPage.chrgText.cpMin; // 上一页结束位置,不包括 [PAGEBREAK]

			// 下一页开始位置跳过分页标记后面的一个空格回车换行类字符
			CHARRANGE crBlank = { ftPage.chrgText.cpMax, ftPage.chrgText.cpMax + 1 };
			::SendMessage((HWND)hWndRich, EM_EXSETSEL, 0, (LPARAM)&crBlank);

			if (::SendMessage((HWND)hWndRich, EM_SELECTIONTYPE, 0, 0) == SEL_TEXT)
			{
				wchar_t sSelText[8] = { 0 };
				::SendMessage((HWND)hWndRich, EM_GETSELTEXT, 0, (LPARAM)sSelText);

				if (___isblank(sSelText[0]))
					ftPage.chrgText.cpMax++;
			}

			lsPage.push_back(CHARRANGE{ ftPage.chrgText.cpMax, -1 });	// 新一页位置,不包括 [PAGEBREAK]
			ftPage.chrg.cpMin = ftPage.chrgText.cpMax; // 从下一个位置继续查找
		}

		// 创建内存 PDF 文件
		PDF* pdf = ::PDF_new();
		::PDF_begin_document(pdf, "", 0, "");

		std::deque<unsigned char> sImgData(nPxPaperW * nPxPaperH * 4, 0);

		CImage img;
		img.Create(nPxPaperW, nPxPaperH, 32);
		HDC hImgDC = img.GetDC();

		for (CHARRANGE& chrg : lsPage)
		{
			COLORREF cBkOld = ::SetBkColor(hImgDC, RGB(255, 255, 255)); // 背景
			RECT rPxPage = { 0, 0, nPxPaperW, nPxPaperH };
			::ExtTextOut(hImgDC, 0, 0, ETO_OPAQUE, &rPxPage, NULL, 0, NULL); // 填充白色背景
			::SetBkColor(hImgDC, cBkOld);	// 背景

			FORMATRANGE fr = { hImgDC, hImgDC };
			fr.rc = fr.rcPage = { 0, 0, MM2TW(fPaperW), MM2TW(fPaperH) };	// #define MM2TW(mm) int((mm) * 14400 / 254),mm 转换成 twips (缇)
			fr.rc.left		+= MM2TW(nMarginL);	// 纸张左边距 twip,#define MM2TW(mm) int((mm) * 14400 / 254),mm 转换成 twips (缇)
			fr.rc.top		+= MM2TW(nMarginT);	// 纸张上边距 twip,#define MM2TW(mm) int((mm) * 14400 / 254),mm 转换成 twips (缇)
			fr.rc.right		-= MM2TW(nMarginR);	// 纸张右边距 twip,#define MM2TW(mm) int((mm) * 14400 / 254),mm 转换成 twips (缇)
			fr.rc.bottom	-= MM2TW(nMarginB);	// 纸张下边距 twip,#define MM2TW(mm) int((mm) * 14400 / 254),mm 转换成 twips (缇)
			fr.chrg = chrg;

			::SendMessage(hWndRich, EM_FORMATRANGE, TRUE, (LPARAM)&fr); // 输出到图像

			IStream* pIStream = NULL;
			HGLOBAL hGlobal = ::GlobalAlloc(GHND, 0); // 创建可移动的缓冲区 
			::CreateStreamOnHGlobal(hGlobal, FALSE, &pIStream); // 创建 IStream 流
			img.Save(pIStream, Gdiplus::ImageFormatPNG); // 保存到 IStream 流

			void* pLocalBuf = ::GlobalLock(hGlobal);		// 缓冲区的起始地址
			int nSizeInByte = (int)::GlobalSize(hGlobal);	// 缓冲区的字节长度

			::PDF_begin_page_ext(pdf, fPaperW, fPaperH, "");	// 页面
			std::string sPvfFile = W2M(GetRandomMd5().c_str()); // 虚拟文件名
			::PDF_create_pvf(pdf, sPvfFile.c_str(), 0, pLocalBuf, nSizeInByte, "");
			int nPdfImg = ::PDF_load_image(pdf, "auto", sPvfFile.c_str(), 0, "");
			assert(nPdfImg != -1);

			::GlobalUnlock(hGlobal);
			::GlobalFree(hGlobal);
			pIStream->Release();

			if (nPdfImg != -1)
			{
				::PDF_fit_image(pdf, nPdfImg, 0, 0, "adjustpage");
				::PDF_close_image(pdf, nPdfImg);
			}

			::PDF_delete_pvf(pdf, sPvfFile.c_str(), 0);
			::PDF_end_page_ext(pdf, ""); // 结束本页
		}

		::SendMessage(hWndRich, EM_FORMATRANGE, FALSE, 0); // 释放缓存

		::PDF_end_document(pdf, "");// 关闭PDF文件

		long nPdfBuf = 0;
		const char* sPdfBuf = ::PDF_get_buffer(pdf, &nPdfBuf);
		sPdf.assign(sPdfBuf, nPdfBuf);

		::PDF_delete(pdf);

		img.ReleaseDC();
	}

	::DestroyWindow(hWndRich); // 销毁 RichEdit
	::FreeLibrary(hLibRich); // 卸载 RichEdit 库
	Gdiplus::GdiplusShutdown(uGdiplusToken); // 结束 GDI++
	::CoUninitialize(); // 终止 COM 库

	return sPdf;
}

本文标签: 函数RtfPDF