Windowsでコマンドプロンプトでutf8を表示するには、コードページを指定する必要がある。
VC++から起動した場合chcpできないので、コードから変える必要がある。
#include <cstdio> #include <windows.h> int main() { // 以下の二つはUTF-8を指定 //SetConsoleOutputCP(CP_UTF8); //SetConsoleOutputCP(65001); UINT now = GetConsoleOutputCP(); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); SetConsoleOutputCP(65001); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); SetConsoleOutputCP(now); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); }

wxWidgetsでレイアウトを変更したい場合、
① 現在のSizerに登録されているウィジェットをバックアップ
② Sizerをクリアする(ただしそこに属しているウィジェットは削除しない)
③ 新しいSizerを作成
④ バックアップしたウィジェットを新しいSizerに設定
⑤ 新しいSizerをセット
wxWindowList windowList; wxSizerItemList& itemList = currentSizer->GetChildren(); // 現在のSizerからウィジェットを取り出してリストに保存 for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) { wxSizerItem* item = *it; if (item->IsWindow()) { wxWindow* window = item->GetWindow(); windowList.push_back(window); } }
// Sizerをクリア currentSizer->Clear(false);
// 新しいSizerを作成 wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
// ウィジェットを新しいSizerに追加 for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) { wxWindow* window = *it; newSizer->Add(window, 1, wxALL | wxEXPAND, 0); }
// 親ウィジェットに新しいSizerを設定 panelB->SetSizer(newSizer);
// プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string>
// ウィンドウ作成 class MyFrame : public wxFrame { public: wxBoxSizer* sizerB; wxPanel* panelB; void PanelAB() { // パネルA,B作成 // 交換ボタン配置用のパネル wxPanel* panelA = new wxPanel(this, wxID_ANY, wxPoint(0, 0), wxSize(200, 100)); panelA->SetBackgroundColour(wxColour(255, 0, 0)); // このパネルにパネルB1,B2を配置 // これらのレイアウトを変更する panelB = new wxPanel(this, wxID_ANY, wxPoint(200, 0), wxSize(200, 100)); panelB->SetBackgroundColour(wxColour(0, 0, 255)); // パネルAにボタン作成 wxButton* buttonA = new wxButton(panelA, wxID_ANY, "ButtonA", wxPoint(10, 10), wxSize(100, 50)); // 画面を上下に分割し、上にpanelA、下にpanelBを配置 wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(panelA, 0, wxEXPAND); sizer->Add(panelB, 1, wxEXPAND); SetSizer(sizer); // ボタンAを押したときのイベント buttonA->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this); } void PanelB1B2() { // パネルBをsizerで左右に分割し、panelB1,panelB2を配置 sizerB = new wxBoxSizer(wxHORIZONTAL); wxPanel* panelB1 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100)); wxPanel* panelB2 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100)); panelB1->SetBackgroundColour(wxColour(255, 0, 255)); panelB2->SetBackgroundColour(wxColour(0, 255, 255)); sizerB->Add(panelB1, 1, wxEXPAND); sizerB->Add(panelB2, 1, wxEXPAND); panelB->SetSizer(sizerB); // パネルB1にボタン作成 wxButton* buttonB1 = new wxButton(panelB1, wxID_ANY, "ButtonB1", wxPoint(10, 10), wxSize(80, 50)); wxButton* buttonB2 = new wxButton(panelB2, wxID_ANY, "ButtonB2", wxPoint(10, 10), wxSize(80, 50)); } void PostCreate() { PanelAB(); PanelB1B2(); this->Layout(); // レイアウトの更新 }
// ボタンを押したときにSizerを交換 void OnButtonClicked(wxCommandEvent& event) { /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// wxBoxSizer* currentSizer = sizerB; if (currentSizer) { wxWindowList windowList;
wxSizerItemList& itemList = currentSizer->GetChildren(); // 現在のSizerからウィジェットを取り出してリストに保存 for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) { wxSizerItem* item = *it; if (item->IsWindow()) { wxWindow* window = item->GetWindow(); windowList.push_back(window); } }
// Sizerをクリア currentSizer->Clear(false);
// 新しいSizerを作成 wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
// ウィジェットを新しいSizerに追加 for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) { wxWindow* window = *it; newSizer->Add(window, 1, wxALL | wxEXPAND, 0); }
// 親ウィジェットに新しいSizerを設定 panelB->SetSizer(newSizer);
this->Layout(); // レイアウトの更新
sizerB = newSizer;
}
}
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } };
///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);

