Skip to content

Instantly share code, notes, and snippets.

@nomissbowling
Last active May 14, 2022 07:08
Show Gist options
  • Save nomissbowling/ce9307ef94e7e06f9abdfee44eb8aacf to your computer and use it in GitHub Desktop.
Save nomissbowling/ce9307ef94e7e06f9abdfee44eb8aacf to your computer and use it in GitHub Desktop.
testFT.cpp
/*
testFT.cpp
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\cl.exe"
-source-charset:utf-8 -execution-charset:utf-8
-EHsc -FetestFT.exe testFT.cpp
-I..\FreeType\include
-link
/LIBPATH:..\FreeType\x64
/LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\lib\x64"
/LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\ucrt\x64"
/LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\um\x64"
freetype.lib
del .\testFT.obj
testFT
https://freetype.org/
Unicode Variation Sequences
https://freetype.org/freetype2/docs/reference/ft2-glyph_variants.html
VS
https://hanya-orz.hatenablog.com/entry/20151228/p1
FT_ULong uc = static_cast<FT_ULong>(L'𠮟');
FT_UInt32
FT_Face_GetVariantSelectors
FT_Face_GetVariantsOfChar
FT_Face_GetCharsOfVariant
FT_Face_GetCharVariantIndex
https://freetype.org/freetype2/docs/reference/ft2-glyph_variants.html#ft_face_getcharvariantindex
surrogate pair
https://ja.wikipedia.org/wiki/Unicode
u6f23 漣
u82a6 芦 u8606 蘆
u4e80 亀 u9f9c 龜
u7515 甕
u9e7d 鹽
u212b Å u00c5 Å
u00ee î
u9d0e 鴎 u9dd7 鷗
u53f1 叱 u20b9f 𠮟
Migu 1M: migu-1m-regular.ttf
face has e0100 e0101 e0102 Migu 1M
000e0100: 000053c9 000054ac 00005efb 0000633a 000065a7 00006756 00006897
0000723a 00007259 000072e1 000082a6 000091dc 00009375 00009771
000097ad 000099c1 00009bab
000e0101: 00006f23
000e0102: 00006756 00006f23 00007259 000082a6
L'漣' has e0100 e0101 Migu 1M
000e0101: (same)
000e0102: (same)
L'芦' has e0100 e0102 Migu 1M
000e0100: (same)
000e0102: (same)
https://itouhiro.hatenablog.com/entry/20130419
https://itouhiro.hatenablog.com/entry/20121031
https://itouhiro.hatenablog.com/entry/20120924
https://tasuten.hatenablog.com/entry/20110219/1298050878
https://dtp-bbs.com/forum/archives/n20150327120350_20150327120350.html
https://moji-memo.hatenablog.jp/entry/20130408/1365404490
https://www.iwatafont.co.jp/news/img/about_font.pdf
VS Variation Selector IVS
https://moji.or.jp/mojikiban/aboutivs/
https://ja.wikipedia.org/wiki/%E7%95%B0%E4%BD%93%E5%AD%97%E3%82%BB%E3%83%AC%E3%82%AF%E3%82%BF
https://community.dal.co.jp/s/article/kanji-variants
http://itdoc.hitachi.co.jp/manuals/3020/30203n7360/NTSO0492.htm
http://itdoc.hitachi.co.jp/manuals/3020/30203N73A0/NTSO0554.htm
IVS VS17-VS256 ue0100-ue01ef Ideographic Variation Sequence
http://www.unicode.org/ivd/
SVS VS1-VS16 ufe00-ufe0f Standardized Variation Sequence
http://www.unicode.org/Public/UNIDATA/StandardizedVariants.txt
cmap table
https://aznote.jakou.com/prog/opentype/08_cmap.html
old JIS90 JIS2004
https://fontnavi.jp/zakkuri/304-jis2004_jis90.aspx
tag jp90 jp04
https://moji-memo.hatenablog.jp/entry/20080219/1203409203
IPAex IPA font https://moji.or.jp/ipafont/
hanazono font http://fonts.jp/hanazono/
Migu 1M font https://mix-mplus-ipa.osdn.jp/migu/
MigMix 1M font https://mix-mplus-ipa.osdn.jp/migmix/
create composite font https://itouhiro.hatenablog.com/entries/2012/10/31
Migu 1VS (DejaVu Sans Condensed + Migu 1C)
Migu 1BT (Bitter + Migu 1C)
Migu 2DS (Droid Sans + MigMix 2P)
DejaVu Sans Condensed https://dejavu-fonts.github.io/
https://github.com/dejavu-fonts/dejavu-fonts
Bitter https://fonts.google.com/specimen/Bitter
https://fontmeme.com/jfont/bitter-font/
Droid Sans https://fontmeme.com/jfont/droid-sans-font/
芦󠄀 -> u82a6 ue0100 -> 82 a6 db 40 dd 00
祇󠄀 -> u7947 ue0100
祇󠄁 -> u7947 ue0101
祗 -> u7957
衹 -> u8879
*/
#define UNICODE
#define _UNICODE
#include <wchar.h>
#define _USE_MATH_DEFINES
#include <cmath>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <string>
#include <stdexcept>
#include <exception>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freetype/freetype.h>
typedef unsigned char uchar;
#if 1
#define FONT_BASE "C:\\Windows\\Fonts\\"
// #define FONT_FN FONT_BASE"ipaexm.ttf" // OK u9dd7 u20b9f u82a6 (u8879)
// #define FONT_FN FONT_BASE"ipaexg.ttf" // OK u9dd7 u20b9f u82a6 (u8879)
#define FONT_FN FONT_BASE"migu-1m-regular.ttf" // OK u9dd7 u20b9f (u82a6 u8879)
// #define FONT_FN FONT_BASE"migmix-1m-regular.ttf" // BAD IVS (u82a6 u8879)
// #define FONT_FN FONT_BASE"msgothic.ttc" // OK u9dd7 u20b9f u82a6 u8879
// #define FONT_FN FONT_BASE"DFLgs9.ttc" // OK but not support some glyph
// #define FONT_FN FONT_BASE"DFLgs9.ttf" // font exists but can not get glyph
// #define FONT_FN FONT_BASE"consola.ttf" // not support wchar_t area
#else
#define FONT_BASE ".\\"
#define FONT_FN FONT_BASE"mikaP.ttf"
#endif
using namespace std;
inline unsigned int calcW(unsigned int bitsPixel, unsigned int w)
{
return ((bitsPixel / 8) * w) & ~3;
}
FT_Int drawBitmap(FT_GlyphSlot slot)
{
FT_Bitmap *bmp = &slot->bitmap;
#if 0
FT_Int tw = dst.size().width, th = dst.size().height;
#else
FT_Int tw = bmp->width, th = bmp->rows;
#endif
FT_Int mw = calcW(24, tw);
#if 0
FT_Int x = loc.x, y = loc.y; // check in loop i<0 j<0 or no wrap left<->right
#else
FT_Int x = 0, y = 0;
#endif
#if 0
fprintf(stdout, "(%d %d) %d (%d %d)", tw, th, mw, x, y);
fprintf(stdout, " %d %d %d %d %d %d\n", slot->bitmap_left, slot->bitmap_top,
slot->bitmap.width, slot->bitmap.rows, slot->advance.x, slot->advance.y);
#endif
FT_Int x_max = x + bmp->width; // + slot->bitmap_left
FT_Int y_max = y + bmp->rows; // - slot->bitmap_top
FT_Int i, j, p, q;
for(j = y, q = 0; j < y_max; j++, q++){ // j = y - slot->bitmap_top
#if 0
uchar *img_line = (uchar *)dst.data + j * mw;
#endif
for(i = x, p = 0; i < x_max; i++, p++){ // i = x + slot->bitmap_left
uchar b = bmp->buffer[q * bmp->width + p];
#if 1
fprintf(stdout, "%c", ".-+*"[b >> 6]);
#endif
if(i < 0 || j < 0 || i >= tw || j >= th) continue;
#if 0
uchar *img = img_line + i * 3;
double a = b / 255.0;
for(int c = 0; c < 3; ++c){
uchar s = *img;
*img++ = (uchar)(a * col[c] + (1.0 - a) * s);
}
#endif
}
#if 1
fprintf(stdout, "\n");
#endif
}
return slot->bitmap.width; // + slot->bitmap_left
}
int main(int ac, char **av)
{
fprintf(stdout, "sizeof(size_t): %zd\n", sizeof(size_t));
FT_Library library;
FT_Error error = FT_Init_FreeType(&library);
FT_Face face;
FT_Int p = 0; // face index 0: proportional, 1: fixed (wide) eg DFLgs9.ttc
error = FT_New_Face(library, FONT_FN, p, &face); // FT_New_Memory_Face
#if 0
error = FT_Select_Charmap(face, FT_ENCODING_UNICODE);
#endif
FT_Int dpi = 72, ffact = 64, fpt = 16; // ffact: 64 (1:1) at 72dpi
FT_Int scale = (FT_Int)(fpt * 2.0); // fontScale = 2.0
// use 16pt(test) or 48pt(release) at 100dpi (1:1 at 72dpi)
error = FT_Set_Char_Size(face, scale * ffact, 0, dpi, 0);
#if 0
error = FT_Select_Size(face, fpt); // always 16pt ? returns 35 ?
#else
error = FT_Set_Pixel_Sizes(face, scale, scale); // width height OK
#endif
#if 1 // get Selectors (VS)
vector<FT_UInt32> vsel; // unknown size
#if 1 // vsel for face has e0100 e0101 e0102 Migu 1M
FT_UInt32 *fsel = FT_Face_GetVariantSelectors(face);
if(fsel) while(*fsel) vsel.push_back(*fsel++);
#else // vsel for L'芦' has e0100 e0102 Migu 1M (L'漣' has e0101 e0102)
FT_ULong uc = static_cast<FT_ULong>(L'芦'); // L'叱' L'𠮟' has no gsel
FT_UInt32 *gsel = FT_Face_GetVariantsOfChar(face, uc);
if(gsel) while(*gsel) vsel.push_back(*gsel++);
#endif
// get CodePoints in each Selectors
map<FT_UInt32, vector<FT_UInt32> > vselCodePoints;
for(auto &v : vsel){
FT_UInt32 *codePoints = FT_Face_GetCharsOfVariant(face, v);
if(codePoints)
while(*codePoints) vselCodePoints[v].push_back(*codePoints++);
}
#if 0 // display Selectors and CodePoints
for(auto &kv : vselCodePoints){
fprintf(stderr, " %08x:", kv.first);
for(auto &cp : kv.second) fprintf(stderr, " %08x", cp);
fprintf(stderr, "\n");
/*
for face
000e0100: 000053c9 ... 00009bab
000e0101: 00006f23
000e0102: 00006756 ... 000082a6
for L'芦'
000e0100: 000053c9 ... 00009bab
000e0102: 00006756 ... 000082a6
*/
}
#endif
#endif
#if 0 // debug only
if(true){
#if 0
FT_ULong uni = L'\u3042'; // u3042
wchar_t u[] = L"\u3042"; // u3042 -> index of mikaP.ttf = 0x07bd
*u = L"\u3041"[0]; // re write it (anti optimize) 0x07bc
*u = L"\u9b31"[0]; // re write it (anti optimize) 0x3760
#endif
FT_ULong wc = 0x20b9f; // not found (0xd842df9f bad wc vs)
FT_Int gid = FT_Face_GetCharVariantIndex(face, wc, 0x000e0100); // vs
fprintf(stderr, "test VS id: %08x: %08x\a\n", wc, gid); // 0x00000000
wc = 0x20b9f; // found 0x00003153 (0xd842df9f bad wc)
if(!gid) gid = FT_Get_Char_Index(face, wc);
fprintf(stderr, "test gid id: %08x: %08x\a\n", wc, gid); // 0x00003153
}
#endif
FT_GlyphSlot slot = face->glyph;
#if 0
cv::Point loc = pos; // copy
#else
int locx = 0;
#endif
#if 1
#if 0
// 鷗 u9dd7 𠮟 u20b9f = ud842 udf9f 漣 u6f23 芦 u82a6
// const wchar_t *wcs = L"DF麗雅宋 鷗𠮟 PdCaVAWlqg"; // test surrogate
const wchar_t *wcs = L"DF芦鷗𠮟漣龜 Å î PdCaVAWlqg"; // test VS
// const wchar_t *wcs = L"DF麗雅宋 PdCaVAWlqg"; // test kerning
// const wchar_t *wcs = L"Consolas PdCaVAWlqg"; // test proportional
// const wchar_t *wcs = L"Wha't a Beautiful font ++mikaP++ is !";
#else
const wchar_t *wcs = L"漣芦󠄀芦蘆鴎鷗叱𠮟祇󠄀祇󠄁祇祗衹"; // VS u82a6 ue0100
#endif
const wstring wtxt(wcs);
#endif
FT_Int wskip = 0; // check and skip SVS IVS
FT_UInt32 wc = 0, wn = 0, wvs = 0; // must be FT_UInt32
for(auto it = wtxt.begin(); it != wtxt.end(); ++it){
if(wskip){ --wskip; continue; }
FT_UInt32 wch = *it;
#if 0
if(wch == L' '){ loc.x += scale / 2; continue; } // not erase previous draw
#else
if(wch == L' '){ locx += scale / 2; continue; } // not erase previous draw
#endif
// care surrogate pair
FT_UInt16 uw = (FT_UInt16)wch; // for unsigned compare (wch is const)
if(uw < 0x0d800 || uw >= 0x0e000){ // not surrogate
wc = uw;
}else if(uw >= 0x0d800 && uw < 0x0dc00){ // first upper
wc = uw;
continue;
}else if(uw >= 0x0dc00 && uw < 0x0e000){ // second lower
if(!(wc >= 0x0d800 && wc < 0x0dc00)){
fprintf(stderr, "first surrogate out of bounds %08x %08x\a\n", wc, uw);
}else{ // output wc >= u10000 && wc <= u10ffff (4bit+01 16bit)
wc = 0x10000 + ((wc - 0x0d800) << 10) | (uw - 0x0dc00); // 10bit 10bit
// wc = 0x20b9f; // debug only
}
}
#if 0 // debug only
// wc = L'\u9b31'; // for test any glyph re write it (anti optimize) 0x3760
// wc = static_cast<FT_ULong>(L'\u20b9f'); // set bad value wc==0x20b9
// wc = static_cast<FT_ULong>(L'𠮟'); // set bad value wc==0xd842
wc = 0x20b9f; // OK
#endif
if(it + 1 != wtxt.end()){ wn = *(it + 1);
if(wn == 0xdb40){ // NOT (wn >= 0x0d800 && wn < 0x0dc00){ // first upper
wvs = wn;
if(it + 2 != wtxt.end()){ wn = *(it + 2);
if(wn >= 0x0dd00 && wn < 0x0ddf0){ // NOT (wn >= 0x0dc00 && wn < 0x0e000){ // second lower
wskip = 2;
wvs = 0x10000 + ((wvs - 0x0d800) << 10) | (wn - 0x0dc00); // 10 10
}else{
wvs = 0;
}
}else{
wvs = 0;
}
}
}
FT_Int gid = 0;
#if 1 // wc=UInt32,([1(or2)]==db40),(dd00<=[2(or3)]<ddf0) ue0100-ue01ef
if(wvs){
for(auto &v : vsel){
if(v == wvs) gid = FT_Face_GetCharVariantIndex(face, wc, v); // VS
#if 1 // break
if(gid) break;
#else // display all VS
if(gid) fprintf(stderr, "VS %08x: %08x: %08x\n", v, wc, gid);
#endif
}
wvs = 0;
}
#endif
if(!gid) gid = FT_Get_Char_Index(face, wc);
#if 1
if(!gid) fprintf(stderr, "glyph id: %08x: %08x\a\n", wc, gid);
#endif
// | FT_LOAD_NO_SCALE | FT_LOAD_NO_AUTOHINT
// FT_ULong flg = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP; // get outline
FT_ULong flg = FT_LOAD_RENDER; // get bitmap direct
error = FT_Load_Glyph(face, gid, flg);
if(error){
fprintf(stderr, "error: %d\a\n", error);
}else{
if(slot->format == FT_GLYPH_FORMAT_OUTLINE){
#if 1
fprintf(stdout, "slot->outline\n");
#endif
FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); // FT_RENDER_MODE_LCD
#if 0
loc.x += ftDrawFontSlotBitmap(dst, slot, loc, col);
#else
locx += drawBitmap(slot);
#endif
}else if(slot->format == FT_GLYPH_FORMAT_BITMAP){
#if 1
fprintf(stdout, "slot->bitmap\n");
#endif
#if 0
loc.x += ftDrawFontSlotBitmap(dst, slot, loc, col);
#else
locx += drawBitmap(slot);
#endif
}else{
fprintf(stderr, "slot unknown\a\n");
}
}
}
FT_Done_Face(face);
FT_Done_FreeType(library);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment