add a lib for rendering text in sdl2 and rules to make in makefile

This commit is contained in:
2026-01-21 14:45:11 -06:00
parent f3c0aafe2f
commit e7d9c013f4
1754 changed files with 850297 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
# Process this file with automake to produce Makefile.in
NULL =
EXTRA_DIST =
CLEANFILES =
DISTCLEANFILES =
MAINTAINERCLEANFILES =
EXTRA_DIST += meson.build
include Makefile.sources
# Convenience targets:
lib:
@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
libs:
@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
bin_PROGRAMS =
AM_CPPFLAGS = \
-DHB_DISABLE_DEPRECATED \
-I$(top_srcdir)/src/ \
-I$(top_builddir)/src/ \
$(GLIB_CFLAGS) \
$(FREETYPE_CFLAGS) \
$(CAIRO_FT_CFLAGS) \
$(CHAFA_CFLAGS) \
$(NULL)
LDADD = \
$(top_builddir)/src/libharfbuzz.la \
-lm \
$(GLIB_LIBS) \
$(FREETYPE_LIBS) \
$(NULL)
if HAVE_GLIB
if HAVE_CAIRO
hb_view_SOURCES = $(HB_VIEW_sources)
hb_view_LDADD = \
$(top_builddir)/src/libharfbuzz-cairo.la \
$(LDADD) \
$(CAIRO_LIBS) \
$(CAIRO_FT_LIBS) \
$(CHAFA_LIBS) \
$(NULL)
bin_PROGRAMS += hb-view
endif # HAVE_CAIRO
hb_shape_SOURCES = $(HB_SHAPE_sources)
bin_PROGRAMS += hb-shape
hb_info_SOURCES = $(HB_INFO_sources)
hb_info_LDADD = \
$(LDADD) \
$(NULL)
if HAVE_GOBJECT
hb_info_LDADD += \
$(top_builddir)/src/libharfbuzz-gobject.la \
$(GOBJECT_LIBS) \
$(NULL)
endif # HAVE_GOBJECT
if HAVE_CHAFA
hb_info_LDADD += $(CHAFA_LIBS)
endif # HAVE_CHAFA
bin_PROGRAMS += hb-info
hb_subset_SOURCES = $(HB_SUBSET_CLI_sources)
hb_subset_LDADD = \
$(top_builddir)/src/libharfbuzz-subset.la \
$(LDADD)
bin_PROGRAMS += hb-subset
hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources)
bin_PROGRAMS += hb-ot-shape-closure
endif # HAVE_GLIB
#if HAVE_FONTCONFIG
#hb_fc_list_SOURCES = \
# hb-fc.cc \
# hb-fc.h \
# hb-fc-list.c \
# $(NULL)
#hb_fc_list_LDADD = \
# $(LDADD) \
# $(FONTCONFIG_LIBS) \
# $(NULL)
#bin_PROGRAMS += hb-fc-list
#endif # HAVE_FONTCONFIG
-include $(top_srcdir)/git.mk

View File

@@ -0,0 +1,59 @@
HB_VIEW_sources = \
ansi-print.hh \
face-options.hh \
font-options.hh \
hb-view.cc \
helper-cairo-ansi.hh \
helper-cairo-ft.hh \
helper-cairo.hh \
main-font-text.hh \
options.hh \
output-options.hh \
shape-consumer.hh \
shape-options.hh \
text-options.hh \
view-cairo.hh \
view-options.hh \
$(NULL)
HB_SHAPE_sources = \
batch.hh \
face-options.hh \
font-options.hh \
hb-shape.cc \
main-font-text.hh \
options.hh \
output-options.hh \
shape-consumer.hh \
shape-format.hh \
shape-options.hh \
shape-output.hh \
text-options.hh \
$(NULL)
HB_INFO_sources = \
batch.hh \
face-options.hh \
font-options.hh \
hb-info.cc \
options.hh \
$(NULL)
HB_SUBSET_CLI_sources = \
batch.hh \
face-options.hh \
hb-subset.cc \
main-font-text.hh \
options.hh \
output-options.hh \
text-options.hh \
$(NULL)
HB_OT_SHAPE_CLOSURE_sources = \
face-options.hh \
font-options.hh \
hb-ot-shape-closure.cc \
main-font-text.hh \
options.hh \
text-options.hh \
$(NULL)

View File

@@ -0,0 +1,442 @@
/*
* Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef ANSI_PRINT_HH
#define ANSI_PRINT_HH
#include "hb.hh"
#include <cairo.h>
#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#if defined (_MSC_VER) && (_MSC_VER < 1800)
static inline long int
lround (double x)
{
if (x >= 0)
return floor (x + 0.5);
else
return ceil (x - 0.5);
}
#endif
#define CELL_W 8
#define CELL_H (2 * CELL_W)
struct color_diff_t
{
int dot (const color_diff_t &o)
{ return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; }
int v[4];
};
struct color_t
{
static color_t from_ansi (unsigned int x)
{
color_t c = {(0xFFu<<24) | ((0xFFu*(x&1))<<16) | ((0xFFu*((x >> 1)&1))<<8) | (0xFFu*((x >> 2)&1))};
return c;
}
unsigned int to_ansi ()
{
return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4);
}
color_diff_t diff (const color_t &o)
{
color_diff_t d;
for (unsigned int i = 0; i < 4; i++)
d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF);
return d;
}
uint32_t v;
};
struct image_t
{
public:
image_t (unsigned int width_,
unsigned int height_,
const uint32_t *data_,
unsigned int stride_) :
width (width_),
height (height_),
own_data (false),
data ((color_t *) data_),
stride (stride_) {}
image_t (unsigned int width_,
unsigned int height_) :
width (width_),
height (height_),
own_data (true),
data ((color_t *) malloc (sizeof (data[0]) * width * height)),
stride (width) {}
~image_t ()
{ if (own_data) free (data); }
color_t &operator () (unsigned int x, unsigned int y)
{ return data[x + y * stride]; }
color_t operator () (unsigned int x, unsigned int y) const
{ return data[x + y * stride]; }
void
copy_sub_image (const image_t &s,
unsigned int x, unsigned int y,
unsigned int w, unsigned int h)
{
assert (x < width);
assert (y < height);
for (unsigned int row = 0; row < h; row++) {
color_t *p = data + x + hb_min (y + row, height - 1) * stride;
color_t *q = s.data + row * s.stride;
if (x + w <= width)
for (unsigned int col = 0; col < w; col++)
*q++ = *p++;
else {
unsigned int limit = width - x;
for (unsigned int col = 0; col < limit; col++)
*q++ = *p++;
p--;
for (unsigned int col = limit; col < w; col++)
*q++ = *p;
}
}
}
const unsigned int width;
const unsigned int height;
private:
bool own_data;
color_t * const data;
const unsigned int stride;
};
struct biimage_t
{
public:
biimage_t (unsigned int width, unsigned int height) :
width (width),
height (height),
bg (0), fg (0), unicolor (true),
data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {}
~biimage_t ()
{ free (data); }
void set (const image_t &image)
{
assert (image.width == width);
assert (image.height == height);
int freq[8] = {0};
for (unsigned int y = 0; y < height; y++)
for (unsigned int x = 0; x < width; x++) {
color_t c = image (x, y);
freq[c.to_ansi ()]++;
}
bg = 0;
for (unsigned int i = 1; i < 8; i++)
if (freq[bg] < freq[i])
bg = i;
fg = 8;
for (unsigned int i = 0; i < 8; i++)
if (i != bg && (fg == 8 || freq[fg] < freq[i]))
fg = i;
if (freq[fg] == 0) {
fg = bg;
unicolor = true;
}
else
unicolor = false;
/* Set the data... */
if (unicolor) {
memset (data, 0, sizeof (data[0]) * width * height);
return;
}
color_t bgc = color_t::from_ansi (bg);
color_t fgc = color_t::from_ansi (fg);
color_diff_t diff = fgc.diff (bgc);
double dd = sqrt (diff.dot (diff));
for (unsigned int y = 0; y < height; y++)
for (unsigned int x = 0; x < width; x++) {
double d = sqrt (diff.dot (image (x, y).diff (bgc)));
(*this)(x, y) = d <= 0 ? 0 : d >= dd ? 255 : lround (d / dd * 255.);
}
}
uint8_t &operator () (unsigned int x, unsigned int y)
{ return data[x + y * width]; }
uint8_t operator () (unsigned int x, unsigned int y) const
{ return data[x + y * width]; }
const unsigned int width;
const unsigned int height;
unsigned int bg;
unsigned int fg;
bool unicolor;
private:
uint8_t * const data;
};
static const char *
block_best (const biimage_t &bi, bool *inverse)
{
assert (bi.width <= CELL_W);
assert (bi.height <= CELL_H);
unsigned int score = UINT_MAX;
unsigned int row_sum[CELL_H] = {0};
unsigned int col_sum[CELL_W] = {0};
unsigned int row_sum_i[CELL_H] = {0};
unsigned int col_sum_i[CELL_W] = {0};
unsigned int quad[2][2] = {{0}};
unsigned int quad_i[2][2] = {{0}};
unsigned int total = 0;
unsigned int total_i = 0;
for (unsigned int y = 0; y < bi.height; y++)
for (unsigned int x = 0; x < bi.width; x++) {
unsigned int c = bi (x, y);
unsigned int c_i = 255 - c;
row_sum[y] += c;
row_sum_i[y] += c_i;
col_sum[x] += c;
col_sum_i[x] += c_i;
quad[2 * y / bi.height][2 * x / bi.width] += c;
quad_i[2 * y / bi.height][2 * x / bi.width] += c_i;
total += c;
total_i += c_i;
}
/* Make the sums cumulative */
for (unsigned int i = 1; i < bi.height; i++) {
row_sum[i] += row_sum[i - 1];
row_sum_i[i] += row_sum_i[i - 1];
}
for (unsigned int i = 1; i < bi.width; i++) {
col_sum[i] += col_sum[i - 1];
col_sum_i[i] += col_sum_i[i - 1];
}
const char *best_c = " ";
/* Maybe empty is better! */
if (total < score) {
score = total;
*inverse = false;
best_c = " ";
}
/* Maybe full is better! */
if (total_i < score) {
score = total_i;
*inverse = true;
best_c = " ";
}
/* Find best lower line */
if (1) {
unsigned int best_s = UINT_MAX;
bool best_inv = false;
int best_i = 0;
for (unsigned int i = 0; i < bi.height - 1; i++)
{
unsigned int s;
s = row_sum[i] + total_i - row_sum_i[i];
if (s < best_s) {
best_s = s;
best_i = i;
best_inv = false;
}
s = row_sum_i[i] + total - row_sum[i];
if (s < best_s) {
best_s = s;
best_i = i;
best_inv = true;
}
}
if (best_s < score) {
static const char *lower[7] = {"", "", "", "", "", "", ""};
unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.height);
if (1 <= which && which <= 7) {
score = best_s;
*inverse = best_inv;
best_c = lower[7 - which];
}
}
}
/* Find best left line */
if (1) {
unsigned int best_s = UINT_MAX;
bool best_inv = false;
int best_i = 0;
for (unsigned int i = 0; i < bi.width - 1; i++)
{
unsigned int s;
s = col_sum[i] + total_i - col_sum_i[i];
if (s < best_s) {
best_s = s;
best_i = i;
best_inv = true;
}
s = col_sum_i[i] + total - col_sum[i];
if (s < best_s) {
best_s = s;
best_i = i;
best_inv = false;
}
}
if (best_s < score) {
static const char *left [7] = {"", "", "", "", "", "", ""};
unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.width);
if (1 <= which && which <= 7) {
score = best_s;
*inverse = best_inv;
best_c = left[which - 1];
}
}
}
/* Find best quadrant */
if (1) {
unsigned int q = 0;
unsigned int qs = 0;
for (unsigned int i = 0; i < 2; i++)
for (unsigned int j = 0; j < 2; j++)
if (quad[i][j] > quad_i[i][j]) {
q += 1 << (2 * i + j);
qs += quad_i[i][j];
} else
qs += quad[i][j];
if (qs < score) {
const char *c = nullptr;
bool inv = false;
switch (q) {
case 1: c = ""; inv = true; break;
case 2: c = ""; inv = true; break;
case 4: c = ""; inv = false; break;
case 6: c = ""; inv = false; break;
case 7: c = ""; inv = false; break;
case 8: c = ""; inv = false; break;
case 9: c = ""; inv = false; break;
case 11: c = ""; inv = false; break;
case 13: c = ""; inv = false; break;
case 14: c = ""; inv = false; break;
}
if (c) {
score = qs;
*inverse = inv;
best_c = c;
}
}
}
return best_c;
}
static inline void
ansi_print_image_rgb24 (const uint32_t *data,
unsigned int width,
unsigned int height,
unsigned int stride,
cairo_write_func_t write_func,
void *closure)
{
image_t image (width, height, data, stride);
unsigned int rows = (height + CELL_H - 1) / CELL_H;
unsigned int cols = (width + CELL_W - 1) / CELL_W;
image_t cell (CELL_W, CELL_H);
biimage_t bi (CELL_W, CELL_H);
unsigned int last_bg = -1, last_fg = -1;
for (unsigned int row = 0; row < rows; row++)
{
for (unsigned int col = 0; col < cols; col++)
{
image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H);
bi.set (cell);
if (bi.unicolor)
{
if (last_bg != bi.bg)
{
char buf[] = "\033[40m";
buf[3] += bi.bg;
write_func (closure, (unsigned char *) buf, 5);
last_bg = bi.bg;
}
write_func (closure, (unsigned char *) " ", 1);
}
else
{
/* Figure out the closest character to the biimage */
bool inverse = false;
const char *c = block_best (bi, &inverse);
if (inverse)
{
if (last_bg != bi.fg || last_fg != bi.bg)
{
char buf[] = "\033[30;40m";
buf[3] += bi.bg;
buf[6] += bi.fg;
write_func (closure, (unsigned char *) buf, 8);
last_bg = bi.fg;
last_fg = bi.bg;
}
}
else
{
if (last_bg != bi.bg || last_fg != bi.fg)
{
char buf[] = "\033[40;30m";
buf[3] += bi.bg;
buf[6] += bi.fg;
write_func (closure, (unsigned char *) buf, 8);
last_bg = bi.bg;
last_fg = bi.fg;
}
}
write_func (closure, (unsigned char *) c, strlen (c));
}
}
write_func (closure, (unsigned char *) "\033[0m\n", 5); /* Reset */
last_bg = last_fg = -1;
}
}
#endif