日本語や絵文字などが混同している場合、フォントを切り替えなければ文字を描画できない。
そこで、描画する文字列を書記素単位にし、書記素にスクリプト=用字=文字の種類の情報を付与して、スクリプトからフォントを引ける辞書を作成する。
#include <iostream> #include <vector> #include <string> #include <unordered_map> #include "u16_to_u32.hpp" #include "render_font.hpp" #pragma warning(disable:4996) //! @brief PBM(1byte,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P1\n%d\n%d\n", width, height); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d ", p[k] ? 0 : 1); k++; } fprintf(fp, "\n"); } fclose(fp); }
// フォントのオブジェクト struct FT_HB_Font { FT_Face *ftface; hb_font_t* hbfont; std::string lang; FT_HB_Font(FT_Face* face,const std::string& language){ ftface = face; // FreeTypeのフォントオブジェクト hbfont = hb_ft_font_create(*ftface, nullptr); // HarfBuzzのフォントオブジェクト lang = language; // HarfBuzz用の言語 } FT_HB_Font():ftface(nullptr),hbfont(nullptr){} void Delete(){ if (ftface) { FT_Done_Face(*ftface); ftface = nullptr; } if (hbfont) { hb_font_destroy(hbfont); hbfont = nullptr; } } };
int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // HarfBuzzのオブジェクト作成 hb_buffer_t* hbbuf; hbbuf = hb_buffer_create(); ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// // Script(用字)に応じたフォントをマップに登録 std::unordered_map< int, FT_HB_Font> script_map; script_map[HB_SCRIPT_HIRAGANA] = FT_HB_Font(my_LoadFonts(library, "C:\\Windows\\Fonts\\msgothic.ttc"),"jp"); script_map[HB_SCRIPT_HAN] = script_map[HB_SCRIPT_HIRAGANA]; script_map[HB_SCRIPT_KATAKANA] = script_map[HB_SCRIPT_HIRAGANA]; script_map[HB_SCRIPT_LATIN] = FT_HB_Font(my_LoadFonts(library, "C:\\Windows\\Fonts\\Times.ttf"),"en"); script_map[EMOJI] = FT_HB_Font(my_LoadFonts(library, "C:\\Windows\\Fonts\\seguiemj.ttf"),"en"); ////////////////////////////////////////////////////// // HarfBuzzのUnicode関数の取得 std::u16string text = u"漢字あ゙いう👨👨👧👦àbâáăã"; // 文字列をUTF32で表現した書記素の配列に変換 std::vector<Grapheme> graphemes; u16_to_u32(text.c_str(), graphemes); Image image(1024, 300); // 描画先をクリア std::fill(image.image.begin(), image.image.end(), 0); int draw_offset_x = 0; for (size_t i = 0; i < graphemes.size(); i++) { // 用字を取得 // ただし絵文字に関しては、HB_SCRIPT_COMMON が入っている hb_script_t hbscript = graphemes[i].script; FT_HB_Font& face = script_map.at(graphemes[i].is_emoji?EMOJI: hbscript); draw_offset_x = render_by_font( &image // 出力先 ,draw_offset_x // 描画開始位置 ,face.ftface // FreeTypeのフォント ,graphemes[i].u32str // 描画する文字列 ,hbscript // 描画する文字列の用字 ,hbbuf // HarfBuzzのバッファ ,face.hbfont // HarfBuzzのフォント ,face.lang // HarfBuzz用の言語 ); } pbmP1_Write("freetypetest_xxx.pbm", image.imageWidth, image.imageHeight, &image.image[0]); }
#include <unicode/ubrk.h> #include <unicode/ustring.h> #include <vector> #include <string> #include <hb.h> #include <hb-ft.h> #ifdef _DEBUG // デバッグ時リンク #pragma comment(lib, "icuucd.lib") #pragma comment(lib, "icuind.lib") #pragma comment(lib,"harfbuzz.lib") #else // 要リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icuin.lib") #pragma comment(lib,"harfbuzz.lib") #endif
// 絵文字判定関数 bool is_emoji(char32_t c32) { bool _emoji = false; _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI); _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_PRESENTATION); _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_MODIFIER); _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_MODIFIER_BASE); _emoji |= (bool)u_getIntPropertyValue(c32, UCHAR_EMOJI_COMPONENT); return _emoji; }
struct Grapheme{ int32_t start; // 元のutf16文字列のindex int32_t end; // 元のutf16文字列のindex hb_script_t script; // この書記素(書記素なので複数文字の可能性がある)の先頭の文字の書記素 std::u32string u32str; // この書記素のコードポイント表現 bool is_emoji; // この書記素が絵文字かどうかを表すフラグ };
//! @brief UTF-16からUTF-32で表現した書記素の配列を作成 //! @param [in] u16str UTF-16文字列 //! @param [out] graphemes 書記素の配列 bool u16_to_u32( const char16_t* u16str, std::vector<Grapheme>& graphemes) { // hb_unicode_scriptに渡す構造体(解放不要) hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default(); UErrorCode status = U_ZERO_ERROR; // イテレータ作成 UBreakIterator* bi = ubrk_open(UBRK_CHARACTER, nullptr /*文字単位の処理にロケールは不要*/, nullptr, 0, &status); if (U_FAILURE(status)) { return false;// エラーが発生 } int32_t utf16Length = wcslen((wchar_t*)u16str); // テキストを設定 ubrk_setText(bi, (const UChar*)u16str, utf16Length, &status); if (U_FAILURE(status)) { ubrk_close(bi); return false;// エラーが発生 } // 文字境界を使ってUTF-16からUTF-32に変換 int32_t start = ubrk_first(bi); // 文字列(utf16)の最後までループ for (int32_t end = ubrk_next(bi); end != UBRK_DONE; start = end, end = ubrk_next(bi)) { graphemes.push_back(Grapheme{ start, end }); int32_t i = start; bool is_first = true; while(i < end) { UChar32 c32; // ① u16str[i] 以降の文字を一文字コードポイントに変換してcに格納 // ② iを次の文字の位置に進める U16_NEXT(u16str, i, utf16Length, c32); // コードポイントを保存 graphemes.back().u32str.push_back(c32); if(is_first) { is_first = false; graphemes.back().script = hb_unicode_script(ufuncs, c32); // 用字の取得 graphemes.back().is_emoji = is_emoji(c32);// 絵文字かどうかのチェック } } } // 終了処理 ubrk_close(bi); return true; };
#pragma once #ifdef _DEBUG #pragma comment(lib,"freetyped.lib") #pragma comment(lib,"harfbuzz.lib") #else #pragma comment(lib,"freetype.lib") #pragma comment(lib,"harfbuzz.lib") #endif #include <ft2build.h> #include FT_FREETYPE_H #include <hb.h> #include <hb-ft.h> #include <vector> #include <string> constexpr int EMOJI = HB_TAG('0', '0', '0', '0');
struct Image { int imageWidth; int imageHeight; std::vector<unsigned char> image; Image(int w, int h) { image.resize(w * h); imageWidth = w; imageHeight = h; } //! @brief imageへの書き込み時のピクセル計算 int pixel_pos(const int x, const int y) { return y * imageWidth + x; } bool is_valid_area(int xx, int yy) { if (xx < 0)return false; if (yy < 0)return false; if (xx >= imageWidth)return false; if (yy >= imageHeight)return false; return true; } };
//! @brief imageへbmpの内容を書き込む //! @param [in] bmp 文字画像 //! @param [in] startx image画像内の書き込み開始位置 //! @param [in] starty image画像内の書き込み開始位置 void draw(Image* image, const FT_Bitmap& bmp, int startx, int starty) { int Width = bmp.width; int Height = bmp.rows; for (size_t y = 0; y < Height; y++) { for (size_t x = 0; x < Width; x++) { int xx = startx + x; int yy = starty + y; if( image->is_valid_area(xx, yy) == false)continue; if (bmp.buffer[y * Width + x]) { (image->image)[image->pixel_pos(xx, yy)] = 1; } } } }
//! @brief フォントの読込 FreeType2の処理 //! @param [in] library FT_Library //! @param [in] fontfile フォントファイルへのパス //! @return フェイスオブジェクト FT_Face* my_LoadFonts( FT_Library& library, const char* fontfile ) { FT_Face* face = new FT_Face; FT_Error error; // フォントファイル読み込み error = FT_New_Face( library, fontfile, 0, face ); //文字コード指定 error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return nullptr; else if (error) return nullptr; int pixel_size_y = 64; error = FT_Set_Pixel_Sizes( *face, 0, pixel_size_y); return face; }
//! @brief HarfBuzzで計算した座標を使いFreeType2で文字列を描画する //! @param [in] image 描画先 //! @param [in] hbbuf HarfBuzzオブジェクト //! @param [in] face FreeType2のフェイス int my_FaceDraw( Image* image, int draw_offset_x, hb_buffer_t* hbbuf, FT_Face* face ) { //文字数を格納 (書記素数ではない。例えば「あ゙」は2文字) unsigned int glyph_count; hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count); hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count); hb_position_t cursor_x = draw_offset_x; // 各文字ごとに描画する for (unsigned int i = 0; i < glyph_count; i++) { // codepointという変数名だが実際にはグリフインデクスが入っている hb_codepoint_t glyphid = glyph_info[i].codepoint; // 一文字分のオフセット。本来描画される位置からどれぐらいずれるか hb_position_t x_offset = glyph_pos[i].x_offset >> 6;// 結合文字の゛は本来の位置より左側に描画するので hb_position_t y_offset = glyph_pos[i].y_offset >> 6;// x_offsetにはマイナスの値が入る // 次の文字の描画開始位置までのピクセル数 hb_position_t x_advance = glyph_pos[i].x_advance >> 6; hb_position_t y_advance = glyph_pos[i].y_advance >> 6; /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// FT_Error error = FT_Load_Glyph(*face, glyphid, FT_LOAD_RENDER); if (error) { continue; } // 文字を画像化 FT_Render_Glyph((*face)->glyph, FT_RENDER_MODE_NORMAL); int cursor_y = 100; // 画像書き込み // オフセットを加えて座標調整する draw( image, (*face)->glyph->bitmap, cursor_x + x_offset + (*face)->glyph->bitmap_left, cursor_y + y_offset - (*face)->glyph->bitmap_top ); /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// // 次の文字の描画開始値 cursor_x += x_advance; cursor_y += y_advance; } return cursor_x; }
//! @brief 指定したフォントで文字列を描画する //! @param [out] image 出力先 //! @param [in] draw_offset_x 描画開始位置 //! @param [in] ft_face FreeTypeのフォント //! @param [in] text 描画する文字列 //! @param [in] script 描画する文字列の用字 //! @param [in] hbbuf HarfBuzzのバッファ //! @param [in] hbfont HarfBuzzのフォント //! @param [in] lang HarfBuzz用の言語 int render_by_font( Image* image, int draw_offset_x, FT_Face* ft_face, const std::u32string& text, hb_script_t script, hb_buffer_t* hbbuf, hb_font_t* hbfont, std::string lang ) { // バッファの内容をクリア // hb_buffer_tを再利用するときは、hb_buffer_clear_contents()でバッファをクリアする hb_buffer_clear_contents(hbbuf); // バッファにテキストを追加 hb_buffer_add_utf32(hbbuf, (const std::uint32_t*)text.data(), -1, 0, -1);// 描画したいテキストの設定 hb_buffer_set_direction(hbbuf, HB_DIRECTION_LTR); // 文字の方向を左から右として設定 hb_buffer_set_script(hbbuf, script); // Unicodeの用字(Script)として日本語を指定 hb_buffer_set_language(hbbuf, hb_language_from_string(lang.c_str(), -1));// 言語を設定 //////////////////////// hb_shape(hbfont, hbbuf, NULL, 0); //////////////////////// int next_pos = my_FaceDraw(image, draw_offset_x,hbbuf, ft_face);//FreeType2とHarfBuzzで文字列描画 //////////////////////// return next_pos; }


All In One WP Security & Firewal(AIOS)の機能により管理画面に入れなくなった。基本的には、
・一度AIOSを無効にする
・ログインする
・All In One WP Securityを有効にする
・ブロックリストに入っているIPを削除する
の手順で解決する。
無効化の方法は、とにかく一度WordPressがAIOSにアクセスができなくなればよいので、フォルダ名を変えるか、アクセス権限を000にする。
さくらのレンタルサーバーをつかっているので、コントロールパネルへログインし、ファイルマネージャでアクセス権を設定する。


ログインできたら、AIOSのアクセス権を元に戻し、再読み込みをする。
AIOSが無効になっているので、有効化する。

Locked IP addressesから、IPアドレスを選択し削除する。


ICUライブラリの uscript_getScript を使用すれば用字を取得できる。が、どうやらHarfBuzzのhb_unicode_script関数を使用しても可能らしい。
#include <iostream> #include <vector> #include <string> #include <hb.h> #include <hb-ft.h> #ifdef _DEBUG #pragma comment(lib,"harfbuzz.lib") #else #pragma comment(lib,"harfbuzz.lib") #endif #pragma warning(disable:4996)
// UTF-32の文字列の各文字のスクリプトを取得する関数 std::vector<hb_script_t> get_scripts_from_utf32(const std::u32string& text, hb_unicode_funcs_t* ufuncs) { std::vector<hb_script_t> scripts; // 出力用 for (char32_t ch : text) { hb_script_t script = hb_unicode_script(ufuncs, ch); // スクリプトの取得 scripts.push_back(script); } return scripts; }
int main() { ////////////////////////////////////////////////////// // HarfBuzzのUnicode関数の取得 hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default(); // hb_unicode_scriptに渡す構造体(解放不要) std::u32string text = U"漢字あ゙いう👨👨👧👦àbâáăã"; std::vector<hb_script_t> scripts = get_scripts_from_utf32(text, ufuncs); //////////////////////////////////////////// // テスト コードポイントとスクリプトのペアを作成 struct cs_pair { char32_t c; hb_script_t s; }; std::vector<cs_pair> uss; for (size_t i = 0; i < text.size(); i++) { uss.push_back(cs_pair{ text[i], scripts[i] }); } getchar(); }
デバッグモードで確認すると、文字に対応した用字が得られているのがわかる

icuライブラリのU16_NEXTでコードポイントを取得できる。
#include <unicode/ubrk.h> #include <unicode/ustring.h> #ifdef _DEBUG // デバッグ時リンク #pragma comment(lib, "icuucd.lib") #pragma comment(lib, "icuind.lib") #else // リリース用リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icuin.lib") #endif // Utf16文字列からUtf32文字列に変換 std::u32string u16_to_u32(const char16_t* u16str) { UErrorCode status = U_ZERO_ERROR; // イテレータ作成 UBreakIterator* bi = ubrk_open(UBRK_CHARACTER, nullptr /*文字単位の処理にロケールは不要*/, nullptr, 0, &status); if (U_FAILURE(status)) { return U"";// エラーが発生 } int32_t utf16Length = wcslen((wchar_t*)u16str); // テキストを設定 ubrk_setText(bi, (const UChar*)u16str, utf16Length, &status); if (U_FAILURE(status)) { ubrk_close(bi); return U"";// エラーが発生 } std::u32string ret32; // 文字境界を使ってUTF-16からUTF-32に変換 int32_t start = ubrk_first(bi); // 文字列(utf16)の最後までループ for (int32_t end = ubrk_next(bi); end != UBRK_DONE; start = end, end = ubrk_next(bi)) { int32_t i = start; while(i < end) { UChar32 c32; // ① u16str[i] 以降の文字を一文字コードポイントに変換してcに格納 // ② iを次の文字の位置に進める U16_NEXT(u16str, i, utf16Length, c32); // コードポイントを保存 ret32.push_back(c32); } } // 終了処理 ubrk_close(bi); return ret32; };
変換したところで結果を確認するまともな方法(エディタなど)が少ないので、先に作っておいた前回の render_by_font を使い、結果を出力する。
std::u32stringをFreeType2+HarfBuzzで描画する処理を関数化
#include"u16_to_u32.hpp" int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // HarfBuzzのオブジェクト作成 hb_buffer_t* hbbuf; hbbuf = hb_buffer_create(); ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // 出力先を作成 Image image(300, 150); ///////////////////////////////////////////// FaceInfo faces[3] = { FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\msgothic.ttc"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\seguiemj.ttf"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\Times.ttf"), HB_SCRIPT_LATIN, "en" ) }; ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// { std::u32string text32 = u16_to_u32(u"あ゙いう"); render_by_font( &image, faces[0].face, text32.c_str(), faces[0].script, hbbuf, faces[0].hbfont, faces[0].lang.c_str() ); pbmP1_Write("freetypetest_jp.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } { std::u32string text32 = u16_to_u32(u"👨👨👧👦"); render_by_font( &image, faces[1].face, text32.c_str(), faces[1].script, hbbuf, faces[1].hbfont, faces[1].lang.c_str() ); pbmP1_Write("freetypetest_mj.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } { std::u32string text32 = u16_to_u32(u"àbâáăã"); render_by_font( &image, faces[2].face, text32.c_str(), faces[2].script, hbbuf, faces[2].hbfont, faces[2].lang.c_str() ); pbmP1_Write("freetypetest_la.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } for(auto& f : faces){ f.Delete(); } ///////////////////////////////////////////// ///////////////////////////////////////////// ///////////////////////////////////////////// // HarfBuzzのオブジェクト破棄 hb_buffer_destroy(hbbuf); // FreeType2の解放 FT_Done_FreeType(library); }
諸事情により、以前作成したFreeType2+HarfBuzzのコードを関数化する。
HarfBuzzを使いまわす場合、バッファのクリアが必要となり、hb_buffer_clear_contentsを使用する。
#pragma once #ifdef _DEBUG #pragma comment(lib,"freetyped.lib") #pragma comment(lib,"harfbuzz.lib") #else #pragma comment(lib,"freetype.lib") #pragma comment(lib,"harfbuzz.lib") #endif #include <ft2build.h> #include FT_FREETYPE_H #include <hb.h> #include <hb-ft.h>
struct Image { int imageWidth; int imageHeight; std::vector<unsigned char> image; Image(int w, int h) { image.resize(w * h); imageWidth = w; imageHeight = h; } //! @brief imageへの書き込み時のピクセル計算 int pixel_pos(const int x, const int y) { return y * imageWidth + x; } bool is_valid_area(int xx, int yy) { if (xx < 0)return false; if (yy < 0)return false; if (xx >= imageWidth)return false; if (yy >= imageHeight)return false; return true; } };
//! @brief imageへbmpの内容を書き込む //! @param [in] bmp 文字画像 //! @param [in] startx image画像内の書き込み開始位置 //! @param [in] starty image画像内の書き込み開始位置 void draw(Image* image, const FT_Bitmap& bmp, int startx, int starty) { int Width = bmp.width; int Height = bmp.rows; for (size_t y = 0; y < Height; y++) { for (size_t x = 0; x < Width; x++) { int xx = startx + x; int yy = starty + y; if( image->is_valid_area(xx, yy) == false)continue; if (bmp.buffer[y * Width + x]) { (image->image)[image->pixel_pos(xx, yy)] = 1; } } } }
//! @brief フォントの読込 FreeType2の処理 //! @param [in] library FT_Library //! @param [in] fontfile フォントファイルへのパス //! @return フェイスオブジェクト FT_Face* my_LoadFonts( FT_Library& library, const char* fontfile ) { FT_Face* face = new FT_Face; FT_Error error; // フォントファイル読み込み error = FT_New_Face( library, fontfile, 0, face ); //文字コード指定 error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return nullptr; else if (error) return nullptr; int pixel_size_y = 64; error = FT_Set_Pixel_Sizes( *face, 0, pixel_size_y); return face; }
//! @brief HarfBuzzで計算した座標を使いFreeType2で文字列を描画する //! @param [in] image 描画先 //! @param [in] hbbuf HarfBuzzオブジェクト //! @param [in] face FreeType2のフェイス void my_FaceDraw(Image* image, hb_buffer_t* hbbuf, FT_Face* face) { // 描画先をクリア std::fill(image->image.begin(), image->image.end(), 0); //文字数を格納 (書記素数ではない。例えば「あ゙」は2文字) unsigned int glyph_count; hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count); hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count); hb_position_t cursor_x = 0; hb_position_t cursor_y = 0; // 各文字ごとに描画する for (unsigned int i = 0; i < glyph_count; i++) { // codepointという変数名だが実際にはグリフインデクスが入っている hb_codepoint_t glyphid = glyph_info[i].codepoint; // 一文字分のオフセット。本来描画される位置からどれぐらいずれるか hb_position_t x_offset = glyph_pos[i].x_offset >> 6;// 結合文字の゛は本来の位置より左側に描画するので hb_position_t y_offset = glyph_pos[i].y_offset >> 6;// x_offsetにはマイナスの値が入る // 次の文字の描画開始位置までのピクセル数 hb_position_t x_advance = glyph_pos[i].x_advance >> 6; hb_position_t y_advance = glyph_pos[i].y_advance >> 6; /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// FT_Error error = FT_Load_Glyph(*face, glyphid, FT_LOAD_RENDER); if (error) { continue; } // 文字を画像化 FT_Render_Glyph((*face)->glyph, FT_RENDER_MODE_NORMAL); // 画像書き込み // オフセットを加えて座標調整する draw( image, (*face)->glyph->bitmap, cursor_x + x_offset + (*face)->glyph->bitmap_left, cursor_y + y_offset - (*face)->glyph->bitmap_top + 100 ); /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// // 次の文字の描画開始値 cursor_x += x_advance; cursor_y += y_advance; } }
void render_by_font( Image* image, FT_Face* ft_face, const std::u32string& text, hb_script_t script, hb_buffer_t* hbbuf, hb_font_t* hbfont, std::string lang ) { // バッファの内容をクリア // hb_buffer_tを再利用するときは、hb_buffer_clear_contents()でバッファをクリアする hb_buffer_clear_contents(hbbuf); // バッファにテキストを追加 hb_buffer_add_utf32(hbbuf, (const std::uint32_t*)text.data(), -1, 0, -1);// 描画したいテキストの設定 hb_buffer_set_direction(hbbuf, HB_DIRECTION_LTR); // 文字の方向を左から右として設定 hb_buffer_set_script(hbbuf, script); // Unicodeの用字(Script)として日本語を指定 hb_buffer_set_language(hbbuf, hb_language_from_string(lang.c_str(), -1));// 言語として日本語を設定 //////////////////////// hb_shape(hbfont, hbbuf, NULL, 0); //////////////////////// my_FaceDraw(image,hbbuf, ft_face);//FreeType2とHarfBuzzで文字列描画 //////////////////////// }
#include <iostream> #include <vector> #include <fstream> #include <filesystem> #include <unordered_map> #include"render_font.hpp" #pragma warning(disable:4996) #include <string> #include <array> //! @brief PBM(1byte,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p);
//! @brief フォント情報を管理 struct FaceInfo { FT_Face* face; // フェイス(Freetype) hb_font_t* hbfont; // フェイス(HarfBuzz) hb_script_t script; // Unicodeの用字(=Script) std::string lang; // HarfBuzzに指定する言語 FaceInfo(FT_Face* f, hb_script_t s, const char* l) : face(f), script(s), lang(l) { // フォントオブジェクト作成 hbfont = hb_ft_font_create(*face, nullptr); } FaceInfo(FaceInfo&& fr){ face = fr.face; script = fr.script; lang = fr.lang; hbfont = fr.hbfont; fr.face = nullptr; fr.hbfont = nullptr; } // フォント破棄 void Delete() { if (hbfont) hb_font_destroy(hbfont); if (face) FT_Done_Face(*face); } };
int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // HarfBuzzのオブジェクト作成 hb_buffer_t* hbbuf; hbbuf = hb_buffer_create(); ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // 出力先を作成 Image image(300, 150); ///////////////////////////////////////////// // フォント情報作成 FaceInfo faces[3] = { FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\msgothic.ttc"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\seguiemj.ttf"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\Times.ttf"), HB_SCRIPT_LATIN, "en" ) }; ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// { // フォントを使って文字列を描画 render_by_font( &image, faces[0].face, U"あ゙いう", faces[0].script, hbbuf, faces[0].hbfont, faces[0].lang.c_str() ); pbmP1_Write("freetypetest_jp.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } { // フォントを使って文字列を描画 render_by_font( &image, faces[1].face, U"👨👨👧👦", faces[1].script, hbbuf, faces[1].hbfont, faces[1].lang.c_str() ); pbmP1_Write("freetypetest_mj.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } { // フォントを使って文字列を描画 render_by_font( &image, faces[2].face, U"àbâáăã", faces[2].script, hbbuf, faces[2].hbfont, faces[2].lang.c_str() ); pbmP1_Write("freetypetest_la.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } for(auto& f : faces){ f.Delete(); } ///////////////////////////////////////////// ///////////////////////////////////////////// ///////////////////////////////////////////// // HarfBuzzのオブジェクト破棄 hb_buffer_destroy(hbbuf); // FreeType2の解放 FT_Done_FreeType(library); } //! @brief PBM(1byte,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P1\n%d\n%d\n", width, height); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d ", p[k] ? 0 : 1); k++; } fprintf(fp, "\n"); } fclose(fp); }



WSL2+Pythonでプログラムを書いた。これをWindows 11上でアイコンをダブルクリックして起動したい。
基本の環境を汚さないようにするため、Ubuntuをもう一つ入れてそちらで実行できるようにする。

ユーザ名、パスワードを入力した後、コンソールをもう一つ開いてwsl -l -v を実行すると、有効な環境一覧が得られる。
環境が分かったら、 wsl --set-default <環境名> で、使用する環境を切り替える。

wsl -d <環境名> で、環境に入れる
以下のコードをtest.pyとして保存。
import tkinter as tk from tkinter import messagebox # クリックイベント def on_button_click(): messagebox.showinfo("Message", "HelloWorld") # メインウィンドウの作成 root = tk.Tk() root.title("MessageBox") root.geometry("200x100") # ボタンの作成 button = tk.Button(root, text="Click Me!", command=on_button_click) button.pack(pady=20) # ウィンドウの表示 root.mainloop()

バッチファイルを作成し、以下を記述
-d オプションで使用する環境を指定。
python3 の後は自分のスクリプトが置いてあるパスを指定。
Ubuntu 22.04 LTS +Firefox 129.0.2 で動作確認。ちゃんと動いた。

Pybind11を使ってみる。
PYBIND11_MODULE(pydファイル名, 変数名 ) {
変数名 . def (
"Python側の関数名" ,
&C++側の関数名
);
}
// 呼び出し側のPythonのバージョンのライブラリを使用する
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11; int my_add_func(int a, int b) { return a + b; } // PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { m.def( "call_in_python", // Python側で呼び出す関数名 &my_add_func, // C++側の関数名 py::arg("a"), // Python側での引数名(指定省略可) py::arg("b") // Python側での引数名(指定省略可) ); }
import my_module_name val = my_module_name.call_in_python(10,2) print(val)
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11;
std::tuple<int,float> my_calc_func(int a, int b) { return std::make_tuple( a + b, (float)(a) / (float)(b) ); }
// PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { m.def( "call_in_python", // Python側で呼び出す関数名 &my_calc_func, // C++側の関数名 py::arg("a"), // Python側での引数名(指定省略可) py::arg("b") // Python側での引数名(指定省略可) ); }
import my_module_name val1,val2 = my_module_name.call_in_python(10,2) print(val1) print(val2)
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11;
class MyClass { std::string m_str; int value; public: MyClass(std::string str,int val) : m_str(str),value(val) {} std::string get() { return m_str + " " + std::to_string(value); } void set(std::string str) { m_str = str; } };
std::string version() { return "1.0.0"; }
// PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { // クラスを追加 pybind11::class_<MyClass>(m, "MyClassInCPP") // クラス名を指定 PythonからMyClassInCPPとしてアクセス .def(pybind11::init<std::string,int>()) // コンストラクタを指定 .def("get_str", &MyClass::get) // メンバ関数を追加。Pythonからget_strとしてアクセス .def("set_str", &MyClass::set); // 関数を追加 m.def("get_version", &version, "get version function"); }
import my_module_name ss = my_module_name.MyClassInCPP("hello",15) print("get_str " , ss.get_str() ) ss.set_str("string") print("get_str " , ss.get_str() ) print("version:" , my_module_name.get_version() )