例えば ( -0.1 , 0.1 ) というピクセル座標は書き込まれる際には(0,0)というピクセルになってしまうので、小数点以下の計算結果を目視で確認できない。これをできるクラスを作った。
#pragma once #include <vector> #include <array> struct rgb_t { std::array<unsigned char, 3> data; }; // 実数座標と色情報 struct dpixel_t { double x, y; rgb_t c; dpixel_t(double x_, double y_, rgb_t c_) : x(x_), y(y_), c(c_) {} }; // 一つのピクセル。 // そのピクセルの色と、そのピクセル内に入った実数座標の点が記録できる struct pixel_t { rgb_t color; std::vector< dpixel_t > points; public: bool plot(const double x, const double y,const rgb_t rgba) { if (abs(x) > 0.5) return false; points.emplace_back(x,y, rgba); } void set(rgb_t c) { color = c; } size_t count()const { return points.size(); } }; // 実数型のピクセルを書き込める画像データ型 class CRealPixel { int _width; int _height; std::vector< pixel_t > _img; public: // 画像のピクセル数(整数)を指定 void set_size(int width_,int height_) { _width = width_; _height = height_; _img.resize(_width * _height); } // 画像のピクセル数を取得 size_t size()const { return _width * _height; } //画素へ書き込み(整数のIndex) rgb_t& operator[](const size_t index) { return _img[index].color; } // 画素を取得(二次元) rgb_t& get(const int x, const int y) { return operator[](y * _width + x); } // 実数座標の頂点を追加 void plot(const double x_, const double y_, rgb_t c_) { int ix = round(x_); int iy = round(y_); double x = x_ - ix; double y = y_ - iy; _img[iy * _width + ix].plot(x, y, c_); } // RGBの画像を作成 // まず画素の色で塗りつぶし、その中に実数座標の頂点をプロットする // img_ ... 結果の出力先。RGBの配列。 // ratio_ ... 1ピクセルを何ピクセルで表現するか // width ... img_の画素数。 width = _width * ratio_; // height ... img_の画素数。 height = _height * ratio_; void make_int(std::vector<rgb_t>& img_,int ratio_,int*width,int*height) { int rwidth = _width * ratio_; int rheight = _height * ratio_; *width = rwidth; *height = rheight; img_.resize(rwidth * rheight); for (size_t x = 0; x < _width; x++) { for (size_t y = 0; y < _height; y++) { // このブロックを全着色 for (size_t ax = x*ratio_; ax < (x+1)*ratio_; ax++) { for (size_t ay = y * ratio_; ay < (y + 1) * ratio_; ay++) { img_[ay * rwidth + ax] = _img[y * _width + x].color; } } // このブロック内の小数点以下のピクセルをプロット for (size_t j = 0; j < _img[y * _width + x].count(); j++) { double dx = _img[y * _width + x].points[j].x; double dy = _img[y * _width + x].points[j].y; //このブロックの中央 dx += 0.5; dy += 0.5; int rx = x * ratio_ + (ratio_ * dx); int ry = y * ratio_ + (ratio_ * dy); img_[ry * rwidth + rx] = _img[y * _width + x].points[j].c; } } } } };
#include <iostream> #pragma warning(disable:4996) #include "CRealPixel.hpp" //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// void Bresenham( CRealPixel& img, const int width, const int height, const int sx, const int sy, const int ex, const int ey, const rgb_t color ); //////////////////////////////////////////////////////// void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p, const int vmax ); int main() { int width = 100; int height = 40; CRealPixel crp; crp.set_size(width, height); // クリア for (size_t i = 0; i < crp.size(); i++) { crp[i] = rgb_t{ 255,255,255 }; } // ブレゼンハムで書き込み int sx = 5; int sy = 7; int ex = 90; int ey = 30; Bresenham(crp, width, height, sx, sy, ex, ey, { 0,0,0 }); // 浮動小数点型で書き込み double dx = ex - sx; double dy = ey - sy; double a = dy / dx; double b = 7 - a * 5; for (float fx = sx; fx < ex; fx+=0.1) { float fy = a * fx + b; if (round(fx) >= width)break; if (round(fy) >= height)break; crp.plot(fx,fy, { 255,0,0 }); } // ピクセル単位で書き込まれた内容と、実数単位で書き込まれた内容を // RGBの画像へ変換 std::vector<rgb_t> ii; int w, h; crp.make_int(ii, 5, &w, &h); // 変換した画像をファイル保存 ppmP3_write( "aaa.ppm", w, h, &ii[0].data[0], 255 ); } ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// //! @brief ブレゼンハムの直線描画 //! @param [out] img 画像データ(一次元配列)へのポインタ //! @param [in] width 画像の幅(画素数) //! @param [in] height 画像の高さ(画素数) //! @param [in] sx 線の始点X //! @param [in] sy 線の始点Y //! @param [in] ex 線の終点X //! @param [in] ey 線の終点Y //! @param [in] color 線の色 void Bresenham( CRealPixel& img, const int width, const int height, const int sx, const int sy, const int ex, const int ey, const rgb_t color ) { const int dx = std::abs(ex - sx); const int dy = std::abs(ey - sy); const int nx = (sx < ex) ? 1 : -1; const int ny = (sy < ey) ? 1 : -1; int err = dx - dy; int x = sx; int y = sy; while (1) { if (x >= 0 && y >= 0 && x < width && y < height) { img[y * width + x] = color; } if (x == ex && y == ey) break; const int e2 = 2 * err; if (e2 > -dy) { err = err - dy; x += nx; } if (e2 < dx) { err = err + dx; y += ny; } } } // // // ///////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p, const int vmax ) { FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax); 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 %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2] ); k++; } fprintf(fp, "\n"); } fclose(fp); }
tを適当にstepを決めて進めてその頂点をブレゼンハムなどで結ぶと折れ線になってしまうので、なめらかな曲線にできないか検討した。
Bezierの式はこうなっている。
これを展開してまとめなおしてみる。
すると普通の二次方程式にできる。P1,P2,P3は定数なので、pが分かれば解の公式でtを求められる。
仮にある点pのピクセルが点灯したとして、その次の点は、pの周辺の8点のどれかであるから、例えばp.x-1 , p.x , p.x+1 を使って 解の公式を使えば、p.xがその値の時のtが得られる。
本当は連立方程式なりなんなりを解けばいいのかもしれないが、私にそんな知恵はないので、p.x-1,p.x,p.x+1 , p.y-1,p.y,p.y+1 のそれぞれに対応するtを求め、一番直前のピクセルに近い距離のものを選ぶ。
ただし二次方程式なので、解が虚数になることがある。この場合は対象外。さらに解が二つ出てきたら、どちらかが「現在のtの値」より小さい、つまりバックしてしまう点なので、そうならないほうを選ぶ。
#pragma warning(disable:4996) #include <iostream> #include <array> #include <vector> #include <optional> using rgb_t = std::array<unsigned char, 3>; struct Image_t { int width; int height; std::vector<rgb_t> data; Image_t(int _width_, int _height_) { width = _width_; height = _height_; data = std::vector<rgb_t>(width * height); } inline int pos(const int x, const int y)const { return y * width + x; } inline bool valid(const int x, const int y)const { if (x < 0)return false; if (y < 0)return false; if (x >= width)return false; if (y >= height)return false; } }; void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p ); struct coord_t { int x, y; bool operator==(const coord_t& src) { return x == src.x && y == src.y; } bool operator!=(const coord_t& src) { return x != src.x || y != src.y; } }; struct coordd_t { double x, y; }; //! @brief 画像をクリア //! @param [in,out] img クリアする画像 //! @param [in] color クリアする色 //! @return なし void fill(Image_t& img, const rgb_t color) { std::fill(img.data.begin(), img.data.end(), color); } //! @brief 大きさのある点(矩形)を書き込む //! @param [in,out] img 書き込む画像 //! @param [in] pos 書き込む位置 //! @param [in] color 書き込む色 //! @param [in] size 点の大きさ //! @return なし void plot(Image_t& img, const coord_t pos, const rgb_t color, const int size) { int bx = pos.x - size / 2; int ax = bx + size; int by = pos.y - size / 2; int ay = by + size; for (size_t x = bx; x < ax; x++) { for (size_t y = by; y < ay; y++) { if (img.valid(x, y)) { img.data[img.pos(x, y)] = color; } } } } struct QuadraticEquation { double plus; double minus; };
//! @brief Bezierのtを求める関数 //! @param [in] P1 最初の点の座標 //! @param [in] P2 ハンドルの点の座標 //! @param [in] P3 最後の点の座標 //! @param [in] p求めたい点の座標 //! @return pを得るためのtの値 値は零個~最大二個。零個の時はstd::nulloptを返す std::optional<QuadraticEquation> BezierInverse(double P1,double P2,double P3,double p) { double a = P1 - 2 * P2 + P3; double b = 2 * (-P1 + P2); double c = P1 - p; double test = pow(b, 2) - 4 * a * c; // (-b +- sqrt(test)) / (2*a) double plus,minus; if (test < 0) { //sqrtの中が負数なら虚数なので解をなしとする return std::nullopt; } else { plus = (-b + sqrt(test)) / (2 * a); minus = (-b - sqrt(test)) / (2 * a); } return QuadraticEquation{ plus,minus }; }
coordd_t BezierXY(coord_t P1, coord_t P2, coord_t P3, double t) { double ti = 1.0 - t; coordd_t dP; dP.x = pow(ti, 2) * P1.x + 2 * ti * t * P2.x + pow(t, 2) * P3.x; dP.y = pow(ti, 2) * P1.y + 2 * ti * t * P2.y + pow(t, 2) * P3.y; return dP; }
//! @brief Bézier Curve //! @param [in,out] img 書き込む画像 //! @param [in] 制御点三点 //! @param [in] color 書き込む色 //! @return なし //! @sa https://ja.javascript.info/bezier-curve void Bezier(Image_t& img, const std::array<coord_t, 3>& points, const rgb_t& color) { const int step = 30; coord_t P1 = points[0]; coord_t P2 = points[1]; coord_t P3 = points[2]; coord_t Pbefore = P1; //最初の一点をプロット if (img.valid(P1.x, P1.y)) { plot(img, P1, color, 1); } // tの候補を確認 std::vector< double > cands; double t = 0.0; while (t < 1.0 && Pbefore!=P3){ coord_t P = Pbefore; double derror = (std::numeric_limits<double>::max)(); double rett = 100; coord_t Pn; coord_t tmp; coordd_t cd; cands.clear(); // 現在の頂点の周辺 for (int x = -1; x <= 1; x++) { auto tx = BezierInverse(P1.x, P2.x, P3.x, P.x + x); if (tx != std::nullopt) { cands.push_back(tx.value().plus); cands.push_back(tx.value().minus); } } for (int y = -1; y <= 1; y++) { auto ty = BezierInverse(P1.y, P2.y, P3.y, P.y + y); if (ty != std::nullopt) { cands.push_back(ty.value().plus); cands.push_back(ty.value().minus); } } for (auto tt : cands) { // 直前の点を描いたtより大きく、 // かつ0.0~1.0に収まっている場合 if (tt > t && tt > 0.0 && tt <= 1.0 ) { cd = BezierXY(P1, P2, P3, tt); tmp.x = round(cd.x); tmp.y = round(cd.y); if (tmp != P) { double d = pow(cd.x - P.x, 2) + pow(cd.y - P.y, 2); if (d < derror) { Pn = tmp; derror = d; rett = tt; } } } } t = rett; if (img.valid(Pn.x, Pn.y)) { plot(img, Pn, color, 1); } else { printf("%d %d", Pn.x, Pn.y); } Pbefore = Pn; } }
int main() { Image_t img(400, 250); fill(img, rgb_t{ 255,255,255 }); std::array<coord_t, 3> vcd; vcd[2] = { 50,20 }; vcd[1] = { 200,200 }; vcd[0] = { 300,60 }; plot(img, vcd[0], rgb_t{ 125,0,0 }, 5);// ハンドルを描画 plot(img, vcd[1], rgb_t{ 125,0,0 }, 5); plot(img, vcd[2], rgb_t{ 125,0,0 }, 5); Bezier(img, vcd, rgb_t{ 0,0,255 });// ベジェ曲線を描く ppmP3_write("abc.ppm", img.width, img.height, (unsigned char*)img.data.data()); } //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p ) { FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); 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 %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2] ); k++; } fprintf(fp, "\n"); } fclose(fp); }
#pragma warning(disable:4996) #include <iostream> #include "Bresenham.hpp" #include <array> #include <vector> using rgb_t = std::array<unsigned char, 3>; struct Image_t { int width; int height; std::vector<rgb_t> data; Image_t(int _width_, int _height_) { width = _width_; height = _height_; data = std::vector<rgb_t>(width * height); } inline int pos(const int x, const int y)const { return y * width + x; } inline bool valid(const int x, const int y)const { if (x < 0)return false; if (y < 0)return false; if (x >= width)return false; if (y >= height)return false; } }; void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p ); struct coord_t { int x, y; }; void Bresenham( Image_t& img, const coord_t s, const coord_t e, const rgb_t color ) { Bresenham(img.data.data(), img.width, img.height, s.x, s.y, e.x, e.y, color); } //! @brief 画像をクリア //! @param [in,out] img クリアする画像 //! @param [in] color クリアする色 //! @return なし void fill(Image_t& img, const rgb_t color) { std::fill(img.data.begin(), img.data.end(), color); }
//! @brief 大きさのある点(矩形)を書き込む //! @param [in,out] img 書き込む画像 //! @param [in] pos 書き込む位置 //! @param [in] color 書き込む色 //! @param [in] size 点の大きさ //! @return なし void plot(Image_t& img, const coord_t pos, const rgb_t color, const int size) { int bx = pos.x - size / 2; int ax = bx + size; int by = pos.y - size / 2; int ay = by + size; for (size_t x = bx; x < ax; x++) { for (size_t y = by; y < ay; y++) { if (img.valid(x, y)) { img.data[img.pos(x, y)] = color; } } } }
//! @brief Bézier Curve //! @param [in,out] img 書き込む画像 //! @param [in] 制御点三点 //! @param [in] color 書き込む色 //! @return なし //! @sa https://ja.javascript.info/bezier-curve void Bezier(Image_t& img,const std::array<coord_t,3>& points, const rgb_t& color) { const int step=30; coord_t P1 = points[0]; coord_t P2 = points[1]; coord_t P3 = points[2]; for (size_t s = 0; s < step; s++) { double t = s / (double)step; double ti = 1.0 - t; coord_t P; P.x = pow(ti, 2) * P1.x + 2 * ti * t * P2.x + pow(t, 2) * P3.x; P.y = pow(ti, 2) * P1.y + 2 * ti * t * P2.y + pow(t, 2) * P3.y; if (img.valid(P.x, P.y)) { plot(img, P, color, 3); } } }
int main() { Image_t img(400,300); fill(img, rgb_t{ 255,255,255 }); std::array<coord_t,3> vcd; vcd[0] = { 50,20 }; vcd[1] = {200,200}; vcd[2] = { 300,60 }; Bezier(img, vcd, rgb_t{ 0,0,255 });// ベジェ曲線を描く Bresenham(img, vcd[0], vcd[1], rgb_t{ 0,0,0 });// ブレゼンハムでハンドルを結ぶ線を描く Bresenham(img, vcd[2], vcd[1], rgb_t{ 0,0,0 }); plot(img, vcd[0], rgb_t{ 125,0,0 }, 5);// ハンドルを描画 plot(img, vcd[1], rgb_t{ 125,0,0 }, 5); plot(img, vcd[2], rgb_t{ 125,0,0 }, 5); ppmP3_write("abc.ppm", img.width, img.height, (unsigned char*)img.data.data()); } //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p ) { FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); 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 %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2] ); k++; } fprintf(fp, "\n"); } fclose(fp); }
なお、ハンドルを結ぶ直線を描画するブレゼンハムは作り置きのものを使う。
SFNT Nameを調べるために作ったテーブルをせっかくなのでおいておく。
#pragma warning(disable:4996) #include <iostream> #include <ft2build.h> #include FT_FREETYPE_H #pragma comment(lib,"freetype.lib") #include "disp-sfnt-name.hpp" ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// // 各テーブル std::unordered_map<unsigned int, NameID> nameIDTable; std::unordered_map<unsigned int, LanguageID> languageIDTable; std::unordered_map<unsigned int, PlatformID> platformIDTable; std::unordered_map<unsigned int, EncodingID_Apple> encodingIDAppleTable; std::unordered_map<unsigned int, EncodingID_MACINTOSH> encodingIDMacTable; std::unordered_map<unsigned int, EncodingID_ISO> encodingIDISOTable; std::unordered_map<unsigned int, EncodingID_Microsoft> encodingIDMicrosoftTable; std::unordered_map<unsigned int, EncodingID_Adobe> encodingIDAdobeTable; ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// // 表示用関数 void disp_sfnt(const FT_SfntName& sname); ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// int main() { setlocale(LC_ALL, ""); //テーブルを作成 create_language_id_list(languageIDTable); create_name_id_list(nameIDTable); create_platform_id_list(platformIDTable); create_encoding_id_apple(encodingIDAppleTable); create_encoding_id_MAC(encodingIDMacTable); create_encoding_id_iso(encodingIDISOTable); create_encoding_id_Microsoft(encodingIDMicrosoftTable); create_encoding_id_Adobe(encodingIDAdobeTable); FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; FT_Face face; // handle to face object // フォントファイル読み込み error = FT_New_Face( library, //"C:\\Windows\\Fonts\\ALGER.ttf", //"C:\\Windows\\Fonts\\meiryo.ttc", "C:\\Windows\\Fonts\\msmincho.ttc", 0, &face ); FT_Int count = FT_Get_Sfnt_Name_Count(face); FT_SfntName sfnt_name; for (FT_Int i = 0; i < count; i++) { FT_Get_Sfnt_Name(face, i, &sfnt_name); switch (sfnt_name.platform_id) { case TT_PLATFORM_APPLE_UNICODE: if (0) { printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ; printf("%s / ", encodingIDAppleTable[sfnt_name.encoding_id].macro.c_str()); disp_sfnt(sfnt_name); } break; case TT_PLATFORM_MACINTOSH: if (0) { printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ; printf("%s / ", encodingIDMacTable[sfnt_name.encoding_id].macro.c_str()); disp_sfnt(sfnt_name); } break; case TT_PLATFORM_ISO: if (0) { printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ; printf("%s / ", encodingIDISOTable[sfnt_name.encoding_id].macro.c_str()); disp_sfnt(sfnt_name); } break; case TT_PLATFORM_MICROSOFT: if (1) { printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ; printf("%s / ", encodingIDMicrosoftTable[sfnt_name.encoding_id].macro.c_str()); disp_sfnt(sfnt_name); } break; case TT_PLATFORM_CUSTOM: break; case TT_PLATFORM_ADOBE: if (0) { printf("%s / ", platformIDTable[sfnt_name.platform_id].macro.c_str()); ; printf("%s / ", encodingIDAdobeTable[sfnt_name.encoding_id].macro.c_str()); disp_sfnt(sfnt_name); } break; } } // FreeType2の解放 FT_Done_Face(face); FT_Done_FreeType(library); } void disp_sfnt( const FT_SfntName& sname) { printf("%s / ", nameIDTable[sname.name_id].macro.c_str()); printf("%s / ", languageIDTable[sname.language_id].Primary_Language.c_str()); std::string astr; std::wstring wstr; printf("::name=="); switch (sname.encoding_id) { case 0: for (size_t i = 0; i < sname.string_len; i++) { astr += sname.string[i]; } printf("%s", astr.c_str()); break; default: for (size_t i = 0; i < sname.string_len; i+=2) { char16_t w = *((char16_t*)&sname.string[i]); unsigned char* p = (unsigned char*)&w; // UTF16BEなので2バイトを反転する std::swap(p[0], p[1]); wstr += w; } printf("%ls", wstr.c_str()); } puts(""); }
#pragma once #include <unordered_map> //FT_Get_Sfnt_Name_Count #include<freetype/ftsnames.h> // プラットフォーム TT_PLATFORM_XXX の定義 #include<freetype/ttnameid.h> struct LanguageID { unsigned int LCID; std::string macro; std::string Primary_Language; std::string Region; }; struct NameID { int ID; std::string macro; }; struct PlatformID { int ID; std::string macro; }; struct EncodingID_Apple { int ID; std::string macro; }; struct EncodingID_MACINTOSH { int ID; std::string macro; }; struct EncodingID_ISO { int ID; std::string macro; }; struct EncodingID_Microsoft { int ID; std::string macro; }; struct EncodingID_Adobe { int ID; std::string macro; }; void create_platform_id_list(std::unordered_map<unsigned int, PlatformID>& m) { m[TT_PLATFORM_APPLE_UNICODE] = PlatformID{ TT_PLATFORM_APPLE_UNICODE,"TT_PLATFORM_APPLE_UNICODE" }; m[TT_PLATFORM_MACINTOSH] = PlatformID{ TT_PLATFORM_MACINTOSH ,"TT_PLATFORM_MACINTOSH" }; m[TT_PLATFORM_ISO] = PlatformID{ TT_PLATFORM_ISO ,"TT_PLATFORM_ISO" }; m[TT_PLATFORM_MICROSOFT] = PlatformID{ TT_PLATFORM_MICROSOFT ,"TT_PLATFORM_MICROSOFT" }; m[TT_PLATFORM_CUSTOM] = PlatformID{ TT_PLATFORM_CUSTOM ,"TT_PLATFORM_CUSTOM" }; m[TT_PLATFORM_ADOBE] = PlatformID{ TT_PLATFORM_ADOBE ,"TT_PLATFORM_ADOBE" }; } void create_language_id_list(std::unordered_map<unsigned int, LanguageID>& m) { m[TT_MS_LANGID_ENGLISH_PHILIPPINES] = LanguageID{ TT_MS_LANGID_ENGLISH_PHILIPPINES, "TT_MS_LANGID_ENGLISH_PHILIPPINES","English","Republic of the Philippines" }; m[TT_MS_LANGID_ARABIC_TUNISIA] = LanguageID{ TT_MS_LANGID_ARABIC_TUNISIA, "TT_MS_LANGID_ARABIC_TUNISIA","Arabic","Tunisia" }; m[TT_MS_LANGID_ARABIC_ALGERIA] = LanguageID{ TT_MS_LANGID_ARABIC_ALGERIA, "TT_MS_LANGID_ARABIC_ALGERIA","Arabic","Algeria" }; m[TT_MS_LANGID_ENGLISH_CARIBBEAN] = LanguageID{ TT_MS_LANGID_ENGLISH_CARIBBEAN, "TT_MS_LANGID_ENGLISH_CARIBBEAN","English","Caribbean" }; m[TT_MS_LANGID_SWEDISH_FINLAND] = LanguageID{ TT_MS_LANGID_SWEDISH_FINLAND, "TT_MS_LANGID_SWEDISH_FINLAND","Sweden","Finland" }; m[TT_MS_LANGID_ARABIC_EGYPT] = LanguageID{ TT_MS_LANGID_ARABIC_EGYPT, "TT_MS_LANGID_ARABIC_EGYPT","Arabic","Egypt" }; m[TT_MS_LANGID_ENGLISH_TRINIDAD] = LanguageID{ TT_MS_LANGID_ENGLISH_TRINIDAD, "TT_MS_LANGID_ENGLISH_TRINIDAD","English","Trinidad and Tobago" }; m[TT_MS_LANGID_BENGALI_BANGLADESH] = LanguageID{ TT_MS_LANGID_BENGALI_BANGLADESH, "TT_MS_LANGID_BENGALI_BANGLADESH","Bengali","Bangladesh" }; m[TT_MS_LANGID_ARABIC_SAUDI_ARABIA] = LanguageID{ TT_MS_LANGID_ARABIC_SAUDI_ARABIA, "TT_MS_LANGID_ARABIC_SAUDI_ARABIA","Arabic","Saudi Arabia" }; m[TT_MS_LANGID_ENGLISH_SINGAPORE] = LanguageID{ TT_MS_LANGID_ENGLISH_SINGAPORE, "TT_MS_LANGID_ENGLISH_SINGAPORE","English","Singapore" }; m[TT_MS_LANGID_ENGLISH_UNITED_KINGDOM] = LanguageID{ TT_MS_LANGID_ENGLISH_UNITED_KINGDOM, "TT_MS_LANGID_ENGLISH_UNITED_KINGDOM","English","United Kingdom" }; m[TT_MS_LANGID_ARABIC_OMAN] = LanguageID{ TT_MS_LANGID_ARABIC_OMAN, "TT_MS_LANGID_ARABIC_OMAN","Arabic","Oman" }; m[TT_MS_LANGID_ENGLISH_ZIMBABWE] = LanguageID{ TT_MS_LANGID_ENGLISH_ZIMBABWE, "TT_MS_LANGID_ENGLISH_ZIMBABWE","English","Zimbabwe" }; m[TT_MS_LANGID_ISIZULU_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ISIZULU_SOUTH_AFRICA, "TT_MS_LANGID_ISIZULU_SOUTH_AFRICA","isiZulu","South Africa" }; m[TT_MS_LANGID_ARABIC_MOROCCO] = LanguageID{ TT_MS_LANGID_ARABIC_MOROCCO, "TT_MS_LANGID_ARABIC_MOROCCO","Arabic","Morocco" }; m[TT_MS_LANGID_ARABIC_LIBYA] = LanguageID{ TT_MS_LANGID_ARABIC_LIBYA, "TT_MS_LANGID_ARABIC_LIBYA","Arabic","Libya" }; m[TT_MS_LANGID_ENGLISH_JAMAICA] = LanguageID{ TT_MS_LANGID_ENGLISH_JAMAICA, "TT_MS_LANGID_ENGLISH_JAMAICA","English","Jamaica" }; m[TT_MS_LANGID_CZECH_CZECH_REPUBLIC] = LanguageID{ TT_MS_LANGID_CZECH_CZECH_REPUBLIC, "TT_MS_LANGID_CZECH_CZECH_REPUBLIC","Czech","Czech Republic" }; m[TT_MS_LANGID_BENGALI_INDIA] = LanguageID{ TT_MS_LANGID_BENGALI_INDIA, "TT_MS_LANGID_BENGALI_INDIA","Bengali","India" }; m[TT_MS_LANGID_ARABIC_IRAQ] = LanguageID{ TT_MS_LANGID_ARABIC_IRAQ, "TT_MS_LANGID_ARABIC_IRAQ","Arabic","Iraq" }; m[TT_MS_LANGID_INDONESIAN_INDONESIA] = LanguageID{ TT_MS_LANGID_INDONESIAN_INDONESIA, "TT_MS_LANGID_INDONESIAN_INDONESIA","Indonesian","Indonesia" }; m[TT_MS_LANGID_ENGLISH_AUSTRALIA] = LanguageID{ TT_MS_LANGID_ENGLISH_AUSTRALIA, "TT_MS_LANGID_ENGLISH_AUSTRALIA","English","Australia" }; m[TT_MS_LANGID_ARABIC_YEMEN] = LanguageID{ TT_MS_LANGID_ARABIC_YEMEN, "TT_MS_LANGID_ARABIC_YEMEN","Arabic","Yemen" }; m[TT_MS_LANGID_ENGLISH_INDIA] = LanguageID{ TT_MS_LANGID_ENGLISH_INDIA, "TT_MS_LANGID_ENGLISH_INDIA","English","India" }; m[TT_MS_LANGID_ARABIC_SYRIA] = LanguageID{ TT_MS_LANGID_ARABIC_SYRIA, "TT_MS_LANGID_ARABIC_SYRIA","Arabic","Syria" }; m[TT_MS_LANGID_ENGLISH_MALAYSIA] = LanguageID{ TT_MS_LANGID_ENGLISH_MALAYSIA, "TT_MS_LANGID_ENGLISH_MALAYSIA","English","Malaysia" }; m[TT_MS_LANGID_ENGLISH_UNITED_STATES] = LanguageID{ TT_MS_LANGID_ENGLISH_UNITED_STATES, "TT_MS_LANGID_ENGLISH_UNITED_STATES","English","United States" }; m[TT_MS_LANGID_ARABIC_JORDAN] = LanguageID{ TT_MS_LANGID_ARABIC_JORDAN, "TT_MS_LANGID_ARABIC_JORDAN","Arabic","Jordan" }; m[TT_MS_LANGID_ENGLISH_IRELAND] = LanguageID{ TT_MS_LANGID_ENGLISH_IRELAND, "TT_MS_LANGID_ENGLISH_IRELAND","English","Ireland" }; m[TT_MS_LANGID_ARABIC_LEBANON] = LanguageID{ TT_MS_LANGID_ARABIC_LEBANON, "TT_MS_LANGID_ARABIC_LEBANON","Arabic","Lebanon" }; m[TT_MS_LANGID_ENGLISH_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ENGLISH_SOUTH_AFRICA, "TT_MS_LANGID_ENGLISH_SOUTH_AFRICA","English","South Africa" }; m[TT_MS_LANGID_TIBETAN_PRC] = LanguageID{ TT_MS_LANGID_TIBETAN_PRC, "TT_MS_LANGID_TIBETAN_PRC","Tibetan","PRC" }; m[TT_MS_LANGID_ARABIC_KUWAIT] = LanguageID{ TT_MS_LANGID_ARABIC_KUWAIT, "TT_MS_LANGID_ARABIC_KUWAIT","Arabic","Kuwait" }; m[TT_MS_LANGID_ENGLISH_CANADA] = LanguageID{ TT_MS_LANGID_ENGLISH_CANADA, "TT_MS_LANGID_ENGLISH_CANADA","English","Canada" }; m[TT_MS_LANGID_ARABIC_UAE] = LanguageID{ TT_MS_LANGID_ARABIC_UAE, "TT_MS_LANGID_ARABIC_UAE","Arabic","U.A.E." }; m[TT_MS_LANGID_ENGLISH_NEW_ZEALAND] = LanguageID{ TT_MS_LANGID_ENGLISH_NEW_ZEALAND, "TT_MS_LANGID_ENGLISH_NEW_ZEALAND","English","New Zealand" }; m[TT_MS_LANGID_ARABIC_BAHRAIN] = LanguageID{ TT_MS_LANGID_ARABIC_BAHRAIN, "TT_MS_LANGID_ARABIC_BAHRAIN","Arabic","Bahrain" }; m[TT_MS_LANGID_ENGLISH_BELIZE] = LanguageID{ TT_MS_LANGID_ENGLISH_BELIZE, "TT_MS_LANGID_ENGLISH_BELIZE","English","Belize" }; m[TT_MS_LANGID_ARABIC_QATAR] = LanguageID{ TT_MS_LANGID_ARABIC_QATAR, "TT_MS_LANGID_ARABIC_QATAR","Arabic","Qatar" }; m[TT_MS_LANGID_OCCITAN_FRANCE] = LanguageID{ TT_MS_LANGID_OCCITAN_FRANCE, "TT_MS_LANGID_OCCITAN_FRANCE","Occitan","France" }; m[TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC] = LanguageID{ TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC, "TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC","Spanish","Dominican Republic" }; m[TT_MS_LANGID_BULGARIAN_BULGARIA] = LanguageID{ TT_MS_LANGID_BULGARIAN_BULGARIA, "TT_MS_LANGID_BULGARIAN_BULGARIA","Bulgarian","Bulgaria" }; m[TT_MS_LANGID_GERMAN_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_GERMAN_LUXEMBOURG, "TT_MS_LANGID_GERMAN_LUXEMBOURG","German","Luxembourg" }; m[TT_MS_LANGID_CATALAN_CATALAN] = LanguageID{ TT_MS_LANGID_CATALAN_CATALAN, "TT_MS_LANGID_CATALAN_CATALAN","Catalan","Catalan" }; m[TT_MS_LANGID_CHINESE_TAIWAN] = LanguageID{ TT_MS_LANGID_CHINESE_TAIWAN, "TT_MS_LANGID_CHINESE_TAIWAN","Chinese","Taiwan" }; m[TT_MS_LANGID_CHINESE_PRC] = LanguageID{ TT_MS_LANGID_CHINESE_PRC, "TT_MS_LANGID_CHINESE_PRC","Chinese","People’s Republic of China" }; m[TT_MS_LANGID_CHINESE_HONG_KONG] = LanguageID{ TT_MS_LANGID_CHINESE_HONG_KONG, "TT_MS_LANGID_CHINESE_HONG_KONG","Chinese","Hong Kong S.A.R." }; m[TT_MS_LANGID_YI_PRC] = LanguageID{ TT_MS_LANGID_YI_PRC, "TT_MS_LANGID_YI_PRC","Yi","PRC" }; m[TT_MS_LANGID_CHINESE_SINGAPORE] = LanguageID{ TT_MS_LANGID_CHINESE_SINGAPORE, "TT_MS_LANGID_CHINESE_SINGAPORE","Chinese","Singapore" }; m[TT_MS_LANGID_LAO_LAOS] = LanguageID{ TT_MS_LANGID_LAO_LAOS, "TT_MS_LANGID_LAO_LAOS","Lao","Lao P.D.R." }; m[TT_MS_LANGID_ITALIAN_SWITZERLAND] = LanguageID{ TT_MS_LANGID_ITALIAN_SWITZERLAND, "TT_MS_LANGID_ITALIAN_SWITZERLAND","Italian","Switzerland" }; m[TT_MS_LANGID_CHINESE_MACAO] = LanguageID{ TT_MS_LANGID_CHINESE_MACAO, "TT_MS_LANGID_CHINESE_MACAO","Chinese","Macao S.A.R." }; m[TT_MS_LANGID_SPANISH_VENEZUELA] = LanguageID{ TT_MS_LANGID_SPANISH_VENEZUELA, "TT_MS_LANGID_SPANISH_VENEZUELA","Spanish","Venezuela" }; m[TT_MS_LANGID_DANISH_DENMARK] = LanguageID{ TT_MS_LANGID_DANISH_DENMARK, "TT_MS_LANGID_DANISH_DENMARK","Danish","Denmark" }; m[TT_MS_LANGID_GERMAN_GERMANY] = LanguageID{ TT_MS_LANGID_GERMAN_GERMANY, "TT_MS_LANGID_GERMAN_GERMANY","German","Germany" }; m[TT_MS_LANGID_ARMENIAN_ARMENIA] = LanguageID{ TT_MS_LANGID_ARMENIAN_ARMENIA, "TT_MS_LANGID_ARMENIAN_ARMENIA","Armenian","Armenia" }; m[TT_MS_LANGID_GERMAN_SWITZERLAND] = LanguageID{ TT_MS_LANGID_GERMAN_SWITZERLAND, "TT_MS_LANGID_GERMAN_SWITZERLAND","German","Switzerland" }; m[TT_MS_LANGID_DUTCH_BELGIUM] = LanguageID{ TT_MS_LANGID_DUTCH_BELGIUM, "TT_MS_LANGID_DUTCH_BELGIUM","Dutch","Belgium" }; m[TT_MS_LANGID_GERMAN_AUSTRIA] = LanguageID{ TT_MS_LANGID_GERMAN_AUSTRIA, "TT_MS_LANGID_GERMAN_AUSTRIA","German","Austria" }; m[TT_MS_LANGID_GERMAN_LIECHTENSTEIN] = LanguageID{ TT_MS_LANGID_GERMAN_LIECHTENSTEIN, "TT_MS_LANGID_GERMAN_LIECHTENSTEIN","German","Liechtenstein" }; m[TT_MS_LANGID_IRISH_IRELAND] = LanguageID{ TT_MS_LANGID_IRISH_IRELAND, "TT_MS_LANGID_IRISH_IRELAND","Irish","Ireland" }; m[TT_MS_LANGID_GREEK_GREECE] = LanguageID{ TT_MS_LANGID_GREEK_GREECE, "TT_MS_LANGID_GREEK_GREECE","Greek","Greece" }; m[TT_MS_LANGID_SPANISH_EL_SALVADOR] = LanguageID{ TT_MS_LANGID_SPANISH_EL_SALVADOR, "TT_MS_LANGID_SPANISH_EL_SALVADOR","Spanish","El Salvador" }; m[TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT] = LanguageID{ TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT, "TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT","Spanish (Traditional Sort)","Spain" }; m[TT_MS_LANGID_MARATHI_INDIA] = LanguageID{ TT_MS_LANGID_MARATHI_INDIA, "TT_MS_LANGID_MARATHI_INDIA","Marathi","India" }; m[TT_MS_LANGID_SPANISH_MEXICO] = LanguageID{ TT_MS_LANGID_SPANISH_MEXICO, "TT_MS_LANGID_SPANISH_MEXICO","Spanish","Mexico" }; m[TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT] = LanguageID{ TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT, "TT_MS_LANGID_SPANISH_SPAIN_MODERN_SORT","Spanish (Modern Sort)","Spain" }; m[TT_MS_LANGID_SPANISH_GUATEMALA] = LanguageID{ TT_MS_LANGID_SPANISH_GUATEMALA, "TT_MS_LANGID_SPANISH_GUATEMALA","Spanish","Guatemala" }; m[TT_MS_LANGID_SYRIAC_SYRIA] = LanguageID{ TT_MS_LANGID_SYRIAC_SYRIA, "TT_MS_LANGID_SYRIAC_SYRIA","Syriac","Syria" }; m[TT_MS_LANGID_SPANISH_COSTA_RICA] = LanguageID{ TT_MS_LANGID_SPANISH_COSTA_RICA, "TT_MS_LANGID_SPANISH_COSTA_RICA","Spanish","Costa Rica" }; m[TT_MS_LANGID_SPANISH_PANAMA] = LanguageID{ TT_MS_LANGID_SPANISH_PANAMA, "TT_MS_LANGID_SPANISH_PANAMA","Spanish","Panama" }; m[TT_MS_LANGID_SPANISH_COLOMBIA] = LanguageID{ TT_MS_LANGID_SPANISH_COLOMBIA, "TT_MS_LANGID_SPANISH_COLOMBIA","Spanish","Colombia" }; m[TT_MS_LANGID_SPANISH_PERU] = LanguageID{ TT_MS_LANGID_SPANISH_PERU, "TT_MS_LANGID_SPANISH_PERU","Spanish","Peru" }; m[TT_MS_LANGID_SPANISH_ARGENTINA] = LanguageID{ TT_MS_LANGID_SPANISH_ARGENTINA, "TT_MS_LANGID_SPANISH_ARGENTINA","Spanish","Argentina" }; m[TT_MS_LANGID_SPANISH_ECUADOR] = LanguageID{ TT_MS_LANGID_SPANISH_ECUADOR, "TT_MS_LANGID_SPANISH_ECUADOR","Spanish","Ecuador" }; m[TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM] = LanguageID{ TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM, "TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM","Malay","Brunei Darussalam" }; m[TT_MS_LANGID_SPANISH_CHILE] = LanguageID{ TT_MS_LANGID_SPANISH_CHILE, "TT_MS_LANGID_SPANISH_CHILE","Spanish","Chile" }; m[TT_MS_LANGID_MALAY_MALAYSIA] = LanguageID{ TT_MS_LANGID_MALAY_MALAYSIA, "TT_MS_LANGID_MALAY_MALAYSIA","Malay","Malaysia" }; m[TT_MS_LANGID_SPANISH_URUGUAY] = LanguageID{ TT_MS_LANGID_SPANISH_URUGUAY, "TT_MS_LANGID_SPANISH_URUGUAY","Spanish","Uruguay" }; m[TT_MS_LANGID_UKRAINIAN_UKRAINE] = LanguageID{ TT_MS_LANGID_UKRAINIAN_UKRAINE, "TT_MS_LANGID_UKRAINIAN_UKRAINE","Ukrainian","Ukraine" }; m[TT_MS_LANGID_SPANISH_PARAGUAY] = LanguageID{ TT_MS_LANGID_SPANISH_PARAGUAY, "TT_MS_LANGID_SPANISH_PARAGUAY","Spanish","Paraguay" }; m[TT_MS_LANGID_SPANISH_BOLIVIA] = LanguageID{ TT_MS_LANGID_SPANISH_BOLIVIA, "TT_MS_LANGID_SPANISH_BOLIVIA","Spanish","Bolivia" }; m[TT_MS_LANGID_SPANISH_HONDURAS] = LanguageID{ TT_MS_LANGID_SPANISH_HONDURAS, "TT_MS_LANGID_SPANISH_HONDURAS","Spanish","Honduras" }; m[TT_MS_LANGID_SPANISH_NICARAGUA] = LanguageID{ TT_MS_LANGID_SPANISH_NICARAGUA, "TT_MS_LANGID_SPANISH_NICARAGUA","Spanish","Nicaragua" }; m[TT_MS_LANGID_SPANISH_PUERTO_RICO] = LanguageID{ TT_MS_LANGID_SPANISH_PUERTO_RICO, "TT_MS_LANGID_SPANISH_PUERTO_RICO","Spanish","Puerto Rico" }; m[TT_MS_LANGID_SPANISH_UNITED_STATES] = LanguageID{ TT_MS_LANGID_SPANISH_UNITED_STATES, "TT_MS_LANGID_SPANISH_UNITED_STATES","Spanish","United States" }; m[TT_MS_LANGID_FINNISH_FINLAND] = LanguageID{ TT_MS_LANGID_FINNISH_FINLAND, "TT_MS_LANGID_FINNISH_FINLAND","Finnish","Finland" }; m[TT_MS_LANGID_FRENCH_FRANCE] = LanguageID{ TT_MS_LANGID_FRENCH_FRANCE, "TT_MS_LANGID_FRENCH_FRANCE","French","France" }; m[TT_MS_LANGID_ROMANIAN_ROMANIA] = LanguageID{ TT_MS_LANGID_ROMANIAN_ROMANIA, "TT_MS_LANGID_ROMANIAN_ROMANIA","Romanian","Romania" }; m[TT_MS_LANGID_FRENCH_BELGIUM] = LanguageID{ TT_MS_LANGID_FRENCH_BELGIUM, "TT_MS_LANGID_FRENCH_BELGIUM","French","Belgium" }; m[TT_MS_LANGID_FRENCH_CANADA] = LanguageID{ TT_MS_LANGID_FRENCH_CANADA, "TT_MS_LANGID_FRENCH_CANADA","French","Canada" }; m[TT_MS_LANGID_FRENCH_SWITZERLAND] = LanguageID{ TT_MS_LANGID_FRENCH_SWITZERLAND, "TT_MS_LANGID_FRENCH_SWITZERLAND","French","Switzerland" }; m[TT_MS_LANGID_FRENCH_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_FRENCH_LUXEMBOURG, "TT_MS_LANGID_FRENCH_LUXEMBOURG","French","Luxembourg" }; m[TT_MS_LANGID_FRENCH_MONACO] = LanguageID{ TT_MS_LANGID_FRENCH_MONACO, "TT_MS_LANGID_FRENCH_MONACO","French","Principality of Monoco" }; m[TT_MS_LANGID_HEBREW_ISRAEL] = LanguageID{ TT_MS_LANGID_HEBREW_ISRAEL, "TT_MS_LANGID_HEBREW_ISRAEL","Hebrew","Israel" }; m[TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN] = LanguageID{ TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN, "TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN","Serbian (Latin)","Bosnia and Herzegovina" }; m[TT_MS_LANGID_HUNGARIAN_HUNGARY] = LanguageID{ TT_MS_LANGID_HUNGARIAN_HUNGARY, "TT_MS_LANGID_HUNGARIAN_HUNGARY","Hungarian","Hungary" }; m[TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC] = LanguageID{ TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC, "TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC","Uzbek (Cyrillic)","Uzbekistan" }; m[TT_MS_LANGID_ICELANDIC_ICELAND] = LanguageID{ TT_MS_LANGID_ICELANDIC_ICELAND, "TT_MS_LANGID_ICELANDIC_ICELAND","Icelandic","Iceland" }; m[TT_MS_LANGID_ITALIAN_ITALY] = LanguageID{ TT_MS_LANGID_ITALIAN_ITALY, "TT_MS_LANGID_ITALIAN_ITALY","Italian","Italy" }; m[TT_MS_LANGID_JAPANESE_JAPAN] = LanguageID{ TT_MS_LANGID_JAPANESE_JAPAN, "TT_MS_LANGID_JAPANESE_JAPAN","Japanese","Japan" }; m[TT_MS_LANGID_KOREAN_KOREA] = LanguageID{ TT_MS_LANGID_KOREAN_KOREA, "TT_MS_LANGID_KOREAN_KOREA","Korean","Korea" }; m[TT_MS_LANGID_DUTCH_NETHERLANDS] = LanguageID{ TT_MS_LANGID_DUTCH_NETHERLANDS, "TT_MS_LANGID_DUTCH_NETHERLANDS","Dutch","Netherlands" }; m[TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL] = LanguageID{ TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL, "TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL","Norwegian (Bokmal)","Norway" }; m[TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK] = LanguageID{ TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK, "TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK","Norwegian (Nynorsk)","Norway" }; m[TT_MS_LANGID_POLISH_POLAND] = LanguageID{ TT_MS_LANGID_POLISH_POLAND, "TT_MS_LANGID_POLISH_POLAND","Polish","Poland" }; m[TT_MS_LANGID_PORTUGUESE_BRAZIL] = LanguageID{ TT_MS_LANGID_PORTUGUESE_BRAZIL, "TT_MS_LANGID_PORTUGUESE_BRAZIL","Portuguese","Brazil" }; m[TT_MS_LANGID_FRISIAN_NETHERLANDS] = LanguageID{ TT_MS_LANGID_FRISIAN_NETHERLANDS, "TT_MS_LANGID_FRISIAN_NETHERLANDS","Frisian","Netherlands" }; m[TT_MS_LANGID_PORTUGUESE_PORTUGAL] = LanguageID{ TT_MS_LANGID_PORTUGUESE_PORTUGAL, "TT_MS_LANGID_PORTUGUESE_PORTUGAL","Portuguese","Portugal" }; m[TT_MS_LANGID_SAMI_NORTHERN_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_SWEDEN, "TT_MS_LANGID_SAMI_NORTHERN_SWEDEN","Sami (Northern)","Sweden" }; m[TT_MS_LANGID_ROMANSH_SWITZERLAND] = LanguageID{ TT_MS_LANGID_ROMANSH_SWITZERLAND, "TT_MS_LANGID_ROMANSH_SWITZERLAND","Romansh","Switzerland" }; m[TT_MS_LANGID_RUSSIAN_RUSSIA] = LanguageID{ TT_MS_LANGID_RUSSIAN_RUSSIA, "TT_MS_LANGID_RUSSIAN_RUSSIA","Russian","Russia" }; m[TT_MS_LANGID_CROATIAN_CROATIA] = LanguageID{ TT_MS_LANGID_CROATIAN_CROATIA, "TT_MS_LANGID_CROATIAN_CROATIA","Croatian","Croatia" }; m[TT_MS_LANGID_AMHARIC_ETHIOPIA] = LanguageID{ TT_MS_LANGID_AMHARIC_ETHIOPIA, "TT_MS_LANGID_AMHARIC_ETHIOPIA","Amharic","Ethiopia" }; m[TT_MS_LANGID_SERBIAN_SERBIA_LATIN] = LanguageID{ TT_MS_LANGID_SERBIAN_SERBIA_LATIN, "TT_MS_LANGID_SERBIAN_SERBIA_LATIN","Serbian (Latin)","Serbia" }; m[TT_MS_LANGID_TURKMEN_TURKMENISTAN] = LanguageID{ TT_MS_LANGID_TURKMEN_TURKMENISTAN, "TT_MS_LANGID_TURKMEN_TURKMENISTAN","Turkmen","Turkmenistan" }; m[TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC] = LanguageID{ TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC, "TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC","Serbian (Cyrillic)","Serbia" }; m[TT_MS_LANGID_LATVIAN_LATVIA] = LanguageID{ TT_MS_LANGID_LATVIAN_LATVIA, "TT_MS_LANGID_LATVIAN_LATVIA","Latvian","Latvia" }; m[TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA] = LanguageID{ TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA, "TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA","Croatian (Latin)","Bosnia and Herzegovina" }; m[TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA] = LanguageID{ TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA, "TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA","Bosnian (Latin)","Bosnia and Herzegovina" }; m[TT_MS_LANGID_SETSWANA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_SETSWANA_SOUTH_AFRICA, "TT_MS_LANGID_SETSWANA_SOUTH_AFRICA","Setswana","South Africa" }; m[TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC] = LanguageID{ TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC, "TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC","Serbian (Cyrillic)","Bosnia and Herzegovina" }; m[TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC] = LanguageID{ TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC, "TT_MS_LANGID_BOSNIAN_BOSNIA_HERZ_CYRILLIC","Bosnian (Cyrillic)","Bosnia and Herzegovina" }; m[TT_MS_LANGID_SLOVAK_SLOVAKIA] = LanguageID{ TT_MS_LANGID_SLOVAK_SLOVAKIA, "TT_MS_LANGID_SLOVAK_SLOVAKIA","Slovak","Slovakia" }; m[TT_MS_LANGID_ALBANIAN_ALBANIA] = LanguageID{ TT_MS_LANGID_ALBANIAN_ALBANIA, "TT_MS_LANGID_ALBANIAN_ALBANIA","Albanian","Albania" }; m[TT_MS_LANGID_SWEDISH_SWEDEN] = LanguageID{ TT_MS_LANGID_SWEDISH_SWEDEN, "TT_MS_LANGID_SWEDISH_SWEDEN","Swedish","Sweden" }; m[TT_MS_LANGID_THAI_THAILAND] = LanguageID{ TT_MS_LANGID_THAI_THAILAND, "TT_MS_LANGID_THAI_THAILAND","Thai","Thailand" }; m[TT_MS_LANGID_TURKISH_TURKEY] = LanguageID{ TT_MS_LANGID_TURKISH_TURKEY, "TT_MS_LANGID_TURKISH_TURKEY","Turkish","Turkey" }; m[TT_MS_LANGID_URDU_PAKISTAN] = LanguageID{ TT_MS_LANGID_URDU_PAKISTAN, "TT_MS_LANGID_URDU_PAKISTAN","Urdu","Islamic Republic of Pakistan" }; m[TT_MS_LANGID_BELARUSIAN_BELARUS] = LanguageID{ TT_MS_LANGID_BELARUSIAN_BELARUS, "TT_MS_LANGID_BELARUSIAN_BELARUS","Belarusian","Belarus" }; m[TT_MS_LANGID_SLOVENIAN_SLOVENIA] = LanguageID{ TT_MS_LANGID_SLOVENIAN_SLOVENIA, "TT_MS_LANGID_SLOVENIAN_SLOVENIA","Slovenian","Slovenia" }; m[TT_MS_LANGID_ESTONIAN_ESTONIA] = LanguageID{ TT_MS_LANGID_ESTONIAN_ESTONIA, "TT_MS_LANGID_ESTONIAN_ESTONIA","Estonian","Estonia" }; m[TT_MS_LANGID_LITHUANIAN_LITHUANIA] = LanguageID{ TT_MS_LANGID_LITHUANIAN_LITHUANIA, "TT_MS_LANGID_LITHUANIAN_LITHUANIA","Lithuanian","Lithuania" }; m[TT_MS_LANGID_TAJIK_TAJIKISTAN] = LanguageID{ TT_MS_LANGID_TAJIK_TAJIKISTAN, "TT_MS_LANGID_TAJIK_TAJIKISTAN","Tajik (Cyrillic)","Tajikistan" }; m[TT_MS_LANGID_VIETNAMESE_VIET_NAM] = LanguageID{ TT_MS_LANGID_VIETNAMESE_VIET_NAM, "TT_MS_LANGID_VIETNAMESE_VIET_NAM","Vietnamese","Vietnam" }; m[TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN] = LanguageID{ TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN, "TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN","Azeri (Latin)","Azerbaijan" }; m[TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS] = LanguageID{ TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS, "TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS","Faroese","Faroe Islands" }; m[TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC] = LanguageID{ TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC, "TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC","Azeri (Cyrillic)","Azerbaijan" }; m[TT_MS_LANGID_BASQUE_BASQUE] = LanguageID{ TT_MS_LANGID_BASQUE_BASQUE, "TT_MS_LANGID_BASQUE_BASQUE","Basque","Basque" }; m[TT_MS_LANGID_UPPER_SORBIAN_GERMANY] = LanguageID{ TT_MS_LANGID_UPPER_SORBIAN_GERMANY, "TT_MS_LANGID_UPPER_SORBIAN_GERMANY","Upper Sorbian","Germany" }; m[TT_MS_LANGID_LOWER_SORBIAN_GERMANY] = LanguageID{ TT_MS_LANGID_LOWER_SORBIAN_GERMANY, "TT_MS_LANGID_LOWER_SORBIAN_GERMANY","Lower Sorbian","Germany" }; m[TT_MS_LANGID_MACEDONIAN_MACEDONIA] = LanguageID{ TT_MS_LANGID_MACEDONIAN_MACEDONIA, "TT_MS_LANGID_MACEDONIAN_MACEDONIA","Macedonian (FYROM)","Former Yugoslav Republic of Macedonia" }; m[TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA, "TT_MS_LANGID_ISIXHOSA_SOUTH_AFRICA","isiXhosa","South Africa" }; m[TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA, "TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA","Afrikaans","South Africa" }; m[TT_MS_LANGID_GEORGIAN_GEORGIA] = LanguageID{ TT_MS_LANGID_GEORGIAN_GEORGIA, "TT_MS_LANGID_GEORGIAN_GEORGIA","Georgian","Georgia" }; m[TT_MS_LANGID_HINDI_INDIA] = LanguageID{ TT_MS_LANGID_HINDI_INDIA, "TT_MS_LANGID_HINDI_INDIA","Hindi","India" }; m[TT_MS_LANGID_MALTESE_MALTA] = LanguageID{ TT_MS_LANGID_MALTESE_MALTA, "TT_MS_LANGID_MALTESE_MALTA","Maltese","Malta" }; m[TT_MS_LANGID_SAMI_NORTHERN_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_NORWAY, "TT_MS_LANGID_SAMI_NORTHERN_NORWAY","Sami (Northern)","Norway" }; m[TT_MS_LANGID_PASHTO_AFGHANISTAN] = LanguageID{ TT_MS_LANGID_PASHTO_AFGHANISTAN, "TT_MS_LANGID_PASHTO_AFGHANISTAN","Pashto","Afghanistan" }; m[TT_MS_LANGID_SAMI_NORTHERN_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_NORTHERN_FINLAND, "TT_MS_LANGID_SAMI_NORTHERN_FINLAND","Sami (Northern)","Finland" }; m[TT_MS_LANGID_SAMI_LULE_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_LULE_NORWAY, "TT_MS_LANGID_SAMI_LULE_NORWAY","Sami (Lule)","Norway" }; m[TT_MS_LANGID_KANNADA_INDIA] = LanguageID{ TT_MS_LANGID_KANNADA_INDIA, "TT_MS_LANGID_KANNADA_INDIA","Kannada","India" }; m[TT_MS_LANGID_SAMI_LULE_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_LULE_SWEDEN, "TT_MS_LANGID_SAMI_LULE_SWEDEN","Sami (Lule)","Sweden" }; m[TT_MS_LANGID_SAMI_SOUTHERN_NORWAY] = LanguageID{ TT_MS_LANGID_SAMI_SOUTHERN_NORWAY, "TT_MS_LANGID_SAMI_SOUTHERN_NORWAY","Sami (Southern)","Norway" }; m[TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN] = LanguageID{ TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN, "TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN","Sami (Southern)","Sweden" }; m[TT_MS_LANGID_SAMI_SKOLT_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_SKOLT_FINLAND, "TT_MS_LANGID_SAMI_SKOLT_FINLAND","Sami (Skolt)","Finland" }; m[TT_MS_LANGID_SAMI_INARI_FINLAND] = LanguageID{ TT_MS_LANGID_SAMI_INARI_FINLAND, "TT_MS_LANGID_SAMI_INARI_FINLAND","Sami (Inari)","Finland" }; m[TT_MS_LANGID_KAZAKH_KAZAKHSTAN] = LanguageID{ TT_MS_LANGID_KAZAKH_KAZAKHSTAN, "TT_MS_LANGID_KAZAKH_KAZAKHSTAN","Kazakh","Kazakhstan" }; m[TT_MS_LANGID_KYRGYZ_KYRGYZSTAN] = LanguageID{ TT_MS_LANGID_KYRGYZ_KYRGYZSTAN, "TT_MS_LANGID_KYRGYZ_KYRGYZSTAN","Kyrgyz","Kyrgyzstan" }; m[TT_MS_LANGID_KISWAHILI_KENYA] = LanguageID{ TT_MS_LANGID_KISWAHILI_KENYA, "TT_MS_LANGID_KISWAHILI_KENYA","Kiswahili","Kenya" }; m[TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN] = LanguageID{ TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN, "TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN","Uzbek (Latin)","Uzbekistan" }; m[TT_MS_LANGID_TATAR_RUSSIA] = LanguageID{ TT_MS_LANGID_TATAR_RUSSIA, "TT_MS_LANGID_TATAR_RUSSIA","Tatar","Russia" }; m[TT_MS_LANGID_PUNJABI_INDIA] = LanguageID{ TT_MS_LANGID_PUNJABI_INDIA, "TT_MS_LANGID_PUNJABI_INDIA","Punjabi","India" }; m[TT_MS_LANGID_QUECHUA_ECUADOR] = LanguageID{ TT_MS_LANGID_QUECHUA_ECUADOR, "TT_MS_LANGID_QUECHUA_ECUADOR","Quechua","Ecuador" }; m[TT_MS_LANGID_GUJARATI_INDIA] = LanguageID{ TT_MS_LANGID_GUJARATI_INDIA, "TT_MS_LANGID_GUJARATI_INDIA","Gujarati","India" }; m[TT_MS_LANGID_ODIA_INDIA] = LanguageID{ TT_MS_LANGID_ODIA_INDIA, "TT_MS_LANGID_ODIA_INDIA","Odia (formerly Oriya)","India" }; m[TT_MS_LANGID_TAMIL_INDIA] = LanguageID{ TT_MS_LANGID_TAMIL_INDIA, "TT_MS_LANGID_TAMIL_INDIA","Tamil","India" }; m[TT_MS_LANGID_TELUGU_INDIA] = LanguageID{ TT_MS_LANGID_TELUGU_INDIA, "TT_MS_LANGID_TELUGU_INDIA","Telugu","India" }; m[TT_MS_LANGID_MALAYALAM_INDIA] = LanguageID{ TT_MS_LANGID_MALAYALAM_INDIA, "TT_MS_LANGID_MALAYALAM_INDIA","Malayalam","India" }; m[TT_MS_LANGID_ASSAMESE_INDIA] = LanguageID{ TT_MS_LANGID_ASSAMESE_INDIA, "TT_MS_LANGID_ASSAMESE_INDIA","Assamese","India" }; m[TT_MS_LANGID_SANSKRIT_INDIA] = LanguageID{ TT_MS_LANGID_SANSKRIT_INDIA, "TT_MS_LANGID_SANSKRIT_INDIA","Sanskrit","India" }; m[TT_MS_LANGID_MONGOLIAN_MONGOLIA] = LanguageID{ TT_MS_LANGID_MONGOLIAN_MONGOLIA, "TT_MS_LANGID_MONGOLIAN_MONGOLIA","Mongolian (Cyrillic)","Mongolia" }; m[TT_MS_LANGID_MONGOLIAN_PRC] = LanguageID{ TT_MS_LANGID_MONGOLIAN_PRC, "TT_MS_LANGID_MONGOLIAN_PRC","Mongolian (Traditional)","People’s Republic of China" }; m[TT_MS_LANGID_WELSH_UNITED_KINGDOM] = LanguageID{ TT_MS_LANGID_WELSH_UNITED_KINGDOM, "TT_MS_LANGID_WELSH_UNITED_KINGDOM","Welsh","United Kingdom" }; m[TT_MS_LANGID_QUECHUA_PERU] = LanguageID{ TT_MS_LANGID_QUECHUA_PERU, "TT_MS_LANGID_QUECHUA_PERU","Quechua","Peru" }; m[TT_MS_LANGID_KHMER_CAMBODIA] = LanguageID{ TT_MS_LANGID_KHMER_CAMBODIA, "TT_MS_LANGID_KHMER_CAMBODIA","Khmer","Cambodia" }; m[TT_MS_LANGID_GALICIAN_GALICIAN] = LanguageID{ TT_MS_LANGID_GALICIAN_GALICIAN, "TT_MS_LANGID_GALICIAN_GALICIAN","Galician","Galician" }; m[TT_MS_LANGID_KONKANI_INDIA] = LanguageID{ TT_MS_LANGID_KONKANI_INDIA, "TT_MS_LANGID_KONKANI_INDIA","Konkani","India" }; m[TT_MS_LANGID_SINHALA_SRI_LANKA] = LanguageID{ TT_MS_LANGID_SINHALA_SRI_LANKA, "TT_MS_LANGID_SINHALA_SRI_LANKA","Sinhala","Sri Lanka" }; m[TT_MS_LANGID_INUKTITUT_CANADA] = LanguageID{ TT_MS_LANGID_INUKTITUT_CANADA, "TT_MS_LANGID_INUKTITUT_CANADA","Inuktitut","Canada" }; m[TT_MS_LANGID_INUKTITUT_CANADA_LATIN] = LanguageID{ TT_MS_LANGID_INUKTITUT_CANADA_LATIN, "TT_MS_LANGID_INUKTITUT_CANADA_LATIN","Inuktitut (Latin)","Canada" }; m[TT_MS_LANGID_TAMAZIGHT_ALGERIA] = LanguageID{ TT_MS_LANGID_TAMAZIGHT_ALGERIA, "TT_MS_LANGID_TAMAZIGHT_ALGERIA","Tamazight (Latin)","Algeria" }; m[TT_MS_LANGID_NEPALI_NEPAL] = LanguageID{ TT_MS_LANGID_NEPALI_NEPAL, "TT_MS_LANGID_NEPALI_NEPAL","Nepali","Nepal" }; m[TT_MS_LANGID_FILIPINO_PHILIPPINES] = LanguageID{ TT_MS_LANGID_FILIPINO_PHILIPPINES, "TT_MS_LANGID_FILIPINO_PHILIPPINES","Filipino","Philippines" }; m[TT_MS_LANGID_DHIVEHI_MALDIVES] = LanguageID{ TT_MS_LANGID_DHIVEHI_MALDIVES, "TT_MS_LANGID_DHIVEHI_MALDIVES","Divehi","Maldives" }; m[TT_MS_LANGID_HAUSA_NIGERIA] = LanguageID{ TT_MS_LANGID_HAUSA_NIGERIA, "TT_MS_LANGID_HAUSA_NIGERIA","Hausa (Latin)","Nigeria" }; m[TT_MS_LANGID_YORUBA_NIGERIA] = LanguageID{ TT_MS_LANGID_YORUBA_NIGERIA, "TT_MS_LANGID_YORUBA_NIGERIA","Yoruba","Nigeria" }; m[TT_MS_LANGID_QUECHUA_BOLIVIA] = LanguageID{ TT_MS_LANGID_QUECHUA_BOLIVIA, "TT_MS_LANGID_QUECHUA_BOLIVIA","Quechua","Bolivia" }; m[TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA] = LanguageID{ TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA, "TT_MS_LANGID_SESOTHO_SA_LEBOA_SOUTH_AFRICA","Sesotho sa Leboa","South Africa" }; m[TT_MS_LANGID_BASHKIR_RUSSIA] = LanguageID{ TT_MS_LANGID_BASHKIR_RUSSIA, "TT_MS_LANGID_BASHKIR_RUSSIA","Bashkir","Russia" }; m[TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG] = LanguageID{ TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG, "TT_MS_LANGID_LUXEMBOURGISH_LUXEMBOURG","Luxembourgish","Luxembourg" }; m[TT_MS_LANGID_GREENLANDIC_GREENLAND] = LanguageID{ TT_MS_LANGID_GREENLANDIC_GREENLAND, "TT_MS_LANGID_GREENLANDIC_GREENLAND","Greenlandic","Greenland" }; m[TT_MS_LANGID_IGBO_NIGERIA] = LanguageID{ TT_MS_LANGID_IGBO_NIGERIA, "TT_MS_LANGID_IGBO_NIGERIA","Igbo","Nigeria" }; m[TT_MS_LANGID_MAPUDUNGUN_CHILE] = LanguageID{ TT_MS_LANGID_MAPUDUNGUN_CHILE, "TT_MS_LANGID_MAPUDUNGUN_CHILE","Mapudungun","Chile" }; m[TT_MS_LANGID_MOHAWK_MOHAWK] = LanguageID{ TT_MS_LANGID_MOHAWK_MOHAWK, "TT_MS_LANGID_MOHAWK_MOHAWK","Mohawk","Mohawk" }; m[TT_MS_LANGID_BRETON_FRANCE] = LanguageID{ TT_MS_LANGID_BRETON_FRANCE, "TT_MS_LANGID_BRETON_FRANCE","Breton","France" }; m[TT_MS_LANGID_UIGHUR_PRC] = LanguageID{ TT_MS_LANGID_UIGHUR_PRC, "TT_MS_LANGID_UIGHUR_PRC","Uighur","PRC" }; m[TT_MS_LANGID_MAORI_NEW_ZEALAND] = LanguageID{ TT_MS_LANGID_MAORI_NEW_ZEALAND, "TT_MS_LANGID_MAORI_NEW_ZEALAND","Maori","New Zealand" }; m[TT_MS_LANGID_CORSICAN_FRANCE] = LanguageID{ TT_MS_LANGID_CORSICAN_FRANCE, "TT_MS_LANGID_CORSICAN_FRANCE","Corsican","France" }; m[TT_MS_LANGID_ALSATIAN_FRANCE] = LanguageID{ TT_MS_LANGID_ALSATIAN_FRANCE, "TT_MS_LANGID_ALSATIAN_FRANCE","Alsatian","France" }; m[TT_MS_LANGID_YAKUT_RUSSIA] = LanguageID{ TT_MS_LANGID_YAKUT_RUSSIA, "TT_MS_LANGID_YAKUT_RUSSIA","Yakut","Russia" }; m[TT_MS_LANGID_KICHE_GUATEMALA] = LanguageID{ TT_MS_LANGID_KICHE_GUATEMALA, "TT_MS_LANGID_KICHE_GUATEMALA","K’iche","Guatemala" }; m[TT_MS_LANGID_KINYARWANDA_RWANDA] = LanguageID{ TT_MS_LANGID_KINYARWANDA_RWANDA, "TT_MS_LANGID_KINYARWANDA_RWANDA","Kinyarwanda","Rwanda" }; m[TT_MS_LANGID_WOLOF_SENEGAL] = LanguageID{ TT_MS_LANGID_WOLOF_SENEGAL, "TT_MS_LANGID_WOLOF_SENEGAL","Wolof","Senegal" }; m[TT_MS_LANGID_DARI_AFGHANISTAN] = LanguageID{ TT_MS_LANGID_DARI_AFGHANISTAN, "TT_MS_LANGID_DARI_AFGHANISTAN","Dari","Afghanistan" }; } void create_name_id_list(std::unordered_map<unsigned int, NameID>& m) { m[TT_NAME_ID_COPYRIGHT] = NameID{ TT_NAME_ID_COPYRIGHT ,"TT_NAME_ID_COPYRIGHT" }; m[TT_NAME_ID_FONT_FAMILY] = NameID{ TT_NAME_ID_FONT_FAMILY ,"TT_NAME_ID_FONT_FAMILY" }; m[TT_NAME_ID_FONT_SUBFAMILY] = NameID{ TT_NAME_ID_FONT_SUBFAMILY ,"TT_NAME_ID_FONT_SUBFAMILY" }; m[TT_NAME_ID_UNIQUE_ID] = NameID{ TT_NAME_ID_UNIQUE_ID ,"TT_NAME_ID_UNIQUE_ID" }; m[TT_NAME_ID_FULL_NAME] = NameID{ TT_NAME_ID_FULL_NAME ,"TT_NAME_ID_FULL_NAME" }; m[TT_NAME_ID_VERSION_STRING] = NameID{ TT_NAME_ID_VERSION_STRING ,"TT_NAME_ID_VERSION_STRING" }; m[TT_NAME_ID_PS_NAME] = NameID{ TT_NAME_ID_PS_NAME ,"TT_NAME_ID_PS_NAME" }; m[TT_NAME_ID_TRADEMARK] = NameID{ TT_NAME_ID_TRADEMARK ,"TT_NAME_ID_TRADEMARK" }; /* the following values are from the OpenType spec */ m[TT_NAME_ID_MANUFACTURER] = NameID{ TT_NAME_ID_MANUFACTURER,"TT_NAME_ID_MANUFACTURER" }; m[TT_NAME_ID_DESIGNER] = NameID{ TT_NAME_ID_DESIGNER ,"TT_NAME_ID_DESIGNER" }; m[TT_NAME_ID_DESCRIPTION] = NameID{ TT_NAME_ID_DESCRIPTION ,"TT_NAME_ID_DESCRIPTION" }; m[TT_NAME_ID_VENDOR_URL] = NameID{ TT_NAME_ID_VENDOR_URL ,"TT_NAME_ID_VENDOR_URL" }; m[TT_NAME_ID_DESIGNER_URL] = NameID{ TT_NAME_ID_DESIGNER_URL,"TT_NAME_ID_DESIGNER_URL" }; m[TT_NAME_ID_LICENSE] = NameID{ TT_NAME_ID_LICENSE ,"TT_NAME_ID_LICENSE" }; m[TT_NAME_ID_LICENSE_URL] = NameID{ TT_NAME_ID_LICENSE_URL ,"TT_NAME_ID_LICENSE_URL" }; /* number 15 is reserved */ m[TT_NAME_ID_TYPOGRAPHIC_FAMILY] = NameID{ TT_NAME_ID_TYPOGRAPHIC_FAMILY ,"TT_NAME_ID_TYPOGRAPHIC_FAMILY" }; m[TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY] = NameID{ TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,"TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY" }; m[TT_NAME_ID_MAC_FULL_NAME] = NameID{ TT_NAME_ID_MAC_FULL_NAME ,"TT_NAME_ID_MAC_FULL_NAME" }; /* The following code is new as of 2000-01-21 */ m[TT_NAME_ID_SAMPLE_TEXT] = NameID{ TT_NAME_ID_SAMPLE_TEXT,"TT_NAME_ID_SAMPLE_TEXT" }; /* This is new in OpenType 1.3 */ m[TT_NAME_ID_CID_FINDFONT_NAME] = NameID{ TT_NAME_ID_CID_FINDFONT_NAME,"TT_NAME_ID_CID_FINDFONT_NAME" }; /* This is new in OpenType 1.5 */ m[TT_NAME_ID_WWS_FAMILY] = NameID{ TT_NAME_ID_WWS_FAMILY,"TT_NAME_ID_WWS_FAMILY" }; m[TT_NAME_ID_WWS_SUBFAMILY] = NameID{ TT_NAME_ID_WWS_SUBFAMILY,"TT_NAME_ID_WWS_SUBFAMILY" }; /* This is new in OpenType 1.7 */ m[TT_NAME_ID_LIGHT_BACKGROUND] = NameID{ TT_NAME_ID_LIGHT_BACKGROUND,"TT_NAME_ID_LIGHT_BACKGROUND" }; m[TT_NAME_ID_DARK_BACKGROUND] = NameID{ TT_NAME_ID_DARK_BACKGROUND,"TT_NAME_ID_DARK_BACKGROUND" }; /* This is new in OpenType 1.8 */ m[TT_NAME_ID_VARIATIONS_PREFIX] = NameID{ TT_NAME_ID_VARIATIONS_PREFIX,"TT_NAME_ID_VARIATIONS_PREFIX" }; /* these two values are deprecated */ m[TT_NAME_ID_PREFERRED_FAMILY] = NameID{ TT_NAME_ID_PREFERRED_FAMILY,"TT_NAME_ID_PREFERRED_FAMILY" }; m[TT_NAME_ID_PREFERRED_SUBFAMILY] = NameID{ TT_NAME_ID_PREFERRED_SUBFAMILY,"TT_NAME_ID_PREFERRED_SUBFAMILY" }; } //TT_PLATFORM_APPLE_UNICODE void create_encoding_id_apple( std::unordered_map<unsigned int, EncodingID_Apple>& m) { m[TT_APPLE_ID_DEFAULT] = EncodingID_Apple{ TT_APPLE_ID_DEFAULT ,"TT_APPLE_ID_DEFAULT" }; m[TT_APPLE_ID_UNICODE_1_1] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_1_1 ,"TT_APPLE_ID_UNICODE_1_1" }; m[TT_APPLE_ID_ISO_10646] = EncodingID_Apple{ TT_APPLE_ID_ISO_10646 ,"TT_APPLE_ID_ISO_10646" }; m[TT_APPLE_ID_UNICODE_2_0] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_2_0 ,"TT_APPLE_ID_UNICODE_2_0" }; m[TT_APPLE_ID_UNICODE_32] = EncodingID_Apple{ TT_APPLE_ID_UNICODE_32 ,"TT_APPLE_ID_UNICODE_32" }; m[TT_APPLE_ID_VARIANT_SELECTOR] = EncodingID_Apple{ TT_APPLE_ID_VARIANT_SELECTOR,"TT_APPLE_ID_VARIANT_SELECTOR" }; m[TT_APPLE_ID_FULL_UNICODE] = EncodingID_Apple{ TT_APPLE_ID_FULL_UNICODE ,"TT_APPLE_ID_FULL_UNICODE" }; } // TT_PLATFORM_MACINTOSH void create_encoding_id_MAC( std::unordered_map<unsigned int, EncodingID_MACINTOSH>& m ) { m[TT_MAC_ID_ROMAN] = EncodingID_MACINTOSH{ TT_MAC_ID_ROMAN ,"TT_MAC_ID_ROMAN" }; m[TT_MAC_ID_JAPANESE] = EncodingID_MACINTOSH{ TT_MAC_ID_JAPANESE ,"TT_MAC_ID_JAPANESE" }; m[TT_MAC_ID_TRADITIONAL_CHINESE] = EncodingID_MACINTOSH{ TT_MAC_ID_TRADITIONAL_CHINESE ,"TT_MAC_ID_TRADITIONAL_CHINESE" }; m[TT_MAC_ID_KOREAN] = EncodingID_MACINTOSH{ TT_MAC_ID_KOREAN ,"TT_MAC_ID_KOREAN" }; m[TT_MAC_ID_ARABIC] = EncodingID_MACINTOSH{ TT_MAC_ID_ARABIC ,"TT_MAC_ID_ARABIC" }; m[TT_MAC_ID_HEBREW] = EncodingID_MACINTOSH{ TT_MAC_ID_HEBREW ,"TT_MAC_ID_HEBREW" }; m[TT_MAC_ID_GREEK] = EncodingID_MACINTOSH{ TT_MAC_ID_GREEK ,"TT_MAC_ID_GREEK" }; m[TT_MAC_ID_RUSSIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_RUSSIAN ,"TT_MAC_ID_RUSSIAN" }; m[TT_MAC_ID_RSYMBOL] = EncodingID_MACINTOSH{ TT_MAC_ID_RSYMBOL ,"TT_MAC_ID_RSYMBOL" }; m[TT_MAC_ID_DEVANAGARI] = EncodingID_MACINTOSH{ TT_MAC_ID_DEVANAGARI ,"TT_MAC_ID_DEVANAGARI" }; m[TT_MAC_ID_GURMUKHI] = EncodingID_MACINTOSH{ TT_MAC_ID_GURMUKHI ,"TT_MAC_ID_GURMUKHI" }; m[TT_MAC_ID_GUJARATI] = EncodingID_MACINTOSH{ TT_MAC_ID_GUJARATI ,"TT_MAC_ID_GUJARATI" }; m[TT_MAC_ID_ORIYA] = EncodingID_MACINTOSH{ TT_MAC_ID_ORIYA ,"TT_MAC_ID_ORIYA" }; m[TT_MAC_ID_BENGALI] = EncodingID_MACINTOSH{ TT_MAC_ID_BENGALI ,"TT_MAC_ID_BENGALI" }; m[TT_MAC_ID_TAMIL] = EncodingID_MACINTOSH{ TT_MAC_ID_TAMIL ,"TT_MAC_ID_TAMIL" }; m[TT_MAC_ID_TELUGU] = EncodingID_MACINTOSH{ TT_MAC_ID_TELUGU ,"TT_MAC_ID_TELUGU" }; m[TT_MAC_ID_KANNADA] = EncodingID_MACINTOSH{ TT_MAC_ID_KANNADA ,"TT_MAC_ID_KANNADA" }; m[TT_MAC_ID_MALAYALAM] = EncodingID_MACINTOSH{ TT_MAC_ID_MALAYALAM ,"TT_MAC_ID_MALAYALAM" }; m[TT_MAC_ID_SINHALESE] = EncodingID_MACINTOSH{ TT_MAC_ID_SINHALESE ,"TT_MAC_ID_SINHALESE" }; m[TT_MAC_ID_BURMESE] = EncodingID_MACINTOSH{ TT_MAC_ID_BURMESE ,"TT_MAC_ID_BURMESE" }; m[TT_MAC_ID_KHMER] = EncodingID_MACINTOSH{ TT_MAC_ID_KHMER ,"TT_MAC_ID_KHMER" }; m[TT_MAC_ID_THAI] = EncodingID_MACINTOSH{ TT_MAC_ID_THAI ,"TT_MAC_ID_THAI" }; m[TT_MAC_ID_LAOTIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_LAOTIAN ,"TT_MAC_ID_LAOTIAN" }; m[TT_MAC_ID_GEORGIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_GEORGIAN ,"TT_MAC_ID_GEORGIAN" }; m[TT_MAC_ID_ARMENIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_ARMENIAN ,"TT_MAC_ID_ARMENIAN" }; m[TT_MAC_ID_MALDIVIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_MALDIVIAN ,"TT_MAC_ID_MALDIVIAN" }; m[TT_MAC_ID_SIMPLIFIED_CHINESE] = EncodingID_MACINTOSH{ TT_MAC_ID_SIMPLIFIED_CHINESE ,"TT_MAC_ID_SIMPLIFIED_CHINESE" }; m[TT_MAC_ID_TIBETAN] = EncodingID_MACINTOSH{ TT_MAC_ID_TIBETAN ,"TT_MAC_ID_TIBETAN" }; m[TT_MAC_ID_MONGOLIAN] = EncodingID_MACINTOSH{ TT_MAC_ID_MONGOLIAN ,"TT_MAC_ID_MONGOLIAN" }; m[TT_MAC_ID_GEEZ] = EncodingID_MACINTOSH{ TT_MAC_ID_GEEZ ,"TT_MAC_ID_GEEZ" }; m[TT_MAC_ID_SLAVIC] = EncodingID_MACINTOSH{ TT_MAC_ID_SLAVIC ,"TT_MAC_ID_SLAVIC" }; m[TT_MAC_ID_VIETNAMESE] = EncodingID_MACINTOSH{ TT_MAC_ID_VIETNAMESE ,"TT_MAC_ID_VIETNAMESE" }; m[TT_MAC_ID_SINDHI] = EncodingID_MACINTOSH{ TT_MAC_ID_SINDHI ,"TT_MAC_ID_SINDHI" }; m[TT_MAC_ID_UNINTERP] = EncodingID_MACINTOSH{ TT_MAC_ID_UNINTERP ,"TT_MAC_ID_UNINTERP" }; } // TT_PLATFORM_ISO void create_encoding_id_iso( std::unordered_map<unsigned int, EncodingID_ISO>& m ) { m[TT_ISO_ID_7BIT_ASCII] = EncodingID_ISO{ TT_ISO_ID_7BIT_ASCII,"TT_ISO_ID_7BIT_ASCII" }; m[TT_ISO_ID_10646] = EncodingID_ISO{ TT_ISO_ID_10646 ,"TT_ISO_ID_10646" }; m[TT_ISO_ID_8859_1] = EncodingID_ISO{ TT_ISO_ID_8859_1 ,"TT_ISO_ID_8859_1" }; } //TT_PLATFORM_MICROSOFT void create_encoding_id_Microsoft( std::unordered_map<unsigned int, EncodingID_Microsoft>& m ) { m[TT_MS_ID_SYMBOL_CS] = EncodingID_Microsoft{ TT_MS_ID_SYMBOL_CS ,"TT_MS_ID_SYMBOL_CS" }; m[TT_MS_ID_UNICODE_CS] = EncodingID_Microsoft{ TT_MS_ID_UNICODE_CS,"TT_MS_ID_UNICODE_CS" }; m[TT_MS_ID_SJIS] = EncodingID_Microsoft{ TT_MS_ID_SJIS ,"TT_MS_ID_SJIS" }; m[TT_MS_ID_PRC] = EncodingID_Microsoft{ TT_MS_ID_PRC ,"TT_MS_ID_PRC" }; m[TT_MS_ID_BIG_5] = EncodingID_Microsoft{ TT_MS_ID_BIG_5 ,"TT_MS_ID_BIG_5" }; m[TT_MS_ID_WANSUNG] = EncodingID_Microsoft{ TT_MS_ID_WANSUNG ,"TT_MS_ID_WANSUNG" }; m[TT_MS_ID_JOHAB] = EncodingID_Microsoft{ TT_MS_ID_JOHAB ,"TT_MS_ID_JOHAB" }; m[TT_MS_ID_UCS_4] = EncodingID_Microsoft{ TT_MS_ID_UCS_4 ,"TT_MS_ID_UCS_4" }; m[TT_MS_ID_GB2312] = EncodingID_Microsoft{ TT_MS_ID_GB2312 ,"TT_MS_ID_GB2312" }; } //TT_PLATFORM_ADOBE void create_encoding_id_Adobe( std::unordered_map<unsigned int, EncodingID_Adobe>& m ) { m[TT_ADOBE_ID_STANDARD] = EncodingID_Adobe{ TT_ADOBE_ID_STANDARD,"TT_ADOBE_ID_STANDARD" }; m[TT_ADOBE_ID_EXPERT] = EncodingID_Adobe{ TT_ADOBE_ID_EXPERT ,"TT_ADOBE_ID_EXPERT " }; m[TT_ADOBE_ID_CUSTOM] = EncodingID_Adobe{ TT_ADOBE_ID_CUSTOM ,"TT_ADOBE_ID_CUSTOM " }; m[TT_ADOBE_ID_LATIN_1] = EncodingID_Adobe{ TT_ADOBE_ID_LATIN_1 ,"TT_ADOBE_ID_LATIN_1 " }; }
例えば "meiryo.ttc"から「メイリオ」、"msmincho.ttc"から「MS 明朝」などを取得する。
フォントファイル(ttfやttcなど)の中にはsfntテーブルというのがあり、そこにコピーライト、バージョン、フォントファミリ名などが格納されている。
具体的に情報が入っているのはFT_SfntName::string という変数(文字列)なので、これを取り出すことを考える。
sfntテーブルには項目がたくさんあるので、FT_Get_Sfnt_NameCountで項目数を取得してからFT_Get_Sfnt_Nameでi番目の情報を取得する。
// テーブル内の項目数を取得 FT_Int count = FT_Get_Sfnt_Name_Count(face); // 取得した項目をここへ格納 FT_SfntName sfnt_name; // すべての項目を読み込む for (FT_Int i = 0; i < count; i++) { // 取得 FT_Get_Sfnt_Name(face, i, &sfnt_name);
// sfnt_nameを処理
}
上記、sfnt_name変数にデータをとれたので、その情報が何を表しているか(コピーライトかフォント名かなど)を特定する。
まず、プラットフォームという括りでデータが分けられている。
プラットフォーム==Microsoft , フォント名 == メイリオ
プラットフォーム==MAC , フォント名==メイリオ
プラットフォーム==Apple , フォント名==メイリオ
みたいな構造をしている。なのですべてのプラットフォームを見てしまうと情報が重複するので、例えばMicrosoftだけに絞って見たほうがいい。
プラットフォームの種類はマクロで定義されているので、その一覧でswitchする。
プラットフォームの定義: https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_platform_xxx
// 取得 FT_Get_Sfnt_Name(face, i, &sfnt_name); switch (sfnt_name.platform_id) { case TT_PLATFORM_APPLE_UNICODE: // 処理 break; case TT_PLATFORM_MACINTOSH:: // 処理 break; case TT_PLATFORM_ISO:: // 処理 break; case TT_PLATFORM_MICROSOFT:: // 処理 break; case TT_PLATFORM_CUSTOM:: // 処理 break; case TT_PLATFORM_ADOBE:: // 処理 break; }
name_idには情報の種類が定義されている。これで、上記sfnt_nameに入っているデータがフォント名かコピーライトかの区別ができる。
name_idの定義:https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_name_id_xxx
FT_Get_Sfnt_Name(face, i, &sfnt_name); switch (sfnt_name.platform_id) { case TT_PLATFORM_MICROSOFT:
switch (sfnt_name.name_id) { case TT_NAME_ID_FULL_NAME: printf("これはフォント名\n"); break; case TT_NAME_ID_VERSION_STRING: printf("これはバージョン\n"); break; case TT_NAME_ID_COPYRIGHT: printf("これは著作権表記\n"); break; }
break; default: // 今はMicrosoft以外のプラットフォームは考えない break; }
このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。
なぜ各項目が二つもあるのか。これはその情報が文字列で格納されているのだが、その言語ごとに入っているからだ。
最終的に求めるのが FT_SfntName::string という文字列なのだが、ここに何語で格納されているかという情報がlanguage_idに入っている。そこで言語による切り替えをしてみる。
なおlanguage_idの定義はplatform_idによって分かれている。
platform_idがTT_PLATFORM_MACINTOSHの場合
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_mac_langid_xxx
platform_idがTT_PLATFORM_MICROSOFTの場合
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_ms_langid_xxx
FT_Get_Sfnt_Name(face, i, &sfnt_name); bool target = true; switch (sfnt_name.platform_id) { case TT_PLATFORM_MICROSOFT: switch (sfnt_name.name_id) { case TT_NAME_ID_FULL_NAME: printf("これはフォント名 "); break; case TT_NAME_ID_VERSION_STRING: printf("これはバージョン "); break; case TT_NAME_ID_COPYRIGHT: printf("これは著作権表記 "); break; default: target = false; break; } if (target == true) {
FT_UShort langID = sfnt_name.language_id; switch (langID) { case TT_MS_LANGID_ENGLISH_UNITED_STATES: printf("TT_MS_LANGID_ENGLISH_UNITED_STATES (0x%04x)", langID); break; case TT_MS_LANGID_JAPANESE_JAPAN: printf("TT_MS_LANGID_JAPANESE_JAPAN (0x%04x)", langID); break; }
puts(""); } break; default: // 今はMicrosoft以外のプラットフォームは考えない break; }
このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。
FT_SfntName::string は言語ごとに入っているといった。これが「同じ項目が複数ある理由」である。
language_idはデータを整理するうえでは重要だが、データを人間が読める形にするためにはあまり意味をなさない。データを実際に取り出すには、encoding_idで分岐する必要がある。
今回は諸事情によりmeiryo.ttcを使うがこの中にはエンコードが一つしかない。
あと、エンコードの一覧もplatform_idごとに分かれている。
TT_PLATFORM_APPLE_UNICODE
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_apple_id_xxx
TT_PLATFORM_MACINTOSH
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_mac_id_xxx
TT_PLATFORM_ISO
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_iso_id_xxx
TT_PLATFORM_MICROSOFT
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_ms_id_xxx
TT_PLATFORM_ADOBE
https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_adobe_id_xxx
FT_Get_Sfnt_Name(face, i, &sfnt_name); bool target = true; switch (sfnt_name.platform_id) { case TT_PLATFORM_MICROSOFT: switch (sfnt_name.name_id) { case TT_NAME_ID_FULL_NAME:printf("これはフォント名 ");break; case TT_NAME_ID_VERSION_STRING:printf("これはバージョン ");break; case TT_NAME_ID_COPYRIGHT:printf("これは著作権表記 ");break; default: //それ以外の項目は考えない target = false; break; } if (target == true) { FT_UShort langID = sfnt_name.language_id; switch (langID) { case TT_MS_LANGID_ENGLISH_UNITED_STATES:printf("(英語)");break; case TT_MS_LANGID_JAPANESE_JAPAN:printf("(日本語)");break; default: //それ以外の言語は考えない break; }
FT_UShort encID = sfnt_name.encoding_id; switch (encID) { case TT_MS_ID_UNICODE_CS: printf(" TT_MS_ID_UNICODE_CS (%d)", encID); break; default: //それ以外のエンコードは考えない break; }
puts(""); } break; default: // 今はMicrosoft以外のプラットフォームは考えない break; }
このプログラムをmeiryo.ttcに対して走らせると、以下のように表示される。
ようやくデータを取り出せる。
FT_SfntName::string にはフォント名などが文字列で入っている
FT_SfntName::string_len にはFT_SfntName::stringのサイズがバイト数で入っている。
注意点は、encoding_idがTT_MS_ID_UNICODE_CSの場合、UTF16BEで入っていてwindowsだとそのまま表示できないのでLEに変換してやる必要がある。
FT_UShort encID = sfnt_name.encoding_id; switch (encID) { case TT_MS_ID_UNICODE_CS: { std::wstring wstr; for (size_t i = 0; i < sfnt_name.string_len; i += 2) { char16_t w = *((char16_t*)&sfnt_name.string[i]); // UTF16BEなので2バイトを反転する unsigned char* p = (unsigned char*)&w; std::swap(p[0], p[1]); wstr += w; } printf(" %ls", wstr.c_str()); }
https://freetype.org/freetype2/docs/reference/ft2-sfnt_names.html
疲れた。
#include <iostream> // 自作ラッパー #include "GraphemeText.hpp" #pragma warning(disable:4996) int main() { // 文字列の定義 GText gtext(u"aあغ山👨👧經经"); gtext.insert(0, u"غ"); FILE* fp = fopen("C:\\test\\test.txt", "wb"); for (size_t i = 0; i < gtext.length(); i++) { wchar_t c[100]{ 0 }; int len = gtext[i].size(); // 一文字書き込み memcpy(c, gtext[i].ptr(), len); fwrite(c, 1, len, fp); } fclose(fp); }
#pragma once // 以下のdllを要求される // icudt69.dll // icuuc69.dll//! @brief 書記素クラス class Grapheme { void* _UnicodeString; int _index; int _length; enum UScriptCode _script; bool _emoji; public: Grapheme(void* _UnicodeString_, int _index_, int _length_); ~Grapheme() {} const char16_t* ptr()const; //! @brief この書記素の元の文字列上のindexを取得。 int index()const { return _index; } //! @brief この書記素のchar16_tとしての文字数。 int length()const {return _length;} //! @brief この書記素のバイト数。 int size()const { return _length * 2; } //! @brief この文字の用字の種類 enum UScriptCode script()const { return _script; } //! @brief これが絵文字かどうか bool isEmoji()const { return _emoji; } };
/////////////////////////////////// //! @brief 書記素の配列オブジェクト class GText { struct GText_Impl* _impl; public: GText(const char16_t* text); GText(const GText& src); ~GText(); const Grapheme& operator[](const size_t index)const; //! @brief この文字列の書記素数 size_t length()const; //! @brief 書記素を一つ置き換える //! @attention Graphemeリストを更新するのでこれまでのGraphemeは使えなくなる void replace(const size_t index, const char16_t c); void replace(const size_t index, const char16_t* c); void erase(const size_t index); void insert(const size_t index, const char16_t c); void insert(const size_t index, const char16_t* c); const char16_t* ptr()const; //! @brief この文字列のバイト数 int size()const; };
#include<vector> #include <string> #include <unicode/ucnv.h> #include <unicode/brkiter.h> #include <unicode/utypes.h> #include <unicode/uscript.h> #include "GraphemeText.hpp" // 要リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icudt.lib") // 書記素クラス Grapheme::Grapheme(void* _UnicodeString_, int _index_, int _length_) : _UnicodeString(_UnicodeString_), _index(_index_), _length(_length_) { icu::UnicodeString* p = (icu::UnicodeString*)_UnicodeString; UErrorCode err; UChar32 c32 = p->char32At(_index); // スクリプト _script = uscript_getScript(c32, &err); _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); } const char16_t* Grapheme::ptr()const { return static_cast<icu::UnicodeString*>(_UnicodeString)->getBuffer() + _index; }
// Graphemeの配列を作成
void makeGraphemeList(std::vector< Grapheme >& glist, icu::UnicodeString& text) { UErrorCode err; icu::BreakIterator* bi = icu::BreakIterator::createCharacterInstance( icu::Locale::getDefault(), err); if (bi == nullptr) throw "bi is NULL"; bi->setText(text); int32_t current = bi->first(); while (current != icu::BreakIterator::DONE) { int32_t prev = current; current = bi->next(); if (current == UBRK_DONE) { break; } int32_t count = current - prev;//文字の長さ //書記素保存 glist.emplace_back(&text, prev, count); } delete bi; }
struct GText_Impl { std::vector< Grapheme > glist; icu::UnicodeString utext; }; GText::GText(const char16_t* text) { _impl = new GText_Impl; _impl->utext = text; makeGraphemeList(_impl->glist, _impl->utext); } const char16_t* GText::ptr()const { return _impl->utext.getBuffer(); } GText::GText(const GText& src) { _impl->utext = src._impl->utext; _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); } GText::~GText() { delete _impl; } const Grapheme& GText::operator[](const size_t index)const { return _impl->glist[index]; } size_t GText::length()const { return _impl->glist.size(); } int GText::size()const { return _impl->utext.length() * 2; } void GText::replace(const size_t index, const char16_t c) { if (index >= _impl->glist.size()) throw "GText replace index is too big"; _impl->utext.replace(_impl->glist[index].index(), _impl->glist[index].length(), c); _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); } void GText::replace(const size_t index, const char16_t* c) { if (index >= _impl->glist.size()) throw "GText replace index is too big"; size_t len = std::char_traits<char16_t>::length(c); _impl->utext.replace(_impl->glist[index].index(), _impl->glist[index].length(),c, len); _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); } void GText::erase(const size_t index) { if (index >= _impl->glist.size()) throw "GText erase index is too big"; _impl->utext.remove(_impl->glist[index].index(), _impl->glist[index].length()); _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); } void GText::insert(const size_t index, const char16_t c) { if (index >= _impl->glist.size()) throw "GText insert index is too big"; _impl->utext.insert(_impl->glist[index].index(), c); _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); } void GText::insert(const size_t index, const char16_t* c) { if (index >= _impl->glist.size()) throw "GText insert index is too big"; size_t len = std::char_traits<char16_t>::length(c); _impl->utext.insert(_impl->glist[index].index(), c, len); _impl->glist.clear(); makeGraphemeList(_impl->glist, _impl->utext); }
以下のプログラムをデバッグモードで(←重要)走らせたところエラーが発生。
#include <iostream> #include <list> int main() { std::list<int> aa,bb; aa.push_back(0); bb.push_back(1); // 違うリストのイテレータを比較 if (aa.begin() == bb.begin()) std::cout << "same" << std::endl; }
このエラーは、
・違うリストのイテレータ同士を比較し
かつ
・_ITERATOR_DEBUG_LEVEL==2の時
のとき起こる。だからデバッグーモードでは起こるがリリースモードでは起こらないということがありうる。
このエラーが出ている個所のコードを抜粋すると以下のようになっている。
_NODISCARD bool operator==(const _List_const_iterator& _Right) const noexcept { #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "list iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL == 2 return this->_Ptr == _Right._Ptr; }
つまりイテレータのoperator==()の中でイテレータの正当性をチェックし、意味のない比較が行われたときは意図してエラーを出している。
従って、
などを指定してやれば回避できる。
この部分はイテレータの正当性をチェックし意図的に発生させているエラーなので、自前で書いてしまえばoperator==の前に検出できる。
#include <iostream> #include <list> int main() { std::list<int> aa,bb; aa.push_back(0); bb.push_back(1); std::list<int>::iterator i1 = aa.begin(); std::list<int>::iterator i2 = bb.begin(); #if _ITERATOR_DEBUG_LEVEL==2
// デバッグ時、異なるリスト同士の比較が生じる前にチェックする if (i1._Getcont() != i2._Getcont()) { std::cout << " i1 != i2 " << std::endl; } else
#endif
{ // デバッグ時、異なるリスト同士の比較が生じることはない if (i1 == i2) { std::cout << "i1 == i2" << std::endl; } else { std::cout << "i1 != i2" << std::endl; } }
}
icuライブラリのラッパーを作成したくなり、hppにicu::UnicodeStringのポインタを持たせ、cpp内でnewする構造にした。この時、前方宣言したらエラーC2757が発生。原因はicuという名前空間が実は存在していないかららしい。
まず、エラーの出るコードは以下。
#pragma once // クラスが名前空間に入っている時は前方宣言をこうする namespace icu{ // C2757 class UnicodeString; } // icu::UnicodeStringのラッパー struct MyICU { icu::UnicodeString* ustr; }; // ICUライブラリのUnicodeStringのラッパーを作成 MyICU make_my_icu(const char16_t* text);
#include <unicode/ucnv.h> #include <unicode/brkiter.h> #include"icuwrap.h" // 要リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icudt.lib") MyICU make_my_icu(const char16_t* text) { MyICU tmp; tmp.ustr = new icu::UnicodeString(text); return tmp; }
#include "icuwrap.h" int main() { MyICU micu = make_my_icu(u"hello"); }
すると以下のコンパイルエラーが発生する
icuというリテラルは unicode/uversion.h の中で定義されているのだが、実はこれはU_ICU_NAMESPACEマクロで生成されたもののエイリアスで、さらにこれはU_ICU_ENTRY_POINT_RENAMEマクロで作成されている
unicode/uversion.h Line 105~
# define U_ICU_NAMESPACE U_ICU_ENTRY_POINT_RENAME(icu) namespace U_ICU_NAMESPACE { } namespace icu = U_ICU_NAMESPACE;
ではこの U_ICU_ENTRY_POINT_RENAME マクロの定義はというと、こちらは unicode/uvernum.h で定義されている。
unicode/uversion.h Line 128~
# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y # define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) # define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX)
ここで ## はトークン結合演算子で、x は 「icu」、U_ICU_VERSION_SUFFIXは「_69」など、_ + ライブラリのバージョンが入っている
unicode/uversion.h Line 89
#define U_ICU_VERSION_SUFFIX _69
つまり「icu」は、「icu_69」のエイリアスである。
前方宣言で自分が定義した「namespace icu」というモノが既にあるのに、同じ「icu」をicu_69のエイリアスとして使おうとしたことになって、エラーが起こっている。
バージョンが固定でいいなら以下のように正規の名前空間で前方宣言すれば解決する
// クラスが名前空間に入っている時は前方宣言をこうする namespace icu_69{ class UnicodeString; } struct MyICU { icu_69::UnicodeString* ustr; }; // ICUライブラリのUnicodeStringのラッパーを作成 MyICU make_my_icu(const char16_t* text);
しかしどうせ隠すなら素直にpimplとして完全に隠す方がいい。
U_DISABLE_RENAMINGマクロを使えば解決しそうに見えるが、マニュアルには「内部処理用だから使うな」と書いてあるのであきらめる。
rgbaがfloat[4]のテクスチャを使う。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, TEXWIDTH, TEXHEIGHT, 0,GL_RGBA, GL_FLOAT, texdata);
GL_RGBA32F,GL_RGBA,GL_FLOATを指定する。他はunsigned charの時と変わらない。
#pragma comment(lib,"glew32s.lib") #define GLEW_STATIC #include <gl/glew.h> #include <GL/glut.h> GLuint ProgramID; GLuint FragmentShaderID; GLuint VertexShaderID; typedef GLfloat rgba_t[4]; //テクスチャ用 typedef GLfloat points_t[3]; //モデルの座標用 typedef GLfloat texcoord_t[2];//テクスチャ座標用 const int P_COUNT = 4; //頂点データ GLuint vertexbuffer; points_t position[P_COUNT]; //テクスチャ座標データ texcoord_t texcoord[P_COUNT]; GLuint texcoordbuffer; //テクスチャデータ rgba_t texdata[P_COUNT]; GLuint textureID; void prepare_buffers() { //テクスチャ(画像)作成 // 2*2の画像 texdata[0][0] = 1.0; texdata[0][1] = 0; texdata[0][2] = 0; texdata[0][2] = 0; texdata[1][0] = 0; texdata[1][1] = 1.0; texdata[1][2] = 0; texdata[0][2] = 0; texdata[0][2] = 0; texdata[2][0] = 0; texdata[2][1] = 0; texdata[2][2] = 1.0; texdata[0][2] = 0; texdata[3][0] = 1.0; texdata[3][1] = 1.0; texdata[3][2] = 1.0; texdata[0][2] = 0; ////////////////////////////////////////// ////////////////////////////////////////// //頂点座標の定義 (四角形) // いつもなら glVertex3fv等て指定するもの position[0][0] = -0.7; position[0][1] = 0.7; position[0][2] = 0; position[1][0] = -0.7; position[1][1] = -0.7; position[1][2] = 0; position[2][0] = 0.7; position[2][1] = -0.7; position[2][2] = 0; position[3][0] = +0.7; position[3][1] = +0.7; position[3][2] = 0; ////////////////////////////////////////// ////////////////////////////////////////// //テクスチャ座標の定義 //いつもならglTexCoord2f等で指定するもの texcoord[0][0] = 0; texcoord[0][1] = 1; texcoord[1][0] = 0; texcoord[1][1] = 0; texcoord[2][0] = 1; texcoord[2][1] = 0; texcoord[3][0] = 1; texcoord[3][1] = 1; ////////////////////////////////////////// ////////////////////////////////////////// //テクスチャの作成 // 普通のテクスチャ作成 glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); int TEXWIDTH = 2; // 2*2の画素数 int TEXHEIGHT = 2; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // テクスチャの割り当て glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, TEXWIDTH, TEXHEIGHT, 0,GL_RGBA, GL_FLOAT, texdata); // テクスチャを拡大・縮小する方法の指定 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ////////////////////////////////////////// ////////////////////////////////////////// //頂点バッファの作成 glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, 3 * P_COUNT * sizeof(GLfloat), position, GL_STATIC_DRAW); //テクスチャ座標バッファの作成 glGenBuffers(1, &texcoordbuffer); glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer); glBufferData(GL_ARRAY_BUFFER, 2 * P_COUNT * sizeof(GLfloat), texcoord, GL_STATIC_DRAW); } void display(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_CULL_FACE);//カリングを無効にする /////////////////////////////////// // 行列の設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(65, 1, 0.1, 10); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0.0, 0.0, -3); /////////////////////////////////// // シェーダを使う glUseProgram(ProgramID); /////////////////////////////////// // 頂点バッファを有効化 glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer( 0, // 属性0 3, // 1要素の個数。GLfloatのx,y,zなので3 GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); /////////////////////////////////// //テクスチャ座標バッファの有効化 glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer); glVertexAttribPointer( 1, // 属性1 2, // 1要素の個数。GLfloatのu,vなので2 GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); /////////////////////////////////// // 四角形の描画 glDrawArrays(GL_TRIANGLE_FAN, 0, P_COUNT); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glFlush(); }
非常に多くのモデルデータを読み込めるBSDライセンスのライブラリ。
ビルドはそんなに難しくないのでとりあえずサンプルコードを先に置いておく。
元のサンプルコード
http://assimp.sourceforge.net/lib_html/usage.html
Bunnyのデータ
https://commons.wikimedia.org/wiki/File:Stanford_Bunny.stl
なおstanford bunnyは(スケールが)大きいのでgluLookatで少し遠いところにカメラを設置している。
#include <iostream> #include <vector> #include <array> //////////////////////////// // 表示用のOpenGL // NOMINMAXをしておかないとmaterial.inlでstd::minでエラーが起こる #define NOMINMAX #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> //////////////////////////// // Assimp #include <assimp/Importer.hpp> // C++ importer interface #include <assimp/scene.h> // Output data structure #include <assimp/postprocess.h> // Post processing flags #pragma comment(lib,"assimp-vc142-mt.lib") //////////////////////////// //読み込んだポリゴンデータの格納先 std::vector<std::array<unsigned int, 3>> triangles; std::vector<std::array<float, 3>> points; ////////////////////////////
//! @brief メッシュファイルを読み込み //! @sa http://assimp.sourceforge.net/lib_html/usage.html bool MyDataImporter( std::vector<std::array<unsigned int, 3>>& faces, std::vector<std::array<float, 3>>& coords, const std::string& pFile) { // Create an instance of the Importer class Assimp::Importer importer; const aiScene* scene = importer.ReadFile(pFile, aiProcess_CalcTangentSpace | //接ベクトル空間を計算する aiProcess_Triangulate | //全ての面を三角形分割する aiProcess_JoinIdenticalVertices //重複頂点をマージする ); // If the import failed, report it if (!scene) { printf("失敗:%s\n", importer.GetErrorString()); return false; } if (scene->HasMeshes()) { //メッシュの配列 aiMesh** p = scene->mMeshes; //最初のメッシュへアクセス aiMesh* mesh0 = p[0];
//三角形一覧取得 int face_count = mesh0->mNumFaces; for (size_t findex = 0; findex < face_count; findex++) { aiFace& face = mesh0->mFaces[findex]; if (face.mNumIndices == 3) { faces.push_back( std::array<unsigned int, 3>{ face.mIndices[0], face.mIndices[1], face.mIndices[2] } ); } }
//頂点一覧取得 int vertex_count = mesh0->mNumVertices;
for (size_t vindex = 0; vindex < vertex_count; vindex++) {
aiVector3D& vtx = mesh0->mVertices[vindex]; coords.push_back( std::array<float, 3>{vtx.x, vtx.y, vtx.z} );
} } // 終了(解放不要) return true; }
//ウィンドウの幅と高さ int width, height; //描画関数 void disp(void) { glViewport(0, 0, width, height); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, 1, 0.1, 500); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( 100, 200, 100, 0, 0, 50, 0, 0, 1); glEnable(GL_CULL_FACE); double v = 0.7; glColor3d(1, 0, 0); for (size_t f = 0; f < triangles.size(); f++) { int p0 = triangles[f][0]; int p1 = triangles[f][1]; int p2 = triangles[f][2]; glBegin(GL_LINE_LOOP); glVertex3fv(points[p0].data()); glVertex3fv(points[p1].data()); glVertex3fv(points[p2].data()); glEnd(); } glEnd(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); std::string f = R"(D:\dev\Stanford_Bunny.stl)"; MyDataImporter(triangles, points, f); glutMainLoop(); return 0; }