View File

@@ -0,0 +1,75 @@
/*
* Copyright © 2021 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef BATCH_HH
#define BATCH_HH
#include "options.hh"
typedef int (*main_func_t) (int argc, char **argv);
template <typename main_t, bool report_status=false>
int
batch_main (int argc, char **argv)
{
if (argc == 2 && !strcmp (argv[1], "--batch"))
{
int ret = 0;
char buf[4092];
while (fgets (buf, sizeof (buf), stdin))
{
size_t l = strlen (buf);
if (l && buf[l - 1] == '\n') buf[l - 1] = '\0';
char *args[64];
argc = 0;
args[argc++] = argv[0];
char *p = buf, *e;
args[argc++] = p;
while ((e = strchr (p, ';')) && argc < (int) ARRAY_LENGTH (args))
{
*e++ = '\0';
while (*e == ';')
e++;
args[argc++] = p = e;
}
int result = main_t () (argc, args);
if (report_status)
fprintf (stdout, result == 0 ? "success\n" : "failure\n");
fflush (stdout);
ret = MAX (ret, result);
}
return ret;
}
int ret = main_t () (argc, argv);
if (report_status && ret != 0)
fprintf (stdout, "error: Operation failed. Probably a bug. File github issue.\n");
return ret;
}
#endif

View File

@@ -0,0 +1,142 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef FACE_OPTIONS_HH
#define FACE_OPTIONS_HH
#include "options.hh"
struct face_options_t
{
~face_options_t ()
{
g_free (font_file);
}
void set_face (hb_face_t *face_)
{ face = face_; }
void add_options (option_parser_t *parser);
void post_parse (GError **error);
static struct cache_t
{
~cache_t ()
{
g_free (font_path);
hb_blob_destroy (blob);
hb_face_destroy (face);
}
char *font_path = nullptr;
hb_blob_t *blob = nullptr;
unsigned face_index = (unsigned) -1;
hb_face_t *face = nullptr;
} cache;
char *font_file = nullptr;
unsigned face_index = 0;
hb_blob_t *blob = nullptr;
hb_face_t *face = nullptr;
};
face_options_t::cache_t face_options_t::cache {};
void
face_options_t::post_parse (GError **error)
{
if (!font_file)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"No font file set");
return;
}
assert (font_file);
const char *font_path = font_file;
if (0 == strcmp (font_path, "-"))
{
#if defined(_WIN32) || defined(__CYGWIN__)
setmode (fileno (stdin), O_BINARY);
font_path = "STDIN";
#else
font_path = "/dev/stdin";
#endif
}
if (!cache.font_path || 0 != strcmp (cache.font_path, font_path))
{
hb_blob_destroy (cache.blob);
cache.blob = hb_blob_create_from_file_or_fail (font_path);
free ((char *) cache.font_path);
cache.font_path = g_strdup (font_path);
if (!cache.blob)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"%s: Failed reading file", font_path);
return;
}
hb_face_destroy (cache.face);
cache.face = nullptr;
cache.face_index = (unsigned) -1;
}
if (cache.face_index != face_index)
{
hb_face_destroy (cache.face);
cache.face = hb_face_create (cache.blob, face_index);
cache.face_index = face_index;
}
blob = cache.blob;
face = cache.face;
}
void
face_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"},
{"face-index", 'y', 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"},
{nullptr}
};
parser->add_group (entries,
"face",
"Font-face options:",
"Options for the font face",
this);
}
#endif

View File

@@ -0,0 +1,381 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef FONT_OPTIONS_HH
#define FONT_OPTIONS_HH
#include "face-options.hh"
#ifdef HAVE_FREETYPE
#include <hb-ft.h>
#endif
#include <hb-ot.h>
#define FONT_SIZE_UPEM 0x7FFFFFFF
#define FONT_SIZE_NONE 0
extern const unsigned DEFAULT_FONT_SIZE;
extern const unsigned SUBPIXEL_BITS;
struct font_options_t : face_options_t
{
~font_options_t ()
{
#ifndef HB_NO_VAR
free (variations);
#endif
g_free (font_funcs);
hb_font_destroy (font);
}
void add_options (option_parser_t *parser);
void post_parse (GError **error);
hb_bool_t sub_font = false;
#ifndef HB_NO_VAR
hb_variation_t *variations = nullptr;
unsigned int num_variations = 0;
#endif
int x_ppem = 0;
int y_ppem = 0;
double ptem = 0.;
double x_embolden = 0.;
double y_embolden = 0.;
hb_bool_t embolden_in_place = false;
double slant = 0.;
unsigned int subpixel_bits = SUBPIXEL_BITS;
mutable double font_size_x = DEFAULT_FONT_SIZE;
mutable double font_size_y = DEFAULT_FONT_SIZE;
char *font_funcs = nullptr;
int ft_load_flags = 2;
unsigned int named_instance = HB_FONT_NO_VAR_NAMED_INSTANCE;
hb_font_t *font = nullptr;
};
static struct supported_font_funcs_t {
char name[4];
void (*func) (hb_font_t *);
} supported_font_funcs[] =
{
{"ot", hb_ot_font_set_funcs},
#ifdef HAVE_FREETYPE
{"ft", hb_ft_font_set_funcs},
#endif
};
void
font_options_t::post_parse (GError **error)
{
assert (!font);
font = hb_font_create (face);
if (font_size_x == FONT_SIZE_UPEM)
font_size_x = hb_face_get_upem (face);
if (font_size_y == FONT_SIZE_UPEM)
font_size_y = hb_face_get_upem (face);
hb_font_set_ppem (font, x_ppem, y_ppem);
hb_font_set_ptem (font, ptem);
hb_font_set_synthetic_bold (font,
(float) x_embolden, (float) y_embolden,
embolden_in_place);
hb_font_set_synthetic_slant (font, slant);
int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
hb_font_set_scale (font, scale_x, scale_y);
#ifndef HB_NO_VAR
hb_font_set_var_named_instance (font, named_instance);
hb_font_set_variations (font, variations, num_variations);
#endif
void (*set_font_funcs) (hb_font_t *) = nullptr;
if (!font_funcs)
{
set_font_funcs = supported_font_funcs[0].func;
}
else
{
for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name))
{
set_font_funcs = supported_font_funcs[i].func;
break;
}
if (!set_font_funcs)
{
GString *s = g_string_new (nullptr);
for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
{
if (i)
g_string_append_c (s, '/');
g_string_append (s, supported_font_funcs[i].name);
}
g_string_append_c (s, '\n');
char *p = g_string_free (s, FALSE);
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Unknown font function implementation `%s'; supported values are: %s; default is %s",
font_funcs,
p,
supported_font_funcs[0].name);
free (p);
return;
}
}
set_font_funcs (font);
#ifdef HAVE_FREETYPE
hb_ft_font_set_load_flags (font, ft_load_flags);
#endif
if (sub_font)
{
hb_font_t *old_font = font;
font = hb_font_create_sub_font (old_font);
hb_font_set_scale (old_font, scale_x * 2, scale_y * 2);
hb_font_destroy (old_font);
}
}
#ifndef HB_NO_VAR
static gboolean
parse_variations (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
char *s = (char *) arg;
char *p;
font_opts->num_variations = 0;
g_free (font_opts->variations);
font_opts->variations = nullptr;
if (!*s)
return true;
/* count the variations first, so we can allocate memory */
p = s;
do {
font_opts->num_variations++;
p = strpbrk (p, ", ");
if (p)
p++;
} while (p);
font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
if (!font_opts->variations)
return false;
/* now do the actual parsing */
p = s;
font_opts->num_variations = 0;
while (p && *p) {
char *end = strpbrk (p, ", ");
if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
font_opts->num_variations++;
p = end ? end + 1 : nullptr;
}
return true;
}
#endif
static gboolean
parse_font_size (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
if (0 == strcmp (arg, "upem"))
{
font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM;
return true;
}
switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH;
case 2: return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one or two space-separated numbers",
name);
return false;
}
}
static gboolean
parse_font_ppem (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) {
case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH;
case 2: return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one or two space-separated numbers",
name);
return false;
}
}
static gboolean
parse_font_embolden (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->x_embolden, &font_opts->y_embolden)) {
case 1: font_opts->y_embolden = font_opts->x_embolden; HB_FALLTHROUGH;
case 2: return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one or two space-separated numbers",
name);
return false;
}
}
static gboolean
parse_font_bold (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
font_opts->embolden_in_place = false;
return parse_font_embolden ( name, arg, data, error);
}
static gboolean
parse_font_grade (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
font_opts->embolden_in_place = true;
return parse_font_embolden ( name, arg, data, error);
}
void
font_options_t::add_options (option_parser_t *parser)
{
face_options_t::add_options (parser);
char *text = nullptr;
{
static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0),
"No supported font-funcs found.");
GString *s = g_string_new (nullptr);
g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n Supported font function implementations are: %s",
supported_font_funcs[0].name,
supported_font_funcs[0].name);
for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++)
{
g_string_append_c (s, '/');
g_string_append (s, supported_font_funcs[i].name);
}
text = g_string_free (s, FALSE);
parser->free_later (text);
}
char *font_size_text;
if (DEFAULT_FONT_SIZE == FONT_SIZE_UPEM)
font_size_text = (char *) "Font size (default: upem)";
else
{
font_size_text = g_strdup_printf ("Font size (default: %u)", DEFAULT_FONT_SIZE);
parser->free_later (font_size_text);
}
int font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0;
GOptionEntry entries[] =
{
{"font-size", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"},
{"font-ppem", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"},
{"font-ptem", 0, font_size_flags,
G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"},
{"font-bold", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_bold, "Set synthetic bold (default: 0)", "1/2 numbers; eg. 0.05"},
{"font-grade", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_grade, "Set synthetic grade (default: 0)", "1/2 numbers; eg. 0.05"},
{"font-slant", 0, font_size_flags,
G_OPTION_ARG_DOUBLE, &this->slant, "Set synthetic slant (default: 0)", "slant ratio; eg. 0.2"},
{"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"},
{"sub-font", 0, G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_NONE, &this->sub_font, "Create a sub-font (default: false)", "boolean"},
{"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"},
{nullptr}
};
parser->add_group (entries,
"font",
"Font-instance options:",
"Options for the font instance",
this,
false /* We add below. */);
#ifndef HB_NO_VAR
const gchar *variations_help = "Comma-separated list of font variations\n"
"\n"
" Variations are set globally. The format for specifying variation settings\n"
" follows. All valid CSS font-variation-settings values other than 'normal'\n"
" and 'inherited' are also accepted, though, not documented below.\n"
"\n"
" The format is a tag, optionally followed by an equals sign, followed by a\n"
" number. For example:\n"
"\n"
" \"wght=500\"\n"
" \"slnt=-7.5\"";
GOptionEntry entries2[] =
{
{"named-instance", 0, 0, G_OPTION_ARG_INT, &this->named_instance, "Set named-instance index (default: none)", "index"},
{"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_variations, variations_help, "list"},
{nullptr}
};
parser->add_group (entries2,
"variations",
"Variations options:",
"Options for font variations used",
this);
#endif
}
#endif

View File

@@ -0,0 +1,222 @@
/*
* Copyright © 2002 Keith Packard
* Copyright © 2014 Google, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the author(s) not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. The authors make no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* Google Author(s): Behdad Esfahbod
*/
#define HAVE_GETOPT_LONG 1 /* XXX */
#include "hb-fc.h"
#include <fontconfig/fontconfig.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#else
#ifdef linux
#define HAVE_GETOPT_LONG 1
#endif
#define HAVE_GETOPT 1
#endif
#ifndef HAVE_GETOPT
#define HAVE_GETOPT 0
#endif
#ifndef HAVE_GETOPT_LONG
#define HAVE_GETOPT_LONG 0
#endif
#if HAVE_GETOPT_LONG
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <getopt.h>
const struct option longopts[] = {
{"verbose", 0, 0, 'v'},
{"format", 1, 0, 'f'},
{"quiet", 0, 0, 'q'},
{"version", 0, 0, 'V'},
{"help", 0, 0, 'h'},
{NULL,0,0,0},
};
#else
#if HAVE_GETOPT
extern char *optarg;
extern int optind, opterr, optopt;
#endif
#endif
static void
usage (char *program, int error)
{
FILE *file = error ? stderr : stdout;
#if HAVE_GETOPT_LONG
fprintf (file, "usage: %s [-vqVh] [-f FORMAT] [--verbose] [--format=FORMAT] [--quiet] [--version] [--help] text [pattern] {element ...} \n",
program);
#else
fprintf (file, "usage: %s [-vqVh] [-f FORMAT] text [pattern] {element ...} \n",
program);
#endif
fprintf (file, "List fonts matching [pattern] that can render [text]\n");
fprintf (file, "\n");
#if HAVE_GETOPT_LONG
fprintf (file, " -v, --verbose display entire font pattern verbosely\n");
fprintf (file, " -f, --format=FORMAT use the given output format\n");
fprintf (file, " -q, --quiet suppress all normal output, exit 1 if no fonts matched\n");
fprintf (file, " -V, --version display font config version and exit\n");
fprintf (file, " -h, --help display this help and exit\n");
#else
fprintf (file, " -v (verbose) display entire font pattern verbosely\n");
fprintf (file, " -f FORMAT (format) use the given output format\n");
fprintf (file, " -q, (quiet) suppress all normal output, exit 1 if no fonts matched\n");
fprintf (file, " -V (version) display HarfBuzz version and exit\n");
fprintf (file, " -h (help) display this help and exit\n");
#endif
exit (error);
}
int
main (int argc, char **argv)
{
int verbose = 0;
int quiet = 0;
const FcChar8 *format = NULL;
int nfont = 0;
int i;
FcObjectSet *os = 0;
FcFontSet *fs;
FcPattern *pat;
const char *text;
#if HAVE_GETOPT_LONG || HAVE_GETOPT
int c;
#if HAVE_GETOPT_LONG
while ((c = getopt_long (argc, argv, "vf:qVh", longopts, NULL)) != -1)
#else
while ((c = getopt (argc, argv, "vf:qVh")) != -1)
#endif
{
switch (c) {
case 'v':
verbose = 1;
break;
case 'f':
format = (FcChar8 *) strdup (optarg);
break;
case 'q':
quiet = 1;
break;
case 'V':
fprintf (stderr, "fontconfig version %d.%d.%d\n",
FC_MAJOR, FC_MINOR, FC_REVISION);
exit (0);
case 'h':
usage (argv[0], 0);
default:
usage (argv[0], 1);
}
}
i = optind;
#else
i = 1;
#endif
if (!argv[i])
usage (argv[0], 1);
text = argv[i];
i++;
if (argv[i])
{
pat = FcNameParse ((FcChar8 *) argv[i]);
if (!pat)
{
fputs ("Unable to parse the pattern\n", stderr);
return 1;
}
while (argv[++i])
{
if (!os)
os = FcObjectSetCreate ();
FcObjectSetAdd (os, argv[i]);
}
}
else
pat = FcPatternCreate ();
if (quiet && !os)
os = FcObjectSetCreate ();
if (!verbose && !format && !os)
os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_FILE, (char *) 0);
FcObjectSetAdd (os, FC_CHARSET);
if (!format)
format = (const FcChar8 *) "%{=fclist}\n";
fs = FcFontList (0, pat, os);
if (os)
FcObjectSetDestroy (os);
if (pat)
FcPatternDestroy (pat);
if (!quiet && fs)
{
int j;
for (j = 0; j < fs->nfont; j++)
{
hb_font_t *font = hb_fc_font_create (fs->fonts[j]);
hb_bool_t can_render = hb_fc_can_render (font, text);
hb_font_destroy (font);
if (!can_render)
continue;
FcPatternDel (fs->fonts[j], FC_CHARSET);
if (verbose)
{
FcPatternPrint (fs->fonts[j]);
}
else
{
FcChar8 *s;
s = FcPatternFormat (fs->fonts[j], format);
if (s)
{
printf ("%s", s);
FcStrFree (s);
}
}
}
}
if (fs) {
nfont = fs->nfont;
FcFontSetDestroy (fs);
}
FcFini ();
return quiet ? (nfont == 0 ? 1 : 0) : 0;
}

147
SDL2_ttf/external/harfbuzz/util/hb-fc.cc vendored Normal file
View File

@@ -0,0 +1,147 @@
/*
* Copyright © 2014 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#include <stdlib.h>
#include <stdio.h>
#include "hb-fc.h"
static hb_bool_t
hb_fc_get_glyph (hb_font_t *font /*HB_UNUSED*/,
void *font_data,
hb_codepoint_t unicode,
hb_codepoint_t variation_selector,
hb_codepoint_t *glyph,
void *user_data /*HB_UNUSED*/)
{
FcCharSet *cs = (FcCharSet *) font_data;
if (variation_selector)
{
/* Fontconfig doesn't cache cmap-14 info. However:
* 1. If the font maps the variation_selector, assume it's
* supported,
* 2. If the font doesn't map it, still say it's supported,
* but return 0. This way, the caller will see the zero
* and reject. If we return unsupported here, then the
* variation selector will be hidden and ignored.
*/
if (FcCharSetHasChar (cs, unicode) &&
FcCharSetHasChar (cs, variation_selector))
{
unsigned int var_num = 0;
if (variation_selector - 0xFE00u < 16)
var_num = variation_selector - 0xFE00 + 1;
else if (variation_selector - 0xE0100u < (256 - 16))
var_num = variation_selector - 0xE0100 + 17;
*glyph = (var_num << 21) | unicode;
}
else
{
*glyph = 0;
}
return true;
}
*glyph = FcCharSetHasChar (cs, unicode) ? unicode : 0;
return *glyph != 0;
}
static hb_font_funcs_t *
_hb_fc_get_font_funcs ()
{
static const hb_font_funcs_t *fc_ffuncs;
if (!fc_ffuncs)
{
hb_font_funcs_t *newfuncs = hb_font_funcs_create ();
hb_font_funcs_set_glyph_func (newfuncs, hb_fc_get_glyph, nullptr, nullptr);
/* XXX MT-unsafe */
if (fc_ffuncs)
hb_font_funcs_destroy (newfuncs);
else
fc_ffuncs = newfuncs;
}
return const_cast<hb_font_funcs_t *> (fc_ffuncs);
}
hb_font_t *
hb_fc_font_create (FcPattern *fcfont)
{
static hb_face_t *face;
hb_font_t *font;
FcCharSet *cs;
if (FcResultMatch != FcPatternGetCharSet (fcfont, FC_CHARSET, 0, &cs))
return hb_font_get_empty ();
if (!face) /* XXX MT-unsafe */
face = hb_face_create (hb_blob_get_empty (), 0);
font = hb_font_create (face);
hb_font_set_funcs (font,
_hb_fc_get_font_funcs (),
FcCharSetCopy (cs),
(hb_destroy_func_t) FcCharSetDestroy);
return font;
}
hb_bool_t
hb_fc_can_render (hb_font_t *font, const char *text)
{
static const char *ot[] = {"ot", nullptr};
hb_buffer_t *buffer = hb_buffer_create ();
hb_buffer_add_utf8 (buffer, text, -1, 0, -1);
/* XXX Do we need this? I think Arabic and Hangul shapers are the
* only one that make any use of this. The Hangul case is not really
* needed, and for Arabic we'll miss a very narrow set of fonts.
* Might be better to force generic shaper perhaps. */
hb_buffer_guess_segment_properties (buffer);
if (!hb_shape_full (font, buffer, nullptr, 0, ot))
abort (); /* hb-ot shaper not enabled? */
unsigned int len;
hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &len);
for (unsigned int i = 0; i < len; i++)
{
if (!info[i].codepoint)
{
return false;
}
}
return true;
}

