ttf嵌入pdf

#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;
}



posted @ 2024-06-05 00:41  Ding-yixia  阅读(44)  评论(0编辑  收藏  举报