Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use HarfBuzz to get top-to-bottom glyph variants #41

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ set(CMAKE_CXX_FLAGS "-Wno-c++11-narrowing")

find_package(Boost 1.73 REQUIRED)
find_package(Freetype REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(HARFBUZZ REQUIRED harfbuzz)

include_directories(vendor/cxxopts/include)
include_directories(vendor/filesystem/include)
include_directories(vendor/sdf-glyph-foundry/include)
include_directories(vendor/protozero/include)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${FREETYPE_INCLUDE_DIRS})
include_directories(${HARFBUZZ_INCLUDE_DIRS})

add_executable(font-maker main.cpp)
target_link_libraries(font-maker ${FREETYPE_LIBRARIES})
target_link_libraries(font-maker ${FREETYPE_LIBRARIES} ${HARFBUZZ_LIBRARIES})

set_property(TARGET font-maker PROPERTY CXX_STANDARD 17)
6 changes: 0 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,4 @@ Use `./build_wasm.sh PATH_TO_INCLUDE_DIR` to build the WASM output, where `PATH_
```
cmake .
make
```

# Running command line

```
./font-maker --name "Noto Sans" output File1.ttf File2.ttf
```
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM ubuntu:22.04

RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y libboost-all-dev cmake clang libfreetype6-dev pkg-config g++ libharfbuzz-dev git && \
rm -rf /var/lib/apt/lists/*

WORKDIR /root
113 changes: 89 additions & 24 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,84 @@
#include "cxxopts.hpp"
#endif
#include <iostream>
#include <hb.h>
#include <hb-ft.h>

using namespace std;

void do_codepoint(protozero::pbf_writer &parent, std::vector<FT_Face> &faces, FT_ULong char_code) {
for (auto const &face : faces) {
FT_UInt char_index = FT_Get_Char_Index(face, char_code);
if (char_index > 0) {
FT_UInt get_glyph_index(hb_font_t *hb_font, hb_buffer_t *buffer, FT_ULong char_code, bool top_to_bottom) {
FT_UInt result = 0;
uint32_t utf32_char = static_cast<uint32_t>(char_code);
hb_buffer_clear_contents(buffer);
hb_buffer_add_utf32(buffer, &utf32_char, 1, 0, 1);
if (top_to_bottom) {
hb_buffer_set_direction(buffer, HB_DIRECTION_TTB);
}
hb_buffer_guess_segment_properties(buffer);
hb_shape(hb_font, buffer, nullptr, 0);
unsigned int glyph_count;
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
if (glyph_count > 0) {
result = glyph_info[0].codepoint;
}
return result;
}


FT_UInt get_glyph_index(const std::string &font_path, FT_ULong char_code, bool top_to_bottom) {
FT_UInt result = 0;
FT_Library ft_library;
if (FT_Init_FreeType(&ft_library)) {
std::cerr << "Failed to initialize FreeType library." << std::endl;
return result;
}
FT_Face face;
if (FT_New_Face(ft_library, font_path.c_str(), 0, &face)) {
std::cerr << "Failed to load font: " << font_path << std::endl;
FT_Done_FreeType(ft_library);
return result;
}
hb_font_t *hb_font = hb_ft_font_create(face, nullptr);
hb_buffer_t *buffer = hb_buffer_create();

result = get_glyph_index(hb_font, buffer, char_code, top_to_bottom);

hb_buffer_destroy(buffer);
hb_font_destroy(hb_font);
FT_Done_Face(face);
FT_Done_FreeType(ft_library);
return result;
}



void do_codepoint(protozero::pbf_writer &parent, std::vector<hb_font_t*> hb_fonts, hb_buffer_t *hb_buffer, FT_ULong char_code) {

for (auto hb_font : hb_fonts) {
bool top_to_bottom = true;
FT_UInt glyph_index = get_glyph_index(hb_font, hb_buffer, char_code, top_to_bottom);
// FT_UInt char_index = 0; //get_glyph_index(face, char_code, false);
FT_Face face = hb_ft_font_get_face(hb_font);
if (glyph_index > 0) {
sdf_glyph_foundry::glyph_info glyph;
glyph.glyph_index = char_index;
glyph.glyph_index = glyph_index;
sdf_glyph_foundry::RenderSDF(glyph, 24, 3, 0.25, face);

string glyph_data;
protozero::pbf_writer glyph_message{glyph_data};

// direct type conversions, no need for checking or casting
glyph_message.add_uint32(3,glyph.width);
glyph_message.add_uint32(4,glyph.height);
glyph_message.add_sint32(5,glyph.left);
glyph_message.add_uint32(3, glyph.width);
glyph_message.add_uint32(4, glyph.height);
glyph_message.add_sint32(5, glyph.left);

// conversions requiring checks, for safety and correctness

// shortening conversion
if (char_code > numeric_limits<uint32_t>::max()) {
throw runtime_error("Invalid value for char_code: too large");
} else {
glyph_message.add_uint32(1,static_cast<uint32_t>(char_code));
glyph_message.add_uint32(1, static_cast<uint32_t>(char_code));
}

// node-fontnik uses glyph.top - glyph.ascender, assuming that the baseline
Expand All @@ -48,36 +100,36 @@ void do_codepoint(protozero::pbf_writer &parent, std::vector<FT_Face> &faces, FT
if (top < numeric_limits<int32_t>::min() || top > numeric_limits<int32_t>::max()) {
throw runtime_error("Invalid value for glyph.top-25");
} else {
glyph_message.add_sint32(6,top);
glyph_message.add_sint32(6, top);
}

// double to uint
if (glyph.advance < numeric_limits<uint32_t>::min() || glyph.advance > numeric_limits<uint32_t>::max()) {
throw runtime_error("Invalid value for glyph.top-glyph.ascender");
} else {
glyph_message.add_uint32(7,static_cast<uint32_t>(glyph.advance));
glyph_message.add_uint32(7, static_cast<uint32_t>(glyph.advance));
}

if (glyph.width > 0) {
glyph_message.add_bytes(2,glyph.bitmap);
glyph_message.add_bytes(2, glyph.bitmap);
}
parent.add_message(3,glyph_data);
parent.add_message(3, glyph_data);
return;
}
}
}

string do_range(std::vector<FT_Face> &faces, std::string name, unsigned start, unsigned end) {
string do_range(std::vector<hb_font_t*> hb_fonts, hb_buffer_t *hb_buffer, std::string name, unsigned start, unsigned end) {
string fontstack_data;
{
protozero::pbf_writer fontstack{fontstack_data};

fontstack.add_string(1,name);
fontstack.add_string(2,to_string(start) + "-" + to_string(end));
fontstack.add_string(1, name);
fontstack.add_string(2, to_string(start) + "-" + to_string(end));

for (unsigned x = start; x <= end; x++) {
FT_ULong char_code = x;
do_codepoint(fontstack,faces, x);
do_codepoint(fontstack, hb_fonts, hb_buffer, x);
}
}

Expand All @@ -92,6 +144,8 @@ string do_range(std::vector<FT_Face> &faces, std::string name, unsigned start, u
struct fontstack {
FT_Library library;
std::vector<FT_Face> *faces;
std::vector<hb_font_t *> *hb_fonts;
hb_buffer_t *hb_buffer;
std::vector<char *> *data;
std::set<std::string> *seen_face_names;
std::string *name;
Expand All @@ -107,6 +161,8 @@ extern "C" {
fontstack *create_fontstack(const char *name) {
fontstack *f = (fontstack *)malloc(sizeof(fontstack));
f->faces = new std::vector<FT_Face>;
f->hb_fonts = new std::vector<hb_font_t *>;
f->hb_buffer = hb_buffer_create();
f->data = new std::vector<char *>;
f->seen_face_names = new std::set<std::string>;

Expand Down Expand Up @@ -141,6 +197,7 @@ extern "C" {
double size = 24 * scale_factor;
FT_Set_Char_Size(face, 0, static_cast<FT_F26Dot6>(size * (1 << 6)), 0, 0);
f->faces->push_back(face);
f->hb_fonts->push_back(hb_ft_font_create(face, nullptr));

if (f->auto_name) {
std::string combined_name = std::string(face->family_name);
Expand All @@ -159,6 +216,11 @@ extern "C" {
}

void free_fontstack(fontstack *f) {
hb_buffer_destroy(f->hb_buffer);

for (auto hb_font : *f->hb_fonts) {
hb_font_destroy(hb_font);
}
for (auto fc : *f->faces) {
FT_Done_Face(fc);
}
Expand All @@ -173,17 +235,17 @@ extern "C" {
}

char *fontstack_name(fontstack *f) {
char *fname = (char *)malloc((f->name->size()+1) * sizeof(char));
strcpy(fname,f->name->c_str());
char *fname = (char *)malloc((f->name->size() + 1) * sizeof(char));
strcpy(fname, f->name->c_str());
return fname;
}

glyph_buffer *generate_glyph_buffer(fontstack *f, uint32_t start_codepoint) {
string result = do_range(*f->faces,*f->name,start_codepoint,start_codepoint+255);
string result = do_range(*f->hb_fonts, f->hb_buffer, *f->name, start_codepoint, start_codepoint + 255);

glyph_buffer *g = (glyph_buffer *)malloc(sizeof(glyph_buffer));
char *result_ptr = (char *)malloc(result.size());
result.copy(result_ptr,result.size());
result.copy(result_ptr, result.size());
g->data = result_ptr;
g->size = result.size();
return g;
Expand All @@ -206,14 +268,17 @@ extern "C" {
#ifndef EMSCRIPTEN
int main(int argc, char *argv[])
{
// cout << get_glyph_index("NotoSansJP-Regular.ttf", 0x30FC, false) << endl;
// return;

cxxopts::Options cmd_options("font-maker", "Create font PBFs from TTFs or OTFs.");
cmd_options.add_options()
("output", "Output directory (to be created, must not already exist)", cxxopts::value<string>())
("fonts", "Input font(s) (as TTF or OTF)", cxxopts::value<vector<string>>())
("name", "Override output fontstack name", cxxopts::value<string>())
("help", "Print usage")
;
cmd_options.positional_help("<OUTPUT_DIR> <INPUT_FONT> [INPUT_FONT2 ...]");
cmd_options.positional_help("--output OUTPUT_DIR --fonts FONT1 FONT1 --name FONTSTACK_NAME");
cmd_options.parse_positional({"output","fonts"});
auto result = cmd_options.parse(argc, argv);
if (result.count("help"))
Expand Down Expand Up @@ -246,15 +311,15 @@ int main(int argc, char *argv[])
f->data->push_back(buffer);
file.read(buffer, size);
std::cout << "Adding " << font << std::endl;
fontstack_add_face(f,(FT_Byte *)buffer,size);
fontstack_add_face(f, (FT_Byte *)buffer,size);
}

std::string fname{fontstack_name(f)};

ghc::filesystem::create_directory(output_dir + "/" + fname);

for (int i = 0; i < 65536; i += 256) {
glyph_buffer *g = generate_glyph_buffer(f,i);
glyph_buffer *g = generate_glyph_buffer(f, i);
char *data = glyph_buffer_data(g);
uint32_t buffer_size = glyph_buffer_size(g);

Expand Down
2 changes: 2 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rm -rf output_dir
./font-maker