46
SDL2_ttf/external/harfbuzz/util/hb-fc.h vendored Normal file
View File

@@ -0,0 +1,46 @@
/*
* Copyright © 2014 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_FC_H
#define HB_FC_H
#include "hb.h"
#include <fontconfig/fontconfig.h>
HB_BEGIN_DECLS
hb_font_t *
hb_fc_font_create (FcPattern *font);
hb_bool_t
hb_fc_can_render (hb_font_t *font, const char *text);
HB_END_DECLS
#endif /* HB_FC_H */

1450
SDL2_ttf/external/harfbuzz/util/hb-info.cc vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
/*
* Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#include "batch.hh"
#include "font-options.hh"
#include "main-font-text.hh"
#include "shape-options.hh"
#include "text-options.hh"
const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE;
const unsigned SUBPIXEL_BITS = 0;
struct shape_closure_consumer_t
{
void add_options (struct option_parser_t *parser)
{
parser->set_summary ("Find glyph set from input text under shaping closure.");
shaper.add_options (parser);
GOptionEntry entries[] =
{
{"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", nullptr},
{nullptr}
};
parser->add_group (entries,
"format",
"Format options:",
"Options controlling output formatting",
this);
}
void init (const font_options_t *font_opts)
{
glyphs = hb_set_create ();
font = hb_font_reference (font_opts->font);
failed = false;
buffer = hb_buffer_create ();
}
template <typename text_options_type>
bool consume_line (text_options_type &text_opts)
{
unsigned int text_len;
const char *text;
if (!(text = text_opts.get_line (&text_len)))
return false;
hb_set_clear (glyphs);
shaper.shape_closure (text, text_len, font, buffer, glyphs);
if (hb_set_is_empty (glyphs))
return true;
/* Print it out! */
bool first = true;
for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);)
{
if (first)
first = false;
else
printf (" ");
if (show_glyph_names)
{
char glyph_name[64];
hb_font_glyph_to_string (font, i, glyph_name, sizeof (glyph_name));
printf ("%s", glyph_name);
} else
printf ("%u", i);
}
return true;
}
void finish (const font_options_t *font_opts)
{
printf ("\n");
hb_font_destroy (font);
font = nullptr;
hb_set_destroy (glyphs);
glyphs = nullptr;
hb_buffer_destroy (buffer);
buffer = nullptr;
}
bool failed;
protected:
shape_options_t shaper;
hb_bool_t show_glyph_names = true;
hb_set_t *glyphs = nullptr;
hb_font_t *font = nullptr;
hb_buffer_t *buffer = nullptr;
};
int
main (int argc, char **argv)
{
using main_t = main_font_text_t<shape_closure_consumer_t, font_options_t, text_options_t>;
return batch_main<main_t> (argc, argv);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2010 Behdad Esfahbod
* Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#include "batch.hh"
#include "font-options.hh"
#include "main-font-text.hh"
#include "shape-consumer.hh"
#include "shape-output.hh"
#include "text-options.hh"
const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM;
const unsigned SUBPIXEL_BITS = 0;
int
main (int argc, char **argv)
{
using main_t = main_font_text_t<shape_consumer_t<shape_output_t>, font_options_t, shape_text_options_t>;
return batch_main<main_t> (argc, argv);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2010 Behdad Esfahbod
* Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#include "batch.hh"
#include "font-options.hh"
#include "main-font-text.hh"
#include "shape-consumer.hh"
#include "text-options.hh"
#include "view-cairo.hh"
const unsigned DEFAULT_FONT_SIZE = 256;
const unsigned SUBPIXEL_BITS = 6;
int
main (int argc, char **argv)
{
using main_t = main_font_text_t<shape_consumer_t<view_cairo_t>, font_options_t, shape_text_options_t>;
return batch_main<main_t> (argc, argv);
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HELPER_CAIRO_ANSI_HH
#define HELPER_CAIRO_ANSI_HH
#include "hb.hh"
#include <cairo.h>
#include "ansi-print.hh"
#ifdef HAVE_CHAFA
# include <chafa.h>
/* Similar to ansi-print.cc */
# define CELL_W 8
# define CELL_H (2 * CELL_W)
static void
chafa_print_image_rgb24 (const void *data, int width, int height, int stride, int level,
cairo_write_func_t write_func,
void *closure)
{
ChafaTermInfo *term_info;
ChafaSymbolMap *symbol_map;
ChafaCanvasConfig *config;
ChafaCanvas *canvas;
GString *gs;
unsigned int cols = (width + CELL_W - 1) / CELL_W;
unsigned int rows = (height + CELL_H - 1) / CELL_H;
gchar **environ;
ChafaCanvasMode mode;
ChafaPixelMode pixel_mode;
/* Adapt to terminal; use sixels if available, and fall back to symbols
* with as many colors as are supported */
chafa_set_n_threads (1); // https://github.com/hpjansson/chafa/issues/125#issuecomment-1397475217
environ = g_get_environ ();
term_info = chafa_term_db_detect (chafa_term_db_get_default (),
environ);
pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;
if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS))
{
pixel_mode = CHAFA_PIXEL_MODE_SIXELS;
mode = CHAFA_CANVAS_MODE_TRUECOLOR;
}
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT))
mode = CHAFA_CANVAS_MODE_TRUECOLOR;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256))
mode = CHAFA_CANVAS_MODE_INDEXED_240;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16))
mode = CHAFA_CANVAS_MODE_INDEXED_16;
else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS))
mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
else
mode = CHAFA_CANVAS_MODE_FGBG;
/* Create the configuration */
symbol_map = chafa_symbol_map_new ();
chafa_symbol_map_add_by_tags (symbol_map,
(ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK
| CHAFA_SYMBOL_TAG_SPACE
| (level >= 2 ? CHAFA_SYMBOL_TAG_WEDGE : 0)
| (level >= 3 ? CHAFA_SYMBOL_TAG_ALL : 0)
));
config = chafa_canvas_config_new ();
chafa_canvas_config_set_canvas_mode (config, mode);
chafa_canvas_config_set_pixel_mode (config, pixel_mode);
chafa_canvas_config_set_cell_geometry (config, CELL_W, CELL_H);
chafa_canvas_config_set_geometry (config, cols, rows);
chafa_canvas_config_set_symbol_map (config, symbol_map);
chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN);
chafa_canvas_config_set_work_factor (config, 1.0f);
/* Create canvas, draw to it and render output string */
canvas = chafa_canvas_new (config);
chafa_canvas_draw_all_pixels (canvas,
/* Cairo byte order is host native */
G_BYTE_ORDER == G_LITTLE_ENDIAN
? CHAFA_PIXEL_BGRA8_PREMULTIPLIED
: CHAFA_PIXEL_ARGB8_PREMULTIPLIED,
(const guint8 *) data,
width,
height,
stride);
gs = chafa_canvas_print (canvas, term_info);
/* Print the string */
write_func (closure, (const unsigned char *) gs->str, gs->len);
if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS)
write_func (closure, (const unsigned char *) "\n", 1);
/* Free resources */
g_string_free (gs, TRUE);
chafa_canvas_unref (canvas);
chafa_canvas_config_unref (config);
chafa_symbol_map_unref (symbol_map);
chafa_term_info_unref (term_info);
g_strfreev (environ);
}
#endif /* HAVE_CHAFA */
static inline cairo_status_t
helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface,
cairo_write_func_t write_func,
void *closure)
{
unsigned int width = cairo_image_surface_get_width (surface);
unsigned int height = cairo_image_surface_get_height (surface);
if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
cairo_t *cr = cairo_create (new_surface);
if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) {
cairo_set_source_rgb (cr, 0., 0., 0.);
cairo_paint (cr);
cairo_set_source_rgb (cr, 1., 1., 1.);
cairo_mask_surface (cr, surface, 0, 0);
} else {
cairo_set_source_rgb (cr, 1., 1., 1.);
cairo_paint (cr);
cairo_set_source_surface (cr, surface, 0, 0);
cairo_paint (cr);
}
cairo_destroy (cr);
surface = new_surface;
} else
cairo_surface_reference (surface);
unsigned int stride = cairo_image_surface_get_stride (surface);
const uint32_t *data = (uint32_t *) (void *) cairo_image_surface_get_data (surface);
/* We don't have rows to spare on the terminal window...
* Find the tight image top/bottom and only print in between. */
/* Use corner color as background color. */
uint32_t bg_color = data ? * (uint32_t *) data : 0;
/* Drop first row while empty */
auto orig_data = data;
while (height)
{
unsigned int i;
for (i = 0; i < width; i++)
if (data[i] != bg_color)
break;
if (i < width)
break;
data += stride / 4;
height--;
}
if (orig_data < data)
{
data -= stride / 4;
height++; /* Add one first blank row for padding. */
}
/* Drop last row while empty */
auto orig_height = height;
while (height)
{
const uint32_t *row = data + (height - 1) * stride / 4;
unsigned int i;
for (i = 0; i < width; i++)
if (row[i] != bg_color)
break;
if (i < width)
break;
height--;
}
if (height < orig_height)
height++; /* Add one last blank row for padding. */
if (width && height)
{
#ifdef HAVE_CHAFA
const char *env = getenv ("HB_CHAFA");
int chafa_level = 1;
if (env)
chafa_level = atoi (env);
if (chafa_level)
chafa_print_image_rgb24 (data, width, height, stride, chafa_level,
write_func, closure);
else
#endif
ansi_print_image_rgb24 (data, width, height, stride / 4,
write_func, closure);
}
cairo_surface_destroy (surface);
return CAIRO_STATUS_SUCCESS;
}
#endif

