-
-
Save ebraminio/b1f868369b4ff18dd6aebc53c26ec555 to your computer and use it in GitHub Desktop.
Simple Font Viewer based on HarfBuzz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// clang a.c -DHB_NO_MT -fno-exceptions -fno-rtti -fno-threadsafe-statics -fvisibility-inlines-hidden ../harfbuzz/src/harfbuzz.cc unix/*.c common/*.c `pkg-config --cflags --libs gtk+-3.0` -lm -ldl -Oz -flto -fuse-ld=lld -Wl,--lto-O3 -Wl,--strip-all -Wl,--gc-sections && ./a.out | |
// a.out = 646K, containing libui and harfbuzz shaping and draw | |
// i686-w64-mingw32-g++ a.c -DHB_NO_MT ../harfbuzz/src/harfbuzz.cc windows/*.cpp common/*.c -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lmsimg32 -lcomdlg32 -ld2d1 -ldwrite -lole32 -loleaut32 -loleacc -luuid -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "ui.h" | |
#include "../harfbuzz/src/hb.h" | |
#include "../harfbuzz/src/hb-ot.h" | |
uiWindow *mainwin; | |
uiArea *canvas; | |
uiAreaHandler handler; | |
uiEditableCombobox *ecbox; | |
uiForm *entryForm; | |
unsigned size = 200; | |
hb_variation_t *vars = NULL; | |
unsigned num_coords = 0; | |
hb_font_t *font; | |
hb_draw_funcs_t *draw_funcs; | |
typedef struct | |
{ | |
uiDrawPath *path; | |
uiDrawContext *context; | |
uiDrawBrush *brush; | |
hb_position_t x_offset; | |
hb_position_t y_offset; | |
} draw_user_data_t; | |
static void _move_to(hb_position_t to_x, hb_position_t to_y, draw_user_data_t *user_data) | |
{ | |
user_data->path = uiDrawNewPath(uiDrawFillModeWinding); // uiDrawFillModeAlternate | |
uiDrawPathNewFigure(user_data->path, (user_data->x_offset + to_x) / 64., -(user_data->y_offset + to_y) / 64.); | |
} | |
static void _line_to(hb_position_t to_x, hb_position_t to_y, draw_user_data_t *user_data) | |
{ | |
uiDrawPathLineTo(user_data->path, (user_data->x_offset + to_x) / 64., -(user_data->y_offset + to_y) / 64.); | |
} | |
static void _cubic_to(hb_position_t control1_x, hb_position_t control1_y, | |
hb_position_t control2_x, hb_position_t control2_y, | |
hb_position_t to_x, hb_position_t to_y, draw_user_data_t *user_data) | |
{ | |
uiDrawPathBezierTo(user_data->path, (user_data->x_offset + control1_x) / 64., -(user_data->y_offset + control1_y) / 64., | |
(user_data->x_offset + control2_x) / 64., -(user_data->y_offset + control2_y) / 64., | |
(user_data->x_offset + to_x) / 64., -(user_data->y_offset + to_y) / 64.); | |
} | |
static void _close_path(draw_user_data_t *user_data) | |
{ | |
uiDrawPathEnd(user_data->path); | |
uiDrawFill(user_data->context, user_data->path, user_data->brush); | |
uiDrawFreePath(user_data->path); | |
} | |
static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) | |
{ | |
hb_font_set_variations(font, vars, num_coords); | |
hb_font_set_scale(font, size * 64, size * 64); | |
// fill the area with white | |
uiDrawBrush brush = {0}; | |
brush.R = brush.G = brush.B = brush.A = 1; | |
uiDrawPath *path = uiDrawNewPath(uiDrawFillModeAlternate); | |
uiDrawPathAddRectangle(path, 0, 0, p->AreaWidth, p->AreaHeight); | |
uiDrawPathEnd(path); | |
uiDrawFill(p->Context, path, &brush); | |
uiDrawFreePath(path); | |
// now transform the coordinate space | |
uiDrawMatrix m; | |
uiDrawMatrixSetIdentity(&m); | |
uiDrawMatrixTranslate(&m, 20, 20 + size); | |
uiDrawTransform(p->Context, &m); | |
// HarfBuzz shape and draw | |
brush.R = brush.G = brush.B = 0; | |
draw_user_data_t draw = { | |
.context = p->Context, | |
.brush = &brush | |
}; | |
hb_buffer_t *buffer = hb_buffer_create(); | |
hb_buffer_add_utf8(buffer, uiEditableComboboxText(ecbox), -1, 0, -1); | |
hb_buffer_guess_segment_properties(buffer); | |
hb_shape(font, buffer, NULL, 0); | |
unsigned length; | |
hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, &length); | |
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL); | |
hb_position_t x_advance = 0, y_advance = 0; | |
for (unsigned i = 0; i < length; ++i) | |
{ | |
draw.x_offset = x_advance + positions[i].x_offset; | |
draw.y_offset = y_advance + positions[i].y_offset; | |
hb_font_draw_glyph(font, infos[i].codepoint, draw_funcs, &draw); | |
x_advance += positions[i].x_advance; | |
y_advance += positions[i].y_advance; | |
} | |
hb_buffer_destroy(buffer); | |
} | |
static int onClosing(uiWindow *w, void *data) | |
{ | |
uiControlDestroy(uiControl(mainwin)); | |
uiQuit(); | |
return 0; | |
} | |
static int shouldQuit(void *data) | |
{ | |
uiControlDestroy(uiControl(mainwin)); | |
return 1; | |
} | |
static void onEditChange(uiEditableCombobox *ecbox, void *data) | |
{ | |
uiAreaQueueRedrawAll(canvas); | |
} | |
static void onVariationSliderChange(uiSlider *slider, void *data) | |
{ | |
((hb_variation_t *) data)->value = uiSliderValue(slider); | |
uiAreaQueueRedrawAll(canvas); | |
} | |
static void onFontSizeSliderChange(uiSlider *slider, void *data) | |
{ | |
size = uiSliderValue(slider); | |
uiAreaQueueRedrawAll(canvas); | |
} | |
static void openFont(char *filename) { | |
for (unsigned i = 0; i < num_coords; ++i) { | |
uiFormDelete(entryForm, /*TODO: ugly*/2 + i); | |
} | |
hb_blob_t *blob = hb_blob_create_from_file(filename); | |
hb_face_t *face = hb_face_create(blob, 0); | |
font = hb_font_create(face); | |
hb_face_destroy(face); | |
hb_blob_destroy(blob); | |
num_coords = hb_ot_var_get_axis_count(face); | |
vars = (hb_variation_t *) calloc(num_coords, sizeof (hb_variation_t)); | |
hb_ot_var_axis_info_t *var_infos = (hb_ot_var_axis_info_t *) calloc(num_coords, sizeof (hb_ot_var_axis_info_t)); | |
hb_ot_var_get_axis_infos(face, 0, &num_coords, var_infos); | |
for (unsigned i = 0; i < num_coords; ++i) { | |
vars[i].tag = var_infos[i].tag; | |
vars[i].value = var_infos[i].default_value; | |
uiSlider *slider = uiNewSlider(var_infos[i].min_value, var_infos[i].max_value); | |
uiSliderSetValue(slider, var_infos[i].default_value); | |
uiSliderOnChanged(slider, onVariationSliderChange, &vars[i]); | |
char tag[5] = {0}; | |
hb_tag_to_string(var_infos[i].tag, tag); | |
uiFormAppend(entryForm, tag, uiControl(slider), 0); | |
} | |
free(var_infos); | |
} | |
static void onOpenFileClicked(uiButton *b, void *data) | |
{ | |
char *filename = uiOpenFile(mainwin); | |
if (filename == NULL) return; | |
openFont(filename); | |
uiFreeText(filename); | |
} | |
static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) {} | |
static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) {} | |
static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) {} | |
static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) { return 0; } | |
int main(int argc, char **argv) | |
{ | |
draw_funcs = hb_draw_funcs_create(); | |
hb_draw_funcs_set_move_to_func(draw_funcs, (hb_draw_move_to_func_t) _move_to); | |
hb_draw_funcs_set_line_to_func(draw_funcs, (hb_draw_line_to_func_t) _line_to); | |
// hb_draw_funcs_set_quadratic_to_func (draw_funcs, NULL); let's translate quad calls to cubic | |
hb_draw_funcs_set_cubic_to_func(draw_funcs, (hb_draw_cubic_to_func_t) _cubic_to); | |
hb_draw_funcs_set_close_path_func(draw_funcs, (hb_draw_close_path_func_t) _close_path); | |
handler.Draw = handlerDraw; | |
uiInitOptions o = {0}; | |
const char *err = uiInit(&o); | |
if (err != NULL) { | |
fprintf(stderr, "error initializing ui: %s\n", err); | |
uiFreeInitError(err); | |
return 1; | |
} | |
uiOnShouldQuit(shouldQuit, NULL); | |
mainwin = uiNewWindow("HarfBuzz demo app", 640, 480, 1); | |
uiWindowSetMargined(mainwin, 1); | |
uiWindowOnClosing(mainwin, onClosing, NULL); | |
uiBox *hbox = uiNewHorizontalBox(); | |
uiBoxSetPadded(hbox, 1); | |
uiWindowSetChild(mainwin, uiControl(hbox)); | |
uiBox *vbox = uiNewVerticalBox(); | |
uiBoxSetPadded(vbox, 1); | |
uiBoxAppend(hbox, uiControl(vbox), 0); | |
uiGroup *group = uiNewGroup("Configurations"); | |
uiGroupSetMargined(group, 1); | |
uiBoxAppend(vbox, uiControl(group), 0); | |
entryForm = uiNewForm(); | |
uiFormSetPadded(entryForm, 1); | |
uiGroupSetChild(group, uiControl(entryForm)); | |
ecbox = uiNewEditableCombobox(); | |
uiEditableComboboxSetText(ecbox, "Sample Text"); | |
uiEditableComboboxAppend(ecbox, "Sample Text"); | |
uiEditableComboboxAppend(ecbox, "مَََََتنِ آزمایشی"); | |
uiEditableComboboxOnChanged(ecbox, onEditChange, NULL); | |
uiFormAppend(entryForm, "Text", uiControl(ecbox), 0); | |
uiSlider *slider = uiNewSlider(12, 400); | |
uiSliderSetValue(slider, size); | |
uiSliderOnChanged(slider, onFontSizeSliderChange, NULL); | |
uiFormAppend(entryForm, "Font Size", uiControl(slider), 0); | |
openFont(argc > 1 ? argv[1] : ""); | |
uiButton *button = uiNewButton("Browse Font Files"); | |
uiButtonOnClicked(button, onOpenFileClicked, NULL); | |
uiBoxAppend(vbox, uiControl(button), 0); | |
handler.Draw = handlerDraw; | |
handler.MouseEvent = handlerMouseEvent; | |
handler.MouseCrossed = handlerMouseCrossed; | |
handler.DragBroken = handlerDragBroken; | |
handler.KeyEvent = handlerKeyEvent; | |
canvas = uiNewArea(&handler); | |
uiBoxAppend(hbox, uiControl(canvas), 1); | |
uiControlShow(uiControl(mainwin)); | |
uiMain(); | |
uiUninit(); | |
free(vars); | |
hb_draw_funcs_destroy(draw_funcs); | |
hb_font_destroy(font); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment