#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdlib.h> // 引入标准库头文件,提供一些通用的函数和宏定义
#include <stdio.h> // 引入标准输入输出头文件,提供输入输出相关的函数
#include <string.h> // 引入字符串处理头文件,提供字符串操作相关的函数
#include <iostream> // 引入输入输出流头文件,提供输入输出流的操作
#include <vector> // 引入向量容器头文件,提供动态数组的功能
#include <cstdint> // 引入固定宽度整数类型头文件,提供固定宽度整数类型的定义
#include <setjmp.h> // 引入setjmp/longjmp头文件,提供异常处理功能
#include <wchar.h> // 引入宽字符处理头文件,提供宽字符操作相关的函数
#include "hpdf.h" // 引入Haru PDF库头文件,提供创建、编辑PDF文档的功能
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
//#define FONT_PATH "C:/fonts/Arial-Unicode-MS.ttf" // Arial Unicode MS字体文件路径
#define FONT_PATH "C:/fonts/SimSun-01.ttf" // Arial Unicode MS字体文件路径
#define START_CHAR (000000) // Unicode编码 起始
#define END_CHAR (65536) // Unicode编码 结束
#define CHARACTERS_PER_LINE 10 // 每行 10 个字符
#define X_SPACEING 50 // 每一行每个字符之间的间隔
#define PAGE_NUM 200 //每页200个字符
float height, x, y;
jmp_buf* env; // 定义全局变量env,用于存储setjmp的环境
// 将Unicode编码转换为UTF-8编码的函数
std::vector<uint8_t> unicode_code_point_to_utf8(uint32_t code_point)
{
////std::vector<uint8_t>类型的变量utf8_bytes,用于存储转换后的UTF-8字节序列
std::vector<uint8_t> utf8_bytes;
/*
1.如果code_point小于等于0x7F(即ASCII字符),
直接将code_point作为UTF-8字节添加到utf8_bytes中。
2.
如果code_point在0x80到0x7FF之间,
将其转换为两个字节的UTF-8编码,并将这两个字节添加到utf8_bytes中。
3.如果code_point在0x800到0xFFFF之间,
将其转换为三个字节的UTF-8编码,并将这三个字节添加到utf8_bytes中。
4.如果code_point在0x10000到0x10FFFF之间(即需要使用代理对表示的Unicode码点),
则先将其分解为前导代理和后尾代理,然后分别将它们转换为UTF-8编码,
最后将这两个UTF-8编码合并到utf8_bytes中。
5.如果code_point超出了有效范围,输出错误信息
*/
if (code_point <= 0x7F)
{
utf8_bytes.push_back(code_point);
}
else if (code_point <= 0x7FF)
{
utf8_bytes.push_back(0xC0 | ((code_point >> 6) & 0x1F));
utf8_bytes.push_back(0x80 | (code_point & 0x3F));
}
else if (code_point <= 0xFFFF)
{
utf8_bytes.push_back(0xE0 | ((code_point >> 12) & 0x0F));
utf8_bytes.push_back(0x80 | ((code_point >> 6) & 0x3F));
utf8_bytes.push_back(0x80 | (code_point & 0x3F));
}
else if (code_point <= 0x10FFFF)
{
// 分解
uint32_t lead_surrogate = (code_point >> 10) + 0xD800;
uint32_t trail_surrogate = (code_point & 0x3FF) + 0xDC00;
// 转换两个 UTF-8
std::vector<uint8_t> lead_bytes = unicode_code_point_to_utf8(lead_surrogate);
std::vector<uint8_t> trail_bytes = unicode_code_point_to_utf8(trail_surrogate);
// 将两个 UTF-8 编码合并
utf8_bytes.insert(utf8_bytes.end(), lead_bytes.begin(), lead_bytes.end());
utf8_bytes.insert(utf8_bytes.end(), trail_bytes.begin(), trail_bytes.end());
}
else
{
std::cerr << "Invalid Unicode code point." << std::endl;
}
//返回转换后的UTF-8字节序列utf8_bytes
return utf8_bytes;
}
void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data)
{
printf("ERROR: error_no=%04X, detail_no=%u\n", (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
longjmp(*env, 1);
}
// 函数:获取指定字体文件中所有存在的Unicode字符,并以十六进制字符串形式保存
void getUnicodeCharsFromFont(const std::string& fontPath, std::vector<std::string>& unicodeCharsHex) {
FT_Library library;
FT_Face face;
// 初始化FreeType库
if (FT_Init_FreeType(&library)) {
std::cerr << "FT_Init_FreeType failed!" << std::endl;
return;
}
// 加载字体文件
if (FT_New_Face(library, fontPath.c_str(), 0, &face)) {
std::cerr << "FT_New_Face failed!" << std::endl;
FT_Done_FreeType(library);
return;
}
// 遍历cmap表中的所有子表
FT_UInt num_charmaps = face->num_charmaps;
for (FT_UInt i = 0; i < num_charmaps; ++i) {
FT_CharMap charmap = face->charmaps[i];
// 只处理Unicode编码的cmap表
if (charmap->encoding == FT_ENCODING_UNICODE) {
for (FT_ULong charcode = 0; charcode < 0x110000; ++charcode) {
FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
if (glyph_index != 0) {
//std::stringstream ss;
//ss << std::hex << charcode; // 转换为十六进制字符串
//unicodeCharsHex.push_back(ss.str()); // 存储到容器中
unicodeCharsHex.push_back(std::to_string(charcode)); // 将Unicode值转换为字符串并存储
}
}
}
}
// 清理资源
FT_Done_Face(face);
FT_Done_FreeType(library);
}
int main(int argc, char** argv)
{
HPDF_Doc pdf=nullptr; // 定义一个 PDF 文档对象
HPDF_Font font=nullptr; // 定义一个字体对象
HPDF_Page page=nullptr; // 定义一个页面对象
jmp_buf env; // 定义一个跳转缓冲区,用于错误处理
unsigned char buf[8];// 定义一个字符缓冲区,用于存储转换后的字符
unsigned int i; // 定义一个无符号整数变量,用于循环计数
std::vector<std::string> unicodeCharsDec;
getUnicodeCharsFromFont(FONT_PATH, unicodeCharsDec);
///* for (auto i : unicodeCharsHex)
// {
// std::cout << i << std::endl;
// }*/
// // 打印结果
// for (const auto& utf8Bytes : utf8BytesList)
// {
// for (uint8_t byte : utf8Bytes)
// {
// std::cout << std::hex << static_cast<int>(byte) << " ";
// }
// std::cout << std::endl;
// }
//#if 1
/*
创建一个新的 PDF 文档对象,并设置错误处理器和跳转缓冲区
当在使用libharu库创建HPDF_Doc对象时发生错误时,
libharu会调用error_handler函数来处理错误。
*/
pdf = HPDF_New(error_handler, env);
if (!pdf)
{
printf("ERROR: cannot create pdf object.\n");
return 1;
}
/*
这是一个错误处理机制,
1.当执行到setjmp(env)时会将当前的执行状态保存到env缓冲区,并返回一个非零的值。
2.setjmp返回的值非零,说明发生了错误并通过longjmp跳转到了相应的错误处理位置。
在这个错误处理块中,首先调用HPDF_Free释放之前创建的HPDF_Doc对象,然后返回1,表示出现错误。
3.setjmp返回的值为0,说明没有发生错误,程序会继续执行后续的代码。
*/
if (setjmp(env))
{
HPDF_Free(pdf);
return 1;
}
/* */
/*
启用UTF编码支持。通过调用HPDF_UseUTFEncodings(pdf);函数,
libharu库会启用UTF-8和UTF-16编码方式,以便支持Unicode字符集。
*/
HPDF_UseUTFEncodings(pdf);
/*
用于设置当前的编码器。
通过调用此函数并传递"UTF-8"作为参数,可以将当前的编码器设置为UTF - 8编码器,
使得PDF文档中的文本内容可以以UTF - 8编码进行处理和显示。
*/
HPDF_SetCurrentEncoder(pdf, "UTF-8");
// 加载 Arial Unicode MS 字体
float height = HPDF_Page_GetHeight(page);
float x = X_SPACEING * 11, y = height;
/*
加载一个TrueType字体文件,并获取该字体的UTF-8编码
FONT_PATH自定义字体文件路径
HPDF_TRUE的功能是在加载TrueType字体时启用子集化选项,表示只有需要的字形会被加载到内存中,而不是整个字体文件。
*/
const char* my_font = HPDF_LoadTTFontFromFile(pdf, FONT_PATH, HPDF_TRUE);
font = HPDF_GetFont(pdf, my_font, "UTF-8");
// 添加页
page = HPDF_AddPage(pdf);
// 设置页的大小
/*
HPDF_PAGE_SIZE_A4:这是一个常量,表示A4纸张的大小,通常为210mm × 297mm。
HPDF_PAGE_PORTRAIT:这是一个常量,表示页面的方向为纵向。
也可以使用HPDF_PAGE_LANDSCAPE常量来表示页面的方向为横向。
*/
HPDF_Page_SetSize(page, HPDF_PAGE_SIZE_A4, HPDF_PAGE_PORTRAIT);
/*
通过将此page作为参数传递给HPDF_Page_BeginText函数,
可以指定在page页面上开始绘制文本。*/
HPDF_Page_BeginText(page);
// 设置字体大小
HPDF_Page_SetFontAndSize(page, font, 12);
// 获取页高
/* */
height = HPDF_Page_GetHeight(page);
/* */
x = X_SPACEING * 11;
y = height;
/* */
HPDF_Page_MoveTextPos(page, x, y);
/* */
int char_count = 0;
int idx = 0;
//START_CHAR (000000) // Unicode编码 起始
//END_CHAR(65536) // Unicode编码 结束
//CHARACTERS_PER_LINE 10 // 每行 10 个字符
//LINES_PER_PAGE 20 // 每页20行
//X_SPACEING 50 // 每一行每个字符之间的间隔
//PAGE_NUM 200 //每页200个字符
//遍历从START_CHAR到END_CHAR之间的所有字符。
//for (i = START_CHAR; i <= END_CHAR; i++)
for(auto i : unicodeCharsDec)
{
//判断当前字符是否是每行的最后一个字符。
if (char_count % CHARACTERS_PER_LINE == 0)
{
//将文本光标移动到下一行的起始位置。
HPDF_Page_MoveTextPos(page, -X_SPACEING * 10, -40);
}
//清空缓冲区buf
memset(buf, 0, sizeof(buf));
// 将当前字符i转换为UTF-8编码。
// unicode 转 utf-8
std::vector<uint8_t> tmp = unicode_code_point_to_utf8(static_cast<uint32_t>(std::stoul(i, nullptr, 10)));
/* for (auto k : tmp)
{
std::cout << "unicode_code_point_to_utf8 " << k<< std::endl;
}*/
//std::cout << "unicode_code_point_to_utf8 " << i << std::endl;
//遍历转换后的UTF-8编码
for (int j = 0; j < tmp.size(); j++)
{
//将UTF-8编码存储到缓冲区buf中。
buf[j] = tmp[j];
}
//在PDF页面上显示当前字符。
HPDF_Page_ShowText(page, (const char*)buf);
//将文本光标向右移动一个字符宽度。
HPDF_Page_MoveTextPos(page, X_SPACEING, 0);
//更新文本光标的横坐标。
x += X_SPACEING;
//更新已处理的字符计数
char_count++;
//char_count==PAGE_NUM需要进行分页处理
if (char_count % PAGE_NUM == 0)
{
//结束当前页面的文本绘制
HPDF_Page_EndText(page);
//重置已处理的字符计数
char_count = 0;
//设置下一个页面的文本光标的横坐标
x = X_SPACEING * 11;
//设置下一个页面的文本光标的纵坐标
y = height;
//添加一个新的PDF页面
page = HPDF_AddPage(pdf);
//开始在新页面上绘制文本
HPDF_Page_BeginText(page);
//设置新页面上的字体和字号
HPDF_Page_SetFontAndSize(page, font, 12);
//将文本光标移动到新页面的指定位置
HPDF_Page_MoveTextPos(page, x, y);
}
}
//结束当前页面的文本绘制
HPDF_Page_EndText(page);
// 生成的PDF文档保存到名为"mypdf.pdf"的文件中。
HPDF_SaveToFile(pdf, "mypdf.pdf");
//释放PDF文档对象
HPDF_Free(pdf);
//#endif
return 0;
}