View File

@@ -0,0 +1,130 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HELPER_CAIRO_FT_HH
#define HELPER_CAIRO_FT_HH
#include "font-options.hh"
#include <cairo-ft.h>
#include <hb-ft.h>
#include FT_MULTIPLE_MASTERS_H
static FT_Library ft_library;
#ifdef HAVE_ATEXIT
static inline
void free_ft_library ()
{
FT_Done_FreeType (ft_library);
}
#endif
static void
_release_blob (void *arg)
{
FT_Face ft_face = (FT_Face) arg;
hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
}
static inline cairo_font_face_t *
helper_cairo_create_ft_font_face (const font_options_t *font_opts)
{
cairo_font_face_t *cairo_face;
/* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because
* cairo will reset the face size. As such, create new face...
* TODO Perhaps add API to hb-ft to encapsulate this code. */
FT_Face ft_face = nullptr;//hb_ft_font_get_face (font);
if (!ft_face)
{
if (!ft_library)
{
FT_Init_FreeType (&ft_library);
#ifdef HAVE_ATEXIT
atexit (free_ft_library);
#endif
}
unsigned int blob_length;
const char *blob_data = hb_blob_get_data (font_opts->blob, &blob_length);
if (FT_New_Memory_Face (ft_library,
(const FT_Byte *) blob_data,
blob_length,
font_opts->face_index,
&ft_face))
fail (false, "FT_New_Memory_Face fail");
}
if (!ft_face)
{
/* This allows us to get some boxes at least... */
cairo_face = cairo_toy_font_face_create ("@cairo:sans",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
}
else
{
ft_face->generic.data = hb_blob_reference (font_opts->blob);
ft_face->generic.finalizer = _release_blob;
#if !defined(HB_NO_VAR) && defined(HAVE_FT_SET_VAR_BLEND_COORDINATES)
unsigned int num_coords;
const float *coords = hb_font_get_var_coords_design (font_opts->font, &num_coords);
if (num_coords)
{
FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
if (ft_coords)
{
for (unsigned int i = 0; i < num_coords; i++)
ft_coords[i] = coords[i] * 65536.f;
FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords);
free (ft_coords);
}
}
#endif
cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags);
}
return cairo_face;
}
static inline bool
helper_cairo_ft_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
bool ret = false;
#ifdef FT_HAS_COLOR
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
if (ft_face)
{
if (FT_HAS_COLOR (ft_face))
ret = true;
cairo_ft_scaled_font_unlock_face (scaled_font);
}
#endif
return ret;
}
#endif

View File

