ttf嵌入pdf

Ding-yixia發表於2024-06-05
#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;
}



相關文章