@@ -0,0 +1,634 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HELPER_CAIRO_HH
#define HELPER_CAIRO_HH
#include "view-options.hh"
#include "output-options.hh"
#ifdef HAVE_CAIRO_FT
# include "helper-cairo-ft.hh"
#endif
#include <cairo.h>
#include <hb.h>
#include <hb-cairo.h>
#include "helper-cairo-ansi.hh"
#ifdef CAIRO_HAS_SVG_SURFACE
# include <cairo-svg.h>
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
# include <cairo-pdf.h>
#endif
#ifdef CAIRO_HAS_PS_SURFACE
# include <cairo-ps.h>
# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
# define HAS_EPS 1
static cairo_surface_t *
_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width,
double height)
{
cairo_surface_t *surface;
surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
cairo_ps_surface_set_eps (surface, true);
return surface;
}
# else
# undef HAS_EPS
# endif
#endif
static inline bool
helper_cairo_use_hb_draw (const font_options_t *font_opts)
{
const char *env = getenv ("HB_DRAW");
if (!env)
/* Older cairo had a bug in rendering COLRv0 fonts in
* right-to-left direction as well as clipping issue
* with user-fonts.
*
* https://github.com/harfbuzz/harfbuzz/issues/4051 */
return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5);
return atoi (env);
}
static inline cairo_scaled_font_t *
helper_cairo_create_scaled_font (const font_options_t *font_opts,
const view_options_t *view_opts)
{
hb_font_t *font = font_opts->font;
bool use_hb_draw = true;
#ifdef HAVE_CAIRO_FT
use_hb_draw = helper_cairo_use_hb_draw (font_opts);
#endif
cairo_font_face_t *cairo_face = nullptr;
if (use_hb_draw)
{
cairo_face = hb_cairo_font_face_create_for_font (font);
hb_cairo_font_face_set_scale_factor (cairo_face, 1 << font_opts->subpixel_bits);
}
#ifdef HAVE_CAIRO_FT
else
cairo_face = helper_cairo_create_ft_font_face (font_opts);
#endif
cairo_matrix_t ctm, font_matrix;
cairo_font_options_t *font_options;
cairo_matrix_init_identity (&ctm);
cairo_matrix_init_scale (&font_matrix,
font_opts->font_size_x,
font_opts->font_size_y);
if (!use_hb_draw)
font_matrix.xy = -font_opts->slant * font_opts->font_size_x;
font_options = cairo_font_options_create ();
cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
#ifdef CAIRO_COLOR_PALETTE_DEFAULT
cairo_font_options_set_color_palette (font_options, view_opts->palette);
#endif
#ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR
if (view_opts->custom_palette)
{
char **entries = g_strsplit (view_opts->custom_palette, ",", -1);
unsigned idx = 0;
for (unsigned i = 0; entries[i]; i++)
{
const char *p = strchr (entries[i], '=');
if (!p)
p = entries[i];
else
{
sscanf (entries[i], "%u", &idx);
p++;
}
unsigned fr, fg, fb, fa;
fr = fg = fb = fa = 0;
if (parse_color (p, fr, fg,fb, fa))
cairo_font_options_set_custom_palette_color (font_options, idx, fr / 255., fg / 255., fb / 255., fa / 255.);
idx++;
}
g_strfreev (entries);
}
#endif
cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
&font_matrix,
&ctm,
font_options);
cairo_font_options_destroy (font_options);
cairo_font_face_destroy (cairo_face);
return scaled_font;
}
static inline bool
helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
hb_font_t *font = hb_cairo_font_face_get_font (cairo_scaled_font_get_font_face (scaled_font));
#ifdef HAVE_CAIRO_FT
if (!font)
return helper_cairo_ft_scaled_font_has_color (scaled_font);
#endif
hb_face_t *face = hb_font_get_face (font);
return hb_ot_color_has_png (face) ||
hb_ot_color_has_layers (face) ||
hb_ot_color_has_paint (face);
}
enum class image_protocol_t {
NONE = 0,
ITERM2,
KITTY,
};
struct finalize_closure_t {
void (*callback)(finalize_closure_t *);
cairo_surface_t *surface;
cairo_write_func_t write_func;
void *closure;
image_protocol_t protocol;
};
static cairo_user_data_key_t finalize_closure_key;
static void
finalize_ansi (finalize_closure_t *closure)
{
cairo_status_t status;
status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
closure->write_func,
closure->closure);
if (status != CAIRO_STATUS_SUCCESS)
fail (false, "Failed to write output: %s",
cairo_status_to_string (status));
}
static cairo_surface_t *
_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width,
double height,
cairo_content_t content,
image_protocol_t protocol HB_UNUSED)
{
cairo_surface_t *surface;
int w = ceil (width);
int h = ceil (height);
switch (content) {
case CAIRO_CONTENT_ALPHA:
surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
break;
default:
case CAIRO_CONTENT_COLOR:
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
break;
case CAIRO_CONTENT_COLOR_ALPHA:
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
break;
}
cairo_status_t status = cairo_surface_status (surface);
if (status != CAIRO_STATUS_SUCCESS)
fail (false, "Failed to create cairo surface: %s",
cairo_status_to_string (status));
finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
ansi_closure->callback = finalize_ansi;
ansi_closure->surface = surface;
ansi_closure->write_func = write_func;
ansi_closure->closure = closure;
if (cairo_surface_set_user_data (surface,
&finalize_closure_key,
(void *) ansi_closure,
(cairo_destroy_func_t) g_free))
g_free ((void *) closure);
return surface;
}
#ifdef CAIRO_HAS_PNG_FUNCTIONS
static cairo_status_t
byte_array_write_func (void *closure,
const unsigned char *data,
unsigned int size)
{
g_byte_array_append ((GByteArray *) closure, data, size);
return CAIRO_STATUS_SUCCESS;
}
static void
finalize_png (finalize_closure_t *closure)
{
cairo_status_t status;
GByteArray *bytes = nullptr;
GString *string;
gchar *base64;
size_t base64_len;
if (closure->protocol == image_protocol_t::NONE)
{
status = cairo_surface_write_to_png_stream (closure->surface,
closure->write_func,
closure->closure);
}
else
{
bytes = g_byte_array_new ();
status = cairo_surface_write_to_png_stream (closure->surface,
byte_array_write_func,
bytes);
}
if (status != CAIRO_STATUS_SUCCESS)
fail (false, "Failed to write output: %s",
cairo_status_to_string (status));
if (closure->protocol == image_protocol_t::NONE)
return;
base64 = g_base64_encode (bytes->data, bytes->len);
base64_len = strlen (base64);
string = g_string_new (NULL);
if (closure->protocol == image_protocol_t::ITERM2)
{
/* https://iterm2.com/documentation-images.html */
g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n",
base64_len, base64);
}
else if (closure->protocol == image_protocol_t::KITTY)
{
#define CHUNK_SIZE 4096
/* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */
for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE)
{
size_t len = base64_len - pos;
if (pos == 0)
g_string_append (string, "\033_Ga=T,f=100,m=");
else
g_string_append (string, "\033_Gm=");
if (len > CHUNK_SIZE)
{
g_string_append (string, "1;");
g_string_append_len (string, base64 + pos, CHUNK_SIZE);
}
else
{
g_string_append (string, "0;");
g_string_append_len (string, base64 + pos, len);
}
g_string_append (string, "\033\\");
}
g_string_append (string, "\n");
#undef CHUNK_SIZE
}
closure->write_func (closure->closure, (unsigned char *) string->str, string->len);
g_byte_array_unref (bytes);
g_free (base64);
g_string_free (string, TRUE);
}
static cairo_surface_t *
_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width,
double height,
cairo_content_t content,
image_protocol_t protocol)
{
cairo_surface_t *surface;
int w = ceil (width);
int h = ceil (height);
switch (content) {
case CAIRO_CONTENT_ALPHA:
surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
break;
default:
case CAIRO_CONTENT_COLOR:
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
break;
case CAIRO_CONTENT_COLOR_ALPHA:
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
break;
}
cairo_status_t status = cairo_surface_status (surface);
if (status != CAIRO_STATUS_SUCCESS)
fail (false, "Failed to create cairo surface: %s",
cairo_status_to_string (status));
finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
png_closure->callback = finalize_png;
png_closure->surface = surface;
png_closure->write_func = write_func;
png_closure->closure = closure;
png_closure->protocol = protocol;
if (cairo_surface_set_user_data (surface,
&finalize_closure_key,
(void *) png_closure,
(cairo_destroy_func_t) g_free))
g_free ((void *) closure);
return surface;
}
#endif
static cairo_status_t
stdio_write_func (void *closure,
const unsigned char *data,
unsigned int size)
{
FILE *fp = (FILE *) closure;
while (size) {
size_t ret = fwrite (data, 1, size, fp);
size -= ret;
data += ret;
if (size && ferror (fp))
fail (false, "Failed to write output: %s", strerror (errno));
}
return CAIRO_STATUS_SUCCESS;
}
static const char *helper_cairo_supported_formats[] =
{
"ansi",
#ifdef CAIRO_HAS_PNG_FUNCTIONS
"png",
#endif
#ifdef CAIRO_HAS_SVG_SURFACE
"svg",
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
"pdf",
#endif
#ifdef CAIRO_HAS_PS_SURFACE
"ps",
#ifdef HAS_EPS
"eps",
#endif
#endif
nullptr
};
template <typename view_options_t,
typename output_options_type>
static inline cairo_t *
helper_cairo_create_context (double w, double h,
view_options_t *view_opts,
output_options_type *out_opts,
cairo_content_t content)
{
cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
void *closure,
double width,
double height) = nullptr;
cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
void *closure,
double width,
double height,
cairo_content_t content,
image_protocol_t protocol) = nullptr;
image_protocol_t protocol = image_protocol_t::NONE;
const char *extension = out_opts->output_format;
if (!extension) {
#if HAVE_ISATTY
if (isatty (fileno (out_opts->out_fp)))
{
#ifdef CAIRO_HAS_PNG_FUNCTIONS
const char *name;
/* https://gitlab.com/gnachman/iterm2/-/issues/7154 */
if ((name = getenv ("LC_TERMINAL")) != nullptr &&
0 == g_ascii_strcasecmp (name, "iTerm2"))
{
extension = "png";
protocol = image_protocol_t::ITERM2;
}
else if ((name = getenv ("TERM_PROGRAM")) != nullptr &&
0 == g_ascii_strcasecmp (name, "WezTerm"))
{
extension = "png";
protocol = image_protocol_t::ITERM2;
}
else if ((name = getenv ("TERM")) != nullptr &&
0 == g_ascii_strcasecmp (name, "xterm-kitty"))
{
extension = "png";
protocol = image_protocol_t::KITTY;
}
else
extension = "ansi";
#else
extension = "ansi";
#endif
}
else
#endif
{
#ifdef CAIRO_HAS_PNG_FUNCTIONS
extension = "png";
#else
extension = "ansi";
#endif
}
}
if (0)
;
else if (0 == g_ascii_strcasecmp (extension, "ansi"))
constructor2 = _cairo_ansi_surface_create_for_stream;
#ifdef CAIRO_HAS_PNG_FUNCTIONS
else if (0 == g_ascii_strcasecmp (extension, "png"))
constructor2 = _cairo_png_surface_create_for_stream;
#endif
#ifdef CAIRO_HAS_SVG_SURFACE
else if (0 == g_ascii_strcasecmp (extension, "svg"))
constructor = cairo_svg_surface_create_for_stream;
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
else if (0 == g_ascii_strcasecmp (extension, "pdf"))
constructor = cairo_pdf_surface_create_for_stream;
#endif
#ifdef CAIRO_HAS_PS_SURFACE
else if (0 == g_ascii_strcasecmp (extension, "ps"))
constructor = cairo_ps_surface_create_for_stream;
#ifdef HAS_EPS
else if (0 == g_ascii_strcasecmp (extension, "eps"))
constructor = _cairo_eps_surface_create_for_stream;
#endif
#endif
unsigned int fr, fg, fb, fa, br, bg, bb, ba;
const char *color;
br = bg = bb = ba = 255;
color = view_opts->back ? view_opts->back : DEFAULT_BACK;
parse_color (color, br, bg, bb, ba);
fr = fg = fb = 0; fa = 255;
color = view_opts->fore ? view_opts->fore : DEFAULT_FORE;
parse_color (color, fr, fg, fb, fa);
if (content == CAIRO_CONTENT_ALPHA)
{
if (view_opts->show_extents ||
br != bg || bg != bb ||
fr != fg || fg != fb)
content = CAIRO_CONTENT_COLOR;
}
if (ba != 255)
content = CAIRO_CONTENT_COLOR_ALPHA;
cairo_surface_t *surface;
FILE *f = out_opts->out_fp;
if (constructor)
surface = constructor (stdio_write_func, f, w, h);
else if (constructor2)
surface = constructor2 (stdio_write_func, f, w, h, content, protocol);
else
fail (false, "Unknown output format `%s'; supported formats are: %s%s",
extension,
g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)),
out_opts->explicit_output_format ? "" :
"\nTry setting format using --output-format");
cairo_t *cr = cairo_create (surface);
content = cairo_surface_get_content (surface);
switch (content) {
case CAIRO_CONTENT_ALPHA:
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
cairo_paint (cr);
cairo_set_source_rgba (cr, 1., 1., 1.,
(fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
break;
default:
case CAIRO_CONTENT_COLOR:
case CAIRO_CONTENT_COLOR_ALPHA:
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
break;
}
cairo_surface_destroy (surface);
return cr;
}
static inline void
helper_cairo_destroy_context (cairo_t *cr)
{
finalize_closure_t *closure = (finalize_closure_t *)
cairo_surface_get_user_data (cairo_get_target (cr),
&finalize_closure_key);
if (closure)
closure->callback (closure);
cairo_status_t status = cairo_status (cr);
if (status != CAIRO_STATUS_SUCCESS)
fail (false, "Failed: %s",
cairo_status_to_string (status));
cairo_destroy (cr);
}
struct helper_cairo_line_t {
cairo_glyph_t *glyphs = nullptr;
unsigned int num_glyphs = 0;
char *utf8 = nullptr;
unsigned int utf8_len = 0;
cairo_text_cluster_t *clusters = nullptr;
unsigned int num_clusters = 0;
cairo_text_cluster_flags_t cluster_flags = (cairo_text_cluster_flags_t) 0;
helper_cairo_line_t (const char *utf8_,
unsigned utf8_len_,
hb_buffer_t *buffer,
hb_bool_t utf8_clusters,
unsigned subpixel_bits) :
utf8 (utf8_ ? g_strndup (utf8_, utf8_len_) : nullptr),
utf8_len (utf8_len_)
{
hb_cairo_glyphs_from_buffer (buffer,
utf8_clusters,
1 << subpixel_bits, 1 << subpixel_bits,
0., 0.,
utf8, utf8_len,
&glyphs, &num_glyphs,
&clusters, &num_clusters,
&cluster_flags);
}
void finish ()
{
if (glyphs)
cairo_glyph_free (glyphs);
if (clusters)
cairo_text_cluster_free (clusters);
g_free (utf8);
}
void get_advance (double *x_advance, double *y_advance)
{
*x_advance = glyphs[num_glyphs].x;
*y_advance = glyphs[num_glyphs].y;
}
};
#endif

View File

@@ -0,0 +1,104 @@
/*
* Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_MAIN_FONT_TEXT_HH
#define HB_MAIN_FONT_TEXT_HH
#include "options.hh"
/* main() body for utilities taking font and processing text.*/
template <typename consumer_t,
typename font_options_type,
typename text_options_type>
struct main_font_text_t :
option_parser_t,
font_options_type,
text_options_type,
consumer_t
{
int operator () (int argc, char **argv)
{
add_options ();
parse (&argc, &argv);
this->init (this);
while (this->consume_line (*this))
;
this->finish (this);
return this->failed ? 1 : 0;
}
protected:
void add_options ()
{
font_options_type::add_options (this);
text_options_type::add_options (this);
consumer_t::add_options (this);
GOptionEntry entries[] =
{
{G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_CALLBACK, (gpointer) &collect_rest, nullptr, "[FONT-FILE] [TEXT]"},
{nullptr}
};
add_main_group (entries, this);
option_parser_t::add_options ();
}
private:
static gboolean
collect_rest (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
main_font_text_t *thiz = (main_font_text_t *) data;
if (!thiz->font_file)
{
thiz->font_file = g_strdup (arg);
return true;
}
if (!thiz->text && !thiz->text_file)
{
thiz->text = g_strdup (arg);
return true;
}
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Too many arguments on the command line");
return false;
}
};
#endif

View File

@@ -0,0 +1,74 @@
hb_view_sources = [
'hb-view.cc',
]
hb_shape_sources = [
'hb-shape.cc',
]
hb_info_sources = [
'hb-info.cc',
]
hb_ot_shape_closure_sources = [
'hb-ot-shape-closure.cc',
]
hb_subset_cli_sources = [
'hb-subset.cc',
]
util_deps = [freetype_dep, cairo_dep, cairo_ft_dep, glib_dep]
if conf.get('HAVE_GLIB', 0) == 1
if conf.get('HAVE_CAIRO', 0) == 1
hb_view = executable('hb-view', hb_view_sources,
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: [util_deps, chafa_dep],
link_with: [libharfbuzz, libharfbuzz_cairo],
install: true,
)
meson.override_find_program('hb-view', hb_view)
endif
hb_shape = executable('hb-shape', hb_shape_sources,
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: util_deps,
link_with: [libharfbuzz],
install: true,
)
meson.override_find_program('hb-shape', hb_shape)
hb_info = executable('hb-info', [hb_info_sources, gobject_enums_h],
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: [util_deps, libharfbuzz_gobject_dep, chafa_dep],
link_with: [libharfbuzz],
install: true,
)
meson.override_find_program('hb-info', hb_info)
hb_subset = executable('hb-subset', hb_subset_cli_sources,
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: util_deps,
link_with: [libharfbuzz, libharfbuzz_subset],
install: true,
)
meson.override_find_program('hb-subset', hb_subset)
hb_ot_shape_closure = executable('hb-ot-shape-closure', hb_ot_shape_closure_sources,
cpp_args: cpp_args,
include_directories: [incconfig, incsrc],
dependencies: util_deps,
link_with: [libharfbuzz],
install: true,
)
meson.override_find_program('hb-ot-shape-closure', hb_ot_shape_closure)
else
# Disable tests that use this
hb_shape = disabler()
hb_subset = disabler()
endif

View File

@@ -0,0 +1,288 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef OPTIONS_HH
#define OPTIONS_HH
#include "hb.hh"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <locale.h>
#include <errno.h>
#include <fcntl.h>
#if defined(_WIN32) || defined(__CYGWIN__)
#include <io.h> /* for setmode() under Windows */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for isatty() */
#endif
#include <hb-features.h>
#include <hb.h>
#include <hb-ot.h>
#include <glib.h>
#include <glib/gprintf.h>
static inline void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3);
static inline void
fail (hb_bool_t suggest_help, const char *format, ...)
{
const char *msg;
va_list vap;
va_start (vap, format);
msg = g_strdup_vprintf (format, vap);
va_end (vap);
const char *prgname = g_get_prgname ();
g_printerr ("%s: %s\n", prgname, msg);
if (suggest_help)
g_printerr ("Try `%s --help' for more information.\n", prgname);
exit (1);
}
struct option_parser_t
{
option_parser_t (const char *parameter_string = nullptr)
: context (g_option_context_new (parameter_string)),
to_free (g_ptr_array_new ())
{}
static void _g_free_g_func (void *p, void * G_GNUC_UNUSED) { g_free (p); }
~option_parser_t ()
{
g_option_context_free (context);
g_ptr_array_foreach (to_free, _g_free_g_func, nullptr);
g_ptr_array_free (to_free, TRUE);
}
void add_options ();
static void
post_parse_ (void *thiz, GError **error) {}
template <typename Type>
static auto
post_parse_ (Type *thiz, GError **error) -> decltype (thiz->post_parse (error))
{ thiz->post_parse (error); }
template <typename Type>
static gboolean
post_parse (GOptionContext *context G_GNUC_UNUSED,
GOptionGroup *group G_GNUC_UNUSED,
gpointer data,
GError **error)
{
option_parser_t::post_parse_ (static_cast<Type *> (data), error);
return !*error;
}
template <typename Type>
void add_group (GOptionEntry *entries,
const gchar *name,
const gchar *description,
const gchar *help_description,
Type *closure,
bool add_parse_hooks = true)
{
GOptionGroup *group = g_option_group_new (name, description, help_description,
static_cast<gpointer>(closure), nullptr);
g_option_group_add_entries (group, entries);
if (add_parse_hooks)
g_option_group_set_parse_hooks (group, nullptr, post_parse<Type>);
g_option_context_add_group (context, group);
}
template <typename Type>
void add_main_group (GOptionEntry *entries,
Type *closure)
{
GOptionGroup *group = g_option_group_new (nullptr, nullptr, nullptr,
static_cast<gpointer>(closure), nullptr);
g_option_group_add_entries (group, entries);
/* https://gitlab.gnome.org/GNOME/glib/-/issues/2460 */
//g_option_group_set_parse_hooks (group, nullptr, post_parse<Type>);
g_option_context_set_main_group (context, group);
}
void set_summary (const char *summary)
{
g_option_context_set_summary (context, summary);
}
void set_description (const char *description)
{
g_option_context_set_description (context, description);
}
void free_later (char *p) {
g_ptr_array_add (to_free, p);
}
bool parse (int *argc, char ***argv, bool ignore_error = false);
GOptionContext *context;
protected:
GPtrArray *to_free;
};
static inline gchar *
shapers_to_string ()
{
GString *shapers = g_string_new (nullptr);
const char **shaper_list = hb_shape_list_shapers ();
for (; *shaper_list; shaper_list++) {
g_string_append (shapers, *shaper_list);
g_string_append_c (shapers, ',');
}
g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
return g_string_free (shapers, false);
}
static G_GNUC_NORETURN gboolean
show_version (const char *name G_GNUC_UNUSED,
const char *arg G_GNUC_UNUSED,
gpointer data G_GNUC_UNUSED,
GError **error G_GNUC_UNUSED)
{
g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
char *shapers = shapers_to_string ();
g_printf ("Available shapers: %s\n", shapers);
g_free (shapers);
if (strcmp (HB_VERSION_STRING, hb_version_string ()))
g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
exit(0);
}
inline void
option_parser_t::add_options ()
{
GOptionEntry entries[] =
{
{"version", 0, G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", nullptr},
{nullptr}
};
g_option_context_add_main_entries (context, entries, nullptr);
}
inline bool
option_parser_t::parse (int *argc, char ***argv, bool ignore_error)
{
setlocale (LC_ALL, "");
GError *parse_error = nullptr;
if (!g_option_context_parse (context, argc, argv, &parse_error))
{
if (parse_error)
{
if (!ignore_error)
fail (true, "%s", parse_error->message);
g_error_free (parse_error);
}
else
{
if (!ignore_error)
fail (true, "Option parse error");
}
return false;
}
return true;
}
/* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */
#if defined (_MSC_VER) && (_MSC_VER < 1800)
#ifndef FLT_RADIX
#define FLT_RADIX 2
#endif
__inline long double scalbn (long double x, int exp)
{
return x * (pow ((long double) FLT_RADIX, exp));
}
__inline float scalbnf (float x, int exp)
{
return x * (pow ((float) FLT_RADIX, exp));
}
#endif
static inline bool
parse_color (const char *s,
unsigned &r,
unsigned &g,
unsigned &b,
unsigned &a)
{
bool ret = false;
while (*s == ' ') s++;
if (*s == '#') s++;
unsigned sr, sg, sb, sa;
sa = 255;
if (sscanf (s, "%2x%2x%2x%2x", &sr, &sg, &sb, &sa) <= 2)
{
if (sscanf (s, "%1x%1x%1x%1x", &sr, &sg, &sb, &sa) >= 3)
{
sr *= 17;
sg *= 17;
sb *= 17;
sa *= 17;
ret = true;
}
}
else
ret = true;
if (ret)
{
r = sr;
g = sg;
b = sb;
a = sa;
}
return ret;
}
#endif

View File

@@ -0,0 +1,117 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef OUTPUT_OPTIONS_HH
#define OUTPUT_OPTIONS_HH
#include "options.hh"
template <bool default_stdout = true>
struct output_options_t
{
~output_options_t ()
{
g_free (output_file);
g_free (output_format);
if (out_fp && out_fp != stdout)
fclose (out_fp);
}
void add_options (option_parser_t *parser,
const char **supported_formats = nullptr)
{
const char *text = nullptr;
if (supported_formats)
{
char *items = g_strjoinv ("/", const_cast<char **> (supported_formats));
text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items);
g_free (items);
parser->free_later ((char *) text);
}
GOptionEntry entries[] =
{
{"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"},
{"output-format", 'O', supported_formats ? 0 : G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_STRING, &this->output_format, text, "format"},
{nullptr}
};
parser->add_group (entries,
"output",
"Output destination & format options:",
"Options for the destination & form of the output",
this);
}
void post_parse (GError **error)
{
if (output_format)
explicit_output_format = true;
if (output_file && !output_format)
{
output_format = strrchr (output_file, '.');
if (output_format)
{
output_format++; /* skip the dot */
output_format = g_strdup (output_format);
}
}
if (output_file && 0 != strcmp (output_file, "-"))
out_fp = fopen (output_file, "wb");
else
{
if (!default_stdout && !output_file)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"No output file was specified");
return;
}
#if defined(_WIN32) || defined(__CYGWIN__)
setmode (fileno (stdout), O_BINARY);
#endif
out_fp = stdout;
}
if (!out_fp)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Cannot open output file `%s': %s",
g_filename_display_name (output_file), strerror (errno));
return;
}
}
char *output_file = nullptr;
char *output_format = nullptr;
bool explicit_output_format = false;
FILE *out_fp = nullptr;
};
#endif

View File

@@ -0,0 +1,104 @@
/*
* Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_SHAPE_CONSUMER_HH
#define HB_SHAPE_CONSUMER_HH
#include "font-options.hh"
#include "shape-options.hh"
#include "text-options.hh"
template <typename output_t>
struct shape_consumer_t : shape_options_t
{
void add_options (option_parser_t *parser)
{
shape_options_t::add_options (parser);
output.add_options (parser);
}
template <typename app_t>
void init (const app_t *app)
{
failed = false;
buffer = hb_buffer_create ();
output.init (buffer, app);
}
template <typename app_t>
bool consume_line (app_t &app)
{
unsigned int text_len;
const char *text;
if (!(text = app.get_line (&text_len)))
return false;
output.new_line ();
for (unsigned int n = num_iterations; n; n--)
{
populate_buffer (buffer, text, text_len, app.text_before, app.text_after, app.font);
if (n == 1)
output.consume_text (buffer, text, text_len, utf8_clusters);
const char *error = nullptr;
if (!shape (app.font, buffer, &error))
{
failed = true;
output.error (error);
if (hb_buffer_get_content_type (buffer) == HB_BUFFER_CONTENT_TYPE_GLYPHS)
break;
else
return true;
}
}
if (glyphs)
output.consume_glyphs (buffer, nullptr, 0, false);
else
output.consume_glyphs (buffer, text, text_len, utf8_clusters);
return true;
}
template <typename app_t>
void finish (const app_t *app)
{
output.finish (buffer, app);
hb_buffer_destroy (buffer);
buffer = nullptr;
}
public:
bool failed = false;
protected:
output_t output;
hb_buffer_t *buffer = nullptr;
};
#endif

View File

@@ -0,0 +1,214 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef SHAPE_FORMAT_OPTIONS_HH
#define SHAPE_FORMAT_OPTIONS_HH
#include "options.hh"
struct shape_format_options_t
{
void add_options (option_parser_t *parser);
void serialize (hb_buffer_t *buffer,
hb_font_t *font,
hb_buffer_serialize_format_t format,
hb_buffer_serialize_flags_t flags,
GString *gs);
void serialize_line_no (unsigned int line_no,
GString *gs);
void serialize_buffer_of_text (hb_buffer_t *buffer,
unsigned int line_no,
const char *text,
unsigned int text_len,
hb_font_t *font,
GString *gs);
void serialize_message (unsigned int line_no,
const char *type,
const char *msg,
GString *gs);
void serialize_buffer_of_glyphs (hb_buffer_t *buffer,
unsigned int line_no,
const char *text,
unsigned int text_len,
hb_font_t *font,
hb_buffer_serialize_format_t output_format,
hb_buffer_serialize_flags_t format_flags,
GString *gs);
hb_bool_t show_glyph_names = true;
hb_bool_t show_positions = true;
hb_bool_t show_advances = true;
hb_bool_t show_clusters = true;
hb_bool_t show_text = false;
hb_bool_t show_unicode = false;
hb_bool_t show_line_num = false;
hb_bool_t show_extents = false;
hb_bool_t show_flags = false;
hb_bool_t trace = false;
};
static gboolean
parse_verbose (const char *name G_GNUC_UNUSED,
const char *arg G_GNUC_UNUSED,
gpointer data G_GNUC_UNUSED,
GError **error G_GNUC_UNUSED)
{
shape_format_options_t *format_opts = (shape_format_options_t *) data;
format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
return true;
}
static gboolean
parse_ned (const char *name G_GNUC_UNUSED,
const char *arg G_GNUC_UNUSED,
gpointer data G_GNUC_UNUSED,
GError **error G_GNUC_UNUSED)
{
shape_format_options_t *format_opts = (shape_format_options_t *) data;
format_opts->show_clusters = format_opts->show_advances = false;
return true;
}
inline void
shape_format_options_t::serialize (hb_buffer_t *buffer,
hb_font_t *font,
hb_buffer_serialize_format_t output_format,
hb_buffer_serialize_flags_t flags,
GString *gs)
{
unsigned int num_glyphs = hb_buffer_get_length (buffer);
unsigned int start = 0;
while (start < num_glyphs)
{
char buf[32768];
unsigned int consumed;
start += hb_buffer_serialize (buffer, start, num_glyphs,
buf, sizeof (buf), &consumed,
font, output_format, flags);
if (!consumed)
break;
g_string_append (gs, buf);
}
}
inline void
shape_format_options_t::serialize_line_no (unsigned int line_no,
GString *gs)
{
if (show_line_num)
g_string_append_printf (gs, "%u: ", line_no);
}
inline void
shape_format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer,
unsigned int line_no,
const char *text,
unsigned int text_len,
hb_font_t *font,
GString *gs)
{
if (show_text)
{
serialize_line_no (line_no, gs);
g_string_append_c (gs, '(');
g_string_append_len (gs, text, text_len);
g_string_append_c (gs, ')');
g_string_append_c (gs, '\n');
}
if (show_unicode)
{
serialize_line_no (line_no, gs);
serialize (buffer, font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT, gs);
g_string_append_c (gs, '\n');
}
}
inline void
shape_format_options_t::serialize_message (unsigned int line_no,
const char *type,
const char *msg,
GString *gs)
{
serialize_line_no (line_no, gs);
g_string_append_printf (gs, "%s: %s", type, msg);
g_string_append_c (gs, '\n');
}
inline void
shape_format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer,
unsigned int line_no,
const char *text,
unsigned int text_len,
hb_font_t *font,
hb_buffer_serialize_format_t output_format,
hb_buffer_serialize_flags_t format_flags,
GString *gs)
{
serialize_line_no (line_no, gs);
serialize (buffer, font, output_format, format_flags, gs);
g_string_append_c (gs, '\n');
}
void
shape_format_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Prefix each line of output with its corresponding input text", nullptr},
{"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Prefix each line of output with its corresponding input codepoint(s)", nullptr},
{"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Prefix each line of output with its corresponding input line number", nullptr},
{"verbose", 'v', G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_verbose, "Prefix each line of output with all of the above", nullptr},
{"no-glyph-names", 0, G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &this->show_glyph_names, "Output glyph indices instead of names", nullptr},
{"no-positions", 0, G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &this->show_positions, "Do not output glyph positions", nullptr},
{"no-advances", 0, G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &this->show_advances, "Do not output glyph advances", nullptr},
{"no-clusters", 0, G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &this->show_clusters, "Do not output cluster indices", nullptr},
{"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Output glyph extents", nullptr},
{"show-flags", 0, 0, G_OPTION_ARG_NONE, &this->show_flags, "Output glyph flags", nullptr},
{"ned", 'v', G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_ned, "No Extra Data; Do not output clusters or advances", nullptr},
{"trace", 'V', 0, G_OPTION_ARG_NONE, &this->trace, "Output interim shaping results", nullptr},
{nullptr}
};
parser->add_group (entries,
"output-syntax",
"Output syntax:\n"
" text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n"
" json: [{\"g\": <glyph name or index>, \"ax\": <horizontal advance>, \"ay\": <vertical advance>, \"dx\": <horizontal displacement>, \"dy\": <vertical displacement>, \"cl\": <glyph cluster index within input>}, ...]\n"
"\nOutput syntax options:",
"Options for the syntax of the output",
this);
}
#endif

View File

@@ -0,0 +1,446 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef SHAPE_OPTIONS_HH
#define SHAPE_OPTIONS_HH
#include "options.hh"
struct shape_options_t
{
~shape_options_t ()
{
g_free (direction);
g_free (language);
g_free (script);
free (features);
g_strfreev (shapers);
}
void add_options (option_parser_t *parser);
void setup_buffer (hb_buffer_t *buffer)
{
hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
hb_buffer_set_script (buffer, hb_script_from_string (script, -1));
hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
hb_buffer_set_flags (buffer, (hb_buffer_flags_t)
(HB_BUFFER_FLAG_DEFAULT |
(bot ? HB_BUFFER_FLAG_BOT : 0) |
(eot ? HB_BUFFER_FLAG_EOT : 0) |
(verify ? HB_BUFFER_FLAG_VERIFY : 0) |
(unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) |
(safe_to_insert_tatweel ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL : 0) |
(preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) |
(remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) |
0));
hb_buffer_set_invisible_glyph (buffer, invisible_glyph);
hb_buffer_set_not_found_glyph (buffer, not_found_glyph);
hb_buffer_set_cluster_level (buffer, cluster_level);
hb_buffer_guess_segment_properties (buffer);
}
void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len,
const char *text_before, const char *text_after,
hb_font_t *font)
{
hb_buffer_clear_contents (buffer);
if (glyphs)
{
/* Call the setup_buffer first while the buffer is empty,
* as guess_segment_properties doesn't like glyphs in the buffer. */
setup_buffer (buffer);
char *glyphs_text = (char *) text;
int glyphs_len = text_len;
if (glyphs_len < 0)
glyphs_len = strlen (glyphs_text);
if (glyphs_len && glyphs_text[glyphs_len - 1] != ']')
{
glyphs_text = g_strdup_printf ("%*s]", glyphs_len, glyphs_text);
glyphs_len = -1;
}
hb_buffer_deserialize_glyphs (buffer,
glyphs_text, glyphs_len,
nullptr,
font,
HB_BUFFER_SERIALIZE_FORMAT_TEXT);
if (!strchr (glyphs_text, '+'))
{
scale_advances = false;
unsigned count;
hb_direction_t direction = hb_buffer_get_direction (buffer);
hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, &count);
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, &count);
for (unsigned i = 0; i < count; i++)
hb_font_get_glyph_advance_for_direction (font,
infos[i].codepoint,
direction,
&positions[i].x_advance,
&positions[i].y_advance);
}
if (glyphs_text != text)
g_free (glyphs_text);
return;
}
if (text_before) {
unsigned int len = strlen (text_before);
hb_buffer_add_utf8 (buffer, text_before, len, len, 0);
}
hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
if (text_after) {
hb_buffer_add_utf8 (buffer, text_after, -1, 0, 0);
}
if (!utf8_clusters) {
/* Reset cluster values to refer to Unicode character index
* instead of UTF-8 index. */
unsigned int num_glyphs = hb_buffer_get_length (buffer);
hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
for (unsigned int i = 0; i < num_glyphs; i++)
{
info->cluster = i;
info++;
}
}
setup_buffer (buffer);
}
hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=nullptr)
{
if (glyphs)
{
/* Scale positions. */
int x_scale, y_scale;
hb_font_get_scale (font, &x_scale, &y_scale);
unsigned upem = hb_face_get_upem (hb_font_get_face (font));
unsigned count;
auto *positions = hb_buffer_get_glyph_positions (buffer, &count);
for (unsigned i = 0; i < count; i++)
{
auto &pos = positions[i];
pos.x_offset = pos.x_offset * x_scale / upem;
pos.y_offset = pos.y_offset * y_scale / upem;
if (scale_advances)
{
pos.x_advance = pos.x_advance * x_scale / upem;
pos.y_advance = pos.y_advance * y_scale / upem;
}
}
}
else
{
if (advance <= 0)
{
if (!hb_shape_full (font, buffer, features, num_features, shapers))
{
if (error)
*error = "Shaping failed.";
goto fail;
}
if (advance < 0)
{
float unit = (1 << SUBPIXEL_BITS);
/* Calculate buffer advance */
float w = 0;
unsigned count = 0;
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &count);
if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction (buffer)))
for (unsigned i = 0; i < count; i++)
w += pos[i].x_advance;
else
for (unsigned i = 0; i < count; i++)
w += pos[i].y_advance;
printf ("Default size: %u\n", (unsigned) roundf (w / unit));
exit (0);
}
}
#ifdef HB_EXPERIMENTAL_API
else
{
float unit = (1 << SUBPIXEL_BITS);
float target_advance = advance * unit;
float w = 0;
hb_tag_t var_tag;
float var_value;
if (!hb_shape_justify (font, buffer, features, num_features, shapers,
target_advance - unit * 0.5f, target_advance + unit * 0.5f,
&w, &var_tag, &var_value))
{
if (error)
*error = "Shaping failed.";
goto fail;
}
}
#endif
}
if (normalize_glyphs)
hb_buffer_normalize_glyphs (buffer);
return true;
fail:
return false;
}
void shape_closure (const char *text, int text_len,
hb_font_t *font, hb_buffer_t *buffer,
hb_set_t *glyphs)
{
hb_buffer_reset (buffer);
hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
setup_buffer (buffer);
hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs);
}
/* Buffer properties */
char *direction = nullptr;
char *language = nullptr;
char *script = nullptr;
/* Buffer flags */
hb_bool_t bot = false;
hb_bool_t eot = false;
hb_bool_t preserve_default_ignorables = false;
hb_bool_t remove_default_ignorables = false;
hb_feature_t *features = nullptr;
unsigned int num_features = 0;
char **shapers = nullptr;
signed advance = 0;
hb_bool_t utf8_clusters = false;
hb_codepoint_t invisible_glyph = 0;
hb_codepoint_t not_found_glyph = 0;
hb_buffer_cluster_level_t cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT;
hb_bool_t normalize_glyphs = false;
hb_bool_t glyphs = false;
bool scale_advances = true;
hb_bool_t verify = false;
hb_bool_t unsafe_to_concat = false;
hb_bool_t safe_to_insert_tatweel = false;
unsigned int num_iterations = 1;
};
static gboolean
parse_shapers (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
shape_options_t *shape_opts = (shape_options_t *) data;
char **shapers = g_strsplit (arg, ",", 0);
for (char **shaper = shapers; *shaper; shaper++)
{
bool found = false;
for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) {
if (strcmp (*shaper, *hb_shaper) == 0)
{
found = true;
break;
}
}
if (!found)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Unknown or unsupported shaper: %s", *shaper);
g_strfreev (shapers);
return false;
}
}
g_strfreev (shape_opts->shapers);
shape_opts->shapers = shapers;
return true;
}
static G_GNUC_NORETURN gboolean
list_shapers (const char *name G_GNUC_UNUSED,
const char *arg G_GNUC_UNUSED,
gpointer data G_GNUC_UNUSED,
GError **error G_GNUC_UNUSED)
{
for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
g_printf ("%s\n", *shaper);
exit(0);
}
static gboolean
parse_features (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
shape_options_t *shape_opts = (shape_options_t *) data;
char *s = (char *) arg;
size_t l = strlen (s);
char *p;
shape_opts->num_features = 0;
g_free (shape_opts->features);
shape_opts->features = nullptr;
/* if the string is quoted, strip the quotes */
if (s[0] == s[l - 1] && (s[0] == '\"' || s[0] == '\''))
{
s[l - 1] = '\0';
s++;
}
if (!*s)
return true;
/* count the features first, so we can allocate memory */
p = s;
do {
shape_opts->num_features++;
p = strpbrk (p, ", ");
if (p)
p++;
} while (p);
shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
if (!shape_opts->features)
return false;
/* now do the actual parsing */
p = s;
shape_opts->num_features = 0;
while (p && *p) {
char *end = strpbrk (p, ", ");
if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
shape_opts->num_features++;
p = end ? end + 1 : nullptr;
}
return true;
}
void
shape_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"list-shapers", 0, G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", nullptr},
{"shaper", 0, G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", nullptr},
{"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Set comma-separated list of shapers to try","list"},
{"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"},
{"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "BCP 47 tag"},
{"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"},
{"bot", 0, 0, G_OPTION_ARG_NONE, &this->bot, "Treat text as beginning-of-paragraph", nullptr},
{"eot", 0, 0, G_OPTION_ARG_NONE, &this->eot, "Treat text as end-of-paragraph", nullptr},
#ifdef HB_EXPERIMENTAL_API
{"justify-to", 0, 0,
G_OPTION_ARG_INT, &this->advance, "Target size to justify to", "SIZE, or -1"},
#endif
{"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->preserve_default_ignorables, "Preserve Default-Ignorable characters", nullptr},
{"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->remove_default_ignorables, "Remove Default-Ignorable characters", nullptr},
{"invisible-glyph", 0, 0, G_OPTION_ARG_INT, &this->invisible_glyph, "Glyph value to replace Default-Ignorables with", nullptr},
{"not-found-glyph", 0, 0, G_OPTION_ARG_INT, &this->not_found_glyph, "Glyph value to replace not-found characters with", nullptr},
{"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", nullptr},
{"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"},
{"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr},
{"unsafe-to-concat",0, 0, G_OPTION_ARG_NONE, &this->unsafe_to_concat, "Produce unsafe-to-concat glyph flag", nullptr},
{"safe-to-insert-tatweel",0, 0, G_OPTION_ARG_NONE, &this->safe_to_insert_tatweel, "Produce safe-to-insert-tatweel glyph flag", nullptr},
{"glyphs", 0, 0, G_OPTION_ARG_NONE, &this->glyphs, "Interpret input as glyph string", nullptr},
{"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr},
{"num-iterations", 'n',G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"},
{nullptr}
};
parser->add_group (entries,
"shape",
"Shape options:",
"Options for the shaping process",
this);
const gchar *features_help = "Comma-separated list of font features\n"
"\n"
" Features can be enabled or disabled, either globally or limited to\n"
" specific character ranges. The format for specifying feature settings\n"
" follows. All valid CSS font-feature-settings values other than 'normal'\n"
" and the global values are also accepted, though not documented below.\n"
" CSS string escapes are not supported."
"\n"
" The range indices refer to the positions between Unicode characters,\n"
" unless the --utf8-clusters is provided, in which case range indices\n"
" refer to UTF-8 byte indices. The position before the first character\n"
" is always 0.\n"
"\n"
" The format is Python-esque. Here is how it all works:\n"
"\n"
" Syntax: Value: Start: End:\n"
"\n"
" Setting value:\n"
" \"kern\" 1 0 ∞ # Turn feature on\n"
" \"+kern\" 1 0 ∞ # Turn feature on\n"
" \"-kern\" 0 0 ∞ # Turn feature off\n"
" \"kern=0\" 0 0 ∞ # Turn feature off\n"
" \"kern=1\" 1 0 ∞ # Turn feature on\n"
" \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n"
"\n"
" Setting index:\n"
" \"kern[]\" 1 0 ∞ # Turn feature on\n"
" \"kern[:]\" 1 0 ∞ # Turn feature on\n"
" \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n"
" \"kern[:5]\" 1 0 5 # Turn feature on, partial\n"
" \"kern[3:5]\" 1 3 5 # Turn feature on, range\n"
" \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n"
"\n"
" Mixing it all:\n"
"\n"
" \"aalt[3:5]=2\" 2 3 5 # Turn 2nd alternate on for range";
GOptionEntry entries2[] =
{
{"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"},
{nullptr}
};
parser->add_group (entries2,
"features",
"Features options:",
"Options for font features used",
this);
}
#endif

View File

@@ -0,0 +1,159 @@
/*
* Copyright © 2010 Behdad Esfahbod
* Copyright © 2011,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_SHAPE_OUTPUT_HH
#define HB_SHAPE_OUTPUT_HH
#include "shape-format.hh"
#include "output-options.hh"
struct shape_output_t : output_options_t<>
{
void add_options (option_parser_t *parser)
{
parser->set_summary ("Shape text with given font.");
output_options_t::add_options (parser, hb_buffer_serialize_list_formats ());
format.add_options (parser);
}
void init (hb_buffer_t *buffer, const font_options_t *font_opts)
{
gs = g_string_new (nullptr);
line_no = 0;
font = hb_font_reference (font_opts->font);
if (!output_format)
serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT;
else
serialize_format = hb_buffer_serialize_format_from_string (output_format, -1);
/* An empty "output_format" parameter basically skips output generating.
* Useful for benchmarking. */
if ((!output_format || *output_format) &&
!hb_buffer_serialize_format_to_string (serialize_format))
{
if (explicit_output_format)
fail (false, "Unknown output format `%s'; supported formats are: %s",
output_format,
g_strjoinv ("/", const_cast<char**> (hb_buffer_serialize_list_formats ())));
else
/* Just default to TEXT if not explicitly requested and the
* file extension is not recognized. */
serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT;
}
unsigned int flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT;
if (!format.show_glyph_names)
flags |= HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES;
if (!format.show_clusters)
flags |= HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS;
if (!format.show_positions)
flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS;
if (!format.show_advances)
flags |= HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES;
if (format.show_extents)
flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS;
if (format.show_flags)
flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS;
serialize_flags = (hb_buffer_serialize_flags_t) flags;
if (format.trace)
hb_buffer_set_message_func (buffer, message_func, this, nullptr);
}
void new_line () { line_no++; }
void consume_text (hb_buffer_t *buffer,
const char *text,
unsigned int text_len,
hb_bool_t utf8_clusters)
{
g_string_set_size (gs, 0);
format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, gs);
fprintf (out_fp, "%s", gs->str);
}
void error (const char *message)
{
g_string_set_size (gs, 0);
format.serialize_message (line_no, "error", message, gs);
fprintf (out_fp, "%s", gs->str);
}
void consume_glyphs (hb_buffer_t *buffer,
const char *text,
unsigned int text_len,
hb_bool_t utf8_clusters)
{
g_string_set_size (gs, 0);
format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font,
serialize_format, serialize_flags, gs);
fprintf (out_fp, "%s", gs->str);
}
void finish (hb_buffer_t *buffer, const font_options_t *font_opts)
{
hb_buffer_set_message_func (buffer, nullptr, nullptr, nullptr);
hb_font_destroy (font);
g_string_free (gs, true);
gs = nullptr;
font = nullptr;
}
static hb_bool_t
message_func (hb_buffer_t *buffer,
hb_font_t *font,
const char *message,
void *user_data)
{
shape_output_t *that = (shape_output_t *) user_data;
that->trace (buffer, font, message);
return true;
}
void
trace (hb_buffer_t *buffer,
hb_font_t *font,
const char *message)
{
g_string_set_size (gs, 0);
format.serialize_line_no (line_no, gs);
g_string_append_printf (gs, "trace: %s buffer: ", message);
format.serialize (buffer, font, serialize_format, serialize_flags, gs);
g_string_append_c (gs, '\n');
fprintf (stderr, "%s", gs->str);
}
protected:
shape_format_options_t format;
GString *gs = nullptr;
unsigned int line_no = 0;
hb_font_t *font = nullptr;
hb_buffer_serialize_format_t serialize_format = HB_BUFFER_SERIALIZE_FORMAT_INVALID;
hb_buffer_serialize_flags_t serialize_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT;
};
#endif

View File

@@ -0,0 +1,370 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef TEXT_OPTIONS_HH
#define TEXT_OPTIONS_HH
#include "options.hh"
struct text_options_t
{
text_options_t ()
: gs (g_string_new (nullptr))
{}
~text_options_t ()
{
g_free (text);
g_free (text_file);
if (gs)
g_string_free (gs, true);
if (in_fp && in_fp != stdin)
fclose (in_fp);
}
void add_options (option_parser_t *parser);
void post_parse (GError **error G_GNUC_UNUSED)
{
if (!text && !text_file)
text_file = g_strdup ("-");
if (text && text_file)
{
g_set_error (error,
G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Only one of text and text-file can be set");
return;
}
if (text_file)
{
if (0 != strcmp (text_file, "-"))
in_fp = fopen (text_file, "r");
else
in_fp = stdin;
if (!in_fp)
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Failed opening text file `%s': %s",
text_file, strerror (errno));
}
}
const char *get_line (unsigned int *len);
int text_len = -1;
char *text = nullptr;
char *text_file = nullptr;
private:
FILE *in_fp = nullptr;
GString *gs = nullptr;
char *line = nullptr;
unsigned line_len = UINT_MAX;
hb_bool_t single_par = false;
};
struct shape_text_options_t : text_options_t
{
~shape_text_options_t ()
{
g_free (text_before);
g_free (text_after);
}
void add_options (option_parser_t *parser);
char *text_before = nullptr;
char *text_after = nullptr;
};
static gboolean
parse_text (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
text_options_t *text_opts = (text_options_t *) data;
if (text_opts->text)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text or --unicodes can be provided but not both");
return false;
}
text_opts->text_len = -1;
text_opts->text = g_strdup (arg);
return true;
}
static bool
encode_unicodes (const char *unicodes,
GString *gs,
GError **error)
{
#define DELIMITERS "<+-|>{},;&#\\xXuUnNiI\n\t\v\f\r "
char *s = (char *) unicodes;
char *p;
while (s && *s)
{
while (*s && strchr (DELIMITERS, *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t u = strtoul (s, &p, 16);
if (errno || s == p)
{
g_string_free (gs, TRUE);
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing Unicode value at: '%s'", s);
return false;
}
g_string_append_unichar (gs, u);
s = p;
}
#undef DELIMITERS
return true;
}
static gboolean
parse_unicodes (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
text_options_t *text_opts = (text_options_t *) data;
if (text_opts->text)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text or --unicodes can be provided but not both");
return false;
}
GString *gs = g_string_new (nullptr);
if (0 == strcmp (arg, "*"))
g_string_append_c (gs, '*');
else
if (!encode_unicodes (arg, gs, error))
return false;
text_opts->text_len = gs->len;
text_opts->text = g_string_free (gs, FALSE);
return true;
}
static gboolean
parse_text_before (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
auto *opts = (shape_text_options_t *) data;
if (opts->text_before)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text-before or --unicodes-before can be provided but not both");
return false;
}
opts->text_before = g_strdup (arg);
fprintf(stderr, "%s\n", opts->text_before);
return true;
}
static gboolean
parse_unicodes_before (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
auto *opts = (shape_text_options_t *) data;
if (opts->text_before)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text-before or --unicodes-before can be provided but not both");
return false;
}
GString *gs = g_string_new (nullptr);
if (!encode_unicodes (arg, gs, error))
return false;
opts->text_before = g_string_free (gs, FALSE);
return true;
}
static gboolean
parse_text_after (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
auto *opts = (shape_text_options_t *) data;
if (opts->text_after)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text-after or --unicodes-after can be provided but not both");
return false;
}
opts->text_after = g_strdup (arg);
return true;
}
static gboolean
parse_unicodes_after (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
auto *opts = (shape_text_options_t *) data;
if (opts->text_after)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Either --text-after or --unicodes-after can be provided but not both");
return false;
}
GString *gs = g_string_new (nullptr);
if (!encode_unicodes (arg, gs, error))
return false;
opts->text_after = g_string_free (gs, FALSE);
return true;
}
const char *
text_options_t::get_line (unsigned int *len)
{
if (text)
{
if (!line)
{
line = text;
line_len = text_len;
}
if (line_len == UINT_MAX)
line_len = strlen (line);
if (!line_len)
{
*len = 0;
return nullptr;
}
const char *ret = line;
const char *p = single_par ? nullptr : (const char *) memchr (line, '\n', line_len);
unsigned int ret_len;
if (!p)
{
ret_len = line_len;
line += ret_len;
line_len = 0;
}
else
{
ret_len = p - ret;
line += ret_len + 1;
line_len -= ret_len + 1;
}
*len = ret_len;
return ret;
}
g_string_set_size (gs, 0);
char buf[BUFSIZ];
while (fgets (buf, sizeof (buf), in_fp))
{
unsigned bytes = strlen (buf);
if (!single_par && bytes && buf[bytes - 1] == '\n')
{
bytes--;
g_string_append_len (gs, buf, bytes);
break;
}
g_string_append_len (gs, buf, bytes);
}
if (ferror (in_fp))
fail (false, "Failed reading text: %s", strerror (errno));
*len = gs->len;
return !*len && feof (in_fp) ? nullptr : gs->str;
}
void
text_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"},
{"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name", "filename"},
{"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"},
{"single-par", 0, 0, G_OPTION_ARG_NONE, &this->single_par, "Treat text as single paragraph", nullptr},
{nullptr}
};
parser->add_group (entries,
"text",
"Text options:\n\nIf no text is provided, standard input is used for input.\n",
"Options for the input text",
this);
}
void
shape_text_options_t::add_options (option_parser_t *parser)
{
text_options_t::add_options (parser);
GOptionEntry entries[] =
{
{"text-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_before, "Set text context before each line", "string"},
{"text-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_after, "Set text context after each line", "string"},
{"unicodes-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_before, "Set Unicode codepoints context before each line", "list of hex numbers"},
{"unicodes-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_after, "Set Unicode codepoints context after each line", "list of hex numbers"},
{nullptr}
};
parser->add_group (entries,
"text-context",
"Textual context options:",
"Options for the input context text",
this);
}
#endif

View File

@@ -0,0 +1,220 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef VIEW_CAIRO_HH
#define VIEW_CAIRO_HH
#include "view-options.hh"
#include "output-options.hh"
#include "helper-cairo.hh"
struct view_cairo_t : view_options_t, output_options_t<>
{
~view_cairo_t ()
{
cairo_debug_reset_static_data ();
}
void add_options (option_parser_t *parser)
{
parser->set_summary ("View text with given font.");
view_options_t::add_options (parser);
output_options_t::add_options (parser, helper_cairo_supported_formats);
}
void init (hb_buffer_t *buffer, const font_options_t *font_opts)
{
lines = g_array_new (false, false, sizeof (helper_cairo_line_t));
subpixel_bits = font_opts->subpixel_bits;
}
void new_line () {}
void consume_text (hb_buffer_t *buffer,
const char *text,
unsigned int text_len,
hb_bool_t utf8_clusters) {}
void error (const char *message)
{ g_printerr ("%s: %s\n", g_get_prgname (), message); }
void consume_glyphs (hb_buffer_t *buffer,
const char *text,
unsigned int text_len,
hb_bool_t utf8_clusters)
{
direction = hb_buffer_get_direction (buffer);
helper_cairo_line_t l (text, text_len, buffer, utf8_clusters, subpixel_bits);
g_array_append_val (lines, l);
}
void finish (hb_buffer_t *buffer, const font_options_t *font_opts)
{
render (font_opts);
for (unsigned int i = 0; i < lines->len; i++) {
helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
line.finish ();
}
#if GLIB_CHECK_VERSION (2, 22, 0)
g_array_unref (lines);
#else
g_array_free (lines, TRUE);
#endif
}
protected:
void render (const font_options_t *font_opts);
hb_direction_t direction = HB_DIRECTION_INVALID; // Remove this, make segment_properties accessible
GArray *lines = nullptr;
unsigned subpixel_bits = 0;
};
inline void
view_cairo_t::render (const font_options_t *font_opts)
{
bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
int vert = vertical ? 1 : 0;
int horiz = vertical ? 0 : 1;
int x_sign = font_opts->font_size_x < 0 ? -1 : +1;
int y_sign = font_opts->font_size_y < 0 ? -1 : +1;
hb_font_t *font = font_opts->font;
if (!have_font_extents)
{
hb_font_extents_t hb_extents;
hb_font_get_extents_for_direction (font, direction, &hb_extents);
font_extents.ascent = scalbn ((double) hb_extents.ascender, - (int) subpixel_bits);
font_extents.descent = -scalbn ((double) hb_extents.descender, - (int) subpixel_bits);
font_extents.line_gap = scalbn ((double) hb_extents.line_gap, - (int) subpixel_bits);
have_font_extents = true;
}
double ascent = y_sign * font_extents.ascent;
double descent = y_sign * font_extents.descent;
double line_gap = y_sign * font_extents.line_gap + line_space;
double leading = ascent + descent + line_gap;
/* Calculate surface size. */
double w = 0, h = 0;
(vertical ? w : h) = (int) lines->len * leading - (font_extents.line_gap + line_space);
(vertical ? h : w) = 0;
for (unsigned int i = 0; i < lines->len; i++) {
helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
double x_advance, y_advance;
line.get_advance (&x_advance, &y_advance);
if (vertical)
h = MAX (h, y_sign * y_advance);
else
w = MAX (w, x_sign * x_advance);
}
cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts,
this);
/* See if font needs color. */
cairo_content_t content = CAIRO_CONTENT_ALPHA;
if (helper_cairo_scaled_font_has_color (scaled_font))
content = CAIRO_CONTENT_COLOR;
/* Create surface. */
cairo_t *cr = helper_cairo_create_context (w + margin.l + margin.r,
h + margin.t + margin.b,
this,
this,
content);
cairo_set_scaled_font (cr, scaled_font);
/* Setup coordinate system. */
cairo_translate (cr, margin.l, margin.t);
if (vertical)
cairo_translate (cr,
w - ascent, /* We currently always stack lines right to left */
y_sign < 0 ? h : 0);
else
{
cairo_translate (cr,
x_sign < 0 ? w : 0,
y_sign < 0 ? descent : ascent);
}
/* Draw. */
cairo_translate (cr, +vert * leading, -horiz * leading);
for (unsigned int i = 0; i < lines->len; i++)
{
helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i);
cairo_translate (cr, -vert * leading, +horiz * leading);
if (show_extents)
{
cairo_save (cr);
cairo_set_source_rgba (cr, 1., 0., 0., .5);
cairo_set_line_width (cr, 10);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
for (unsigned i = 0; i < l.num_glyphs; i++) {
cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y);
cairo_rel_line_to (cr, 0, 0);
}
cairo_stroke (cr);
cairo_restore (cr);
cairo_save (cr);
cairo_set_source_rgba (cr, 1., 0., 1., .5);
cairo_set_line_width (cr, 3);
for (unsigned i = 0; i < l.num_glyphs; i++)
{
hb_glyph_extents_t hb_extents;
hb_font_get_glyph_extents (font, l.glyphs[i].index, &hb_extents);
double x1 = scalbn ((double) hb_extents.x_bearing, - (int) subpixel_bits);
double y1 = -scalbn ((double) hb_extents.y_bearing, - (int) subpixel_bits);
double width = scalbn ((double) hb_extents.width, - (int) subpixel_bits);
double height = -scalbn ((double) hb_extents.height, - (int) subpixel_bits);
cairo_rectangle (cr, l.glyphs[i].x + x1, l.glyphs[i].y + y1, width, height);
}
cairo_stroke (cr);
cairo_restore (cr);
}
if (l.num_clusters)
cairo_show_text_glyphs (cr,
l.utf8, l.utf8_len,
l.glyphs, l.num_glyphs,
l.clusters, l.num_clusters,
l.cluster_flags);
else
cairo_show_glyphs (cr, l.glyphs, l.num_glyphs);
}
/* Clean up. */
helper_cairo_destroy_context (cr);
cairo_scaled_font_destroy (scaled_font);
}
#endif

View File

@@ -0,0 +1,130 @@
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef VIEW_OPTIONS_HH
#define VIEW_OPTIONS_HH
#include "options.hh"
#define DEFAULT_MARGIN 16
#define DEFAULT_FORE "#000000"
#define DEFAULT_BACK "#FFFFFF"
struct view_options_t
{
~view_options_t ()
{
g_free (fore);
g_free (back);
g_free (custom_palette);
}
void add_options (option_parser_t *parser);
char *fore = nullptr;
char *back = nullptr;
unsigned int palette = 0;
char *custom_palette = nullptr;
double line_space = 0;
bool have_font_extents = false;
struct font_extents_t {
double ascent, descent, line_gap;
} font_extents = {0., 0., 0.};
struct margin_t {
double t, r, b, l;
} margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN};
hb_bool_t show_extents = false;
};
static gboolean
parse_font_extents (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
view_options_t *view_opts = (view_options_t *) data;
view_options_t::font_extents_t &e = view_opts->font_extents;
switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf", &e.ascent, &e.descent, &e.line_gap)) {
case 1: HB_FALLTHROUGH;
case 2: HB_FALLTHROUGH;
case 3:
view_opts->have_font_extents = true;
return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one to three space-separated numbers",
name);
return false;
}
}
static gboolean
parse_margin (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
view_options_t *view_opts = (view_options_t *) data;
view_options_t::margin_t &m = view_opts->margin;
switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) {
case 1: m.r = m.t; HB_FALLTHROUGH;
case 2: m.b = m.t; HB_FALLTHROUGH;
case 3: m.l = m.r; HB_FALLTHROUGH;
case 4: return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one to four space-separated numbers",
name);
return false;
}
}
void
view_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"annotate", 0, G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_NONE, &this->show_extents, "Annotate output rendering", nullptr},
{"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"},
{"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa"},
{"font-palette", 0, 0, G_OPTION_ARG_INT, &this->palette, "Set font palette (default: 0)", "index"},
{"custom-palette", 0, 0, G_OPTION_ARG_STRING, &this->custom_palette, "Custom palette", "comma-separated colors"},
{"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"},
{"font-extents", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_extents, "Set font ascent/descent/line-gap (default: auto)","one to three numbers"},
{"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
{"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Draw glyph extents", nullptr},
{nullptr}
};
parser->add_group (entries,
"view",
"View options:",
"Options for output rendering",
this);
}
#endif