add harfbuzz library

This commit is contained in:
Sasha Szpakowski
2022-12-26 18:53:21 -04:00
parent 46a2cbb95d
commit 2d13144fcd
517 changed files with 177200 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# 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_FREETYPE
if HAVE_CAIRO_FT
hb_view_SOURCES = $(HB_VIEW_sources)
hb_view_LDADD = \
$(LDADD) \
$(CAIRO_LIBS) \
$(CAIRO_FT_LIBS) \
$(CHAFA_LIBS) \
$(NULL)
bin_PROGRAMS += hb-view
endif # HAVE_CAIRO_FT
endif # HAVE_FREETYPE
hb_shape_SOURCES = $(HB_SHAPE_sources)
bin_PROGRAMS += hb-shape
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
+52
View File
@@ -0,0 +1,52 @@
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-user.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_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)
+426
View File
@@ -0,0 +1,426 @@
/*
* 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 <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for isatty() */
#endif
#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 ESC_E (char)27
#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)
{
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) {
printf ("%c[%dm", ESC_E, 40 + bi.bg);
last_bg = bi.bg;
}
printf (" ");
} 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) {
printf ("%c[%d;%dm", ESC_E, 30 + bi.bg, 40 + bi.fg);
last_bg = bi.fg;
last_fg = bi.bg;
}
} else {
if (last_bg != bi.bg || last_fg != bi.fg) {
printf ("%c[%d;%dm", ESC_E, 40 + bi.bg, 30 + bi.fg);
last_bg = bi.bg;
last_fg = bi.fg;
}
}
printf ("%s", c);
}
}
printf ("%c[0m\n", ESC_E); /* Reset */
last_bg = last_fg = -1;
}
}
#endif
+75
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
+142
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", 0, 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
+329
View File
@@ -0,0 +1,329 @@
/*
* 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 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;
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_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_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;
}
}
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: %d)", 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, 0,
G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"},
{"font-slant", 0, 0,
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[] =
{
{"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
+222
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;
}
+149
View File
@@ -0,0 +1,149 @@
/*
* 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;
const hb_font_funcs_t *ffuncs;
if (!(ffuncs = 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 = 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
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 */
+122
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);
}
+43
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);
}
+973
View File
@@ -0,0 +1,973 @@
/*
* 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): Garret Rieger, Rod Sheeter
*/
#include "batch.hh"
#include "face-options.hh"
#include "main-font-text.hh"
#include "output-options.hh"
#include <hb-subset.h>
static hb_face_t* preprocess_face(hb_face_t* face)
{
#ifdef HB_EXPERIMENTAL_API
return hb_subset_preprocess (face);
#else
return hb_face_reference(face);
#endif
}
/*
* Command line interface to the harfbuzz font subsetter.
*/
struct subset_main_t : option_parser_t, face_options_t, output_options_t<false>
{
subset_main_t ()
: input (hb_subset_input_create_or_fail ())
{}
~subset_main_t ()
{
hb_subset_input_destroy (input);
}
void parse_face (int argc, const char * const *argv)
{
option_parser_t parser;
face_options_t face_opts;
face_opts.add_options (&parser);
GOptionEntry entries[] =
{
{G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_CALLBACK, (gpointer) &collect_face, nullptr, "[FONT-FILE] [TEXT]"},
{nullptr}
};
parser.add_main_group (entries, &face_opts);
parser.add_options ();
g_option_context_set_ignore_unknown_options (parser.context, true);
g_option_context_set_help_enabled (parser.context, false);
char **args = (char **)
#if GLIB_CHECK_VERSION (2, 68, 0)
g_memdup2
#else
g_memdup
#endif
(argv, argc * sizeof (*argv));
parser.parse (&argc, &args);
g_free (args);
set_face (face_opts.face);
}
void parse (int argc, char **argv)
{
bool help = false;
for (auto i = 1; i < argc; i++)
if (!strncmp ("--help", argv[i], 6))
{
help = true;
break;
}
if (likely (!help))
{
/* Do a preliminary parse to load font-face, such that we can use it
* during main option parsing. */
parse_face (argc, argv);
}
add_options ();
option_parser_t::parse (&argc, &argv);
}
int operator () (int argc, char **argv)
{
parse (argc, argv);
hb_face_t* orig_face = face;
if (preprocess)
orig_face = preprocess_face (face);
hb_face_t *new_face = nullptr;
for (unsigned i = 0; i < num_iterations; i++)
{
hb_face_destroy (new_face);
new_face = hb_subset_or_fail (face, input);
}
bool success = new_face;
if (success)
{
hb_blob_t *result = hb_face_reference_blob (new_face);
write_file (output_file, result);
hb_blob_destroy (result);
}
hb_face_destroy (new_face);
if (preprocess)
hb_face_destroy (orig_face);
return success ? 0 : 1;
}
bool
write_file (const char *output_file, hb_blob_t *blob)
{
assert (out_fp);
unsigned int size;
const char* data = hb_blob_get_data (blob, &size);
while (size)
{
size_t ret = fwrite (data, 1, size, out_fp);
size -= ret;
data += ret;
if (size && ferror (out_fp))
fail (false, "Failed to write output: %s", strerror (errno));
}
return true;
}
void add_options ();
protected:
static gboolean
collect_face (const char *name,
const char *arg,
gpointer data,
GError **error);
static gboolean
collect_rest (const char *name,
const char *arg,
gpointer data,
GError **error);
public:
unsigned num_iterations = 1;
gboolean preprocess;
hb_subset_input_t *input = nullptr;
};
static gboolean
parse_gids (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input);
if (!is_remove && !is_add) hb_set_clear (gids);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (gids);
if (!is_remove)
hb_set_invert (gids);
return true;
}
char *s = (char *) arg;
char *p;
while (s && *s)
{
while (*s && strchr (", ", *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t start_code = strtoul (s, &p, 10);
if (s[0] == '-' || errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing glyph-index at: '%s'", s);
return false;
}
if (p && p[0] == '-') // ranges
{
s = ++p;
hb_codepoint_t end_code = strtoul (s, &p, 10);
if (s[0] == '-' || errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing glyph-index at: '%s'", s);
return false;
}
if (end_code < start_code)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid glyph-index range %u-%u", start_code, end_code);
return false;
}
if (!is_remove)
hb_set_add_range (gids, start_code, end_code);
else
hb_set_del_range (gids, start_code, end_code);
}
else
{
if (!is_remove)
hb_set_add (gids, start_code);
else
hb_set_del (gids, start_code);
}
s = p;
}
return true;
}
static gboolean
parse_glyphs (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input);
if (!is_remove && !is_add) hb_set_clear (gids);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (gids);
if (!is_remove)
hb_set_invert (gids);
return true;
}
const char *p = arg;
const char *p_end = arg + strlen (arg);
hb_font_t *font = hb_font_create (subset_main->face);
while (p < p_end)
{
while (p < p_end && (*p == ' ' || *p == ','))
p++;
const char *end = p;
while (end < p_end && *end != ' ' && *end != ',')
end++;
if (p < end)
{
hb_codepoint_t gid;
if (!hb_font_get_glyph_from_name (font, p, end - p, &gid))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing glyph name: '%s'", p);
return false;
}
if (!is_remove)
hb_set_add (gids, gid);
else
hb_set_del (gids, gid);
}
p = end + 1;
}
hb_font_destroy (font);
return true;
}
static gboolean
parse_text (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input);
if (!is_remove && !is_add) hb_set_clear (unicodes);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (unicodes);
if (!is_remove)
hb_set_invert (unicodes);
return true;
}
for (gchar *c = (gchar *) arg;
*c;
c = g_utf8_find_next_char(c, nullptr))
{
gunichar cp = g_utf8_get_char(c);
if (!is_remove)
hb_set_add (unicodes, cp);
else
hb_set_del (unicodes, cp);
}
return true;
}
static gboolean
parse_unicodes (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input);
if (!is_remove && !is_add) hb_set_clear (unicodes);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (unicodes);
if (!is_remove)
hb_set_invert (unicodes);
return true;
}
// XXX TODO Ranges
#define DELIMITERS "<+->{},;&#\\xXuUnNiI\n\t\v\f\r "
char *s = (char *) arg;
char *p;
while (s && *s)
{
while (*s && strchr (DELIMITERS, *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t start_code = strtoul (s, &p, 16);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing Unicode at: '%s'", s);
return false;
}
if (p && p[0] == '-') // ranges
{
s = ++p;
hb_codepoint_t end_code = strtoul (s, &p, 16);
if (s[0] == '-' || errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing Unicode at: '%s'", s);
return false;
}
if (end_code < start_code)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid Unicode range %u-%u", start_code, end_code);
return false;
}
if (!is_remove)
hb_set_add_range (unicodes, start_code, end_code);
else
hb_set_del_range (unicodes, start_code, end_code);
}
else
{
if (!is_remove)
hb_set_add (unicodes, start_code);
else
hb_set_del (unicodes, start_code);
}
s = p;
}
return true;
}
static gboolean
parse_nameids (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *name_ids = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_ID);
if (!is_remove && !is_add) hb_set_clear (name_ids);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (name_ids);
if (!is_remove)
hb_set_invert (name_ids);
return true;
}
char *s = (char *) arg;
char *p;
while (s && *s)
{
while (*s && strchr (", ", *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t u = strtoul (s, &p, 10);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing nameID at: '%s'", s);
return false;
}
if (!is_remove)
{
hb_set_add (name_ids, u);
} else {
hb_set_del (name_ids, u);
}
s = p;
}
return true;
}
static gboolean
parse_name_languages (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *name_languages = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_LANG_ID);
if (!is_remove && !is_add) hb_set_clear (name_languages);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (name_languages);
if (!is_remove)
hb_set_invert (name_languages);
return true;
}
char *s = (char *) arg;
char *p;
while (s && *s)
{
while (*s && strchr (", ", *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t u = strtoul (s, &p, 10);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing name-language code at: '%s'", s);
return false;
}
if (!is_remove)
{
hb_set_add (name_languages, u);
} else {
hb_set_del (name_languages, u);
}
s = p;
}
return true;
}
template <hb_subset_flags_t flag>
static gboolean
set_flag (const char *name,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_subset_input_set_flags (subset_main->input,
hb_subset_input_get_flags (subset_main->input) | flag);
return true;
}
static gboolean
parse_layout_tag_list (hb_subset_sets_t set_type,
const char *name,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *layout_tags = hb_subset_input_set (subset_main->input, set_type);
if (!is_remove && !is_add) hb_set_clear (layout_tags);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (layout_tags);
if (!is_remove)
hb_set_invert (layout_tags);
return true;
}
char *s = strtok((char *) arg, ", ");
while (s)
{
if (strlen (s) > 4) // tags are at most 4 bytes
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing table tag at: '%s'", s);
return false;
}
hb_tag_t tag = hb_tag_from_string (s, strlen (s));
if (!is_remove)
hb_set_add (layout_tags, tag);
else
hb_set_del (layout_tags, tag);
s = strtok(nullptr, ", ");
}
return true;
}
static gboolean
parse_layout_features (const char *name,
const char *arg,
gpointer data,
GError **error)
{
return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_FEATURE_TAG,
name,
arg,
data,
error);
}
static gboolean
parse_layout_scripts (const char *name,
const char *arg,
gpointer data,
GError **error)
{
return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG,
name,
arg,
data,
error);
}
static gboolean
parse_drop_tables (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *drop_tables = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_DROP_TABLE_TAG);
if (!is_remove && !is_add) hb_set_clear (drop_tables);
if (0 == strcmp (arg, "*"))
{
hb_set_clear (drop_tables);
if (!is_remove)
hb_set_invert (drop_tables);
return true;
}
char *s = strtok((char *) arg, ", ");
while (s)
{
if (strlen (s) > 4) // Table tags are at most 4 bytes.
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing table tag at: '%s'", s);
return false;
}
hb_tag_t tag = hb_tag_from_string (s, strlen (s));
if (!is_remove)
hb_set_add (drop_tables, tag);
else
hb_set_del (drop_tables, tag);
s = strtok(nullptr, ", ");
}
return true;
}
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
static gboolean
parse_instance (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
char *s = strtok((char *) arg, "=");
while (s)
{
unsigned len = strlen (s);
if (len > 4) //Axis tags are 4 bytes.
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis tag at: '%s'", s);
return false;
}
hb_tag_t axis_tag = hb_tag_from_string (s, len);
s = strtok(nullptr, ", ");
if (!s)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag));
return false;
}
if (strcmp (s, "drop") == 0)
{
if (!hb_subset_input_pin_axis_to_default (subset_main->input, subset_main->face, axis_tag))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
}
else
{
errno = 0;
char *p;
float axis_value = strtof (s, &p);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
if (!hb_subset_input_pin_axis_location (subset_main->input, subset_main->face, axis_tag, axis_value))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
}
s = strtok(nullptr, "=");
}
return true;
}
#endif
#endif
template <GOptionArgFunc line_parser, bool allow_comments=true>
static gboolean
parse_file_for (const char *name,
const char *arg,
gpointer data,
GError **error)
{
FILE *fp = nullptr;
if (0 != strcmp (arg, "-"))
fp = fopen (arg, "r");
else
fp = stdin;
if (!fp)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Failed opening file `%s': %s",
arg, strerror (errno));
return false;
}
GString *gs = g_string_new (nullptr);
do
{
g_string_set_size (gs, 0);
char buf[BUFSIZ];
while (fgets (buf, sizeof (buf), fp))
{
unsigned bytes = strlen (buf);
if (bytes && buf[bytes - 1] == '\n')
{
bytes--;
g_string_append_len (gs, buf, bytes);
break;
}
g_string_append_len (gs, buf, bytes);
}
if (ferror (fp))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Failed reading file `%s': %s",
arg, strerror (errno));
return false;
}
g_string_append_c (gs, '\0');
if (allow_comments)
{
char *comment = strchr (gs->str, '#');
if (comment)
*comment = '\0';
}
line_parser ("+", gs->str, data, error);
if (*error)
break;
}
while (!feof (fp));
g_string_free (gs, false);
return true;
}
gboolean
subset_main_t::collect_face (const char *name,
const char *arg,
gpointer data,
GError **error)
{
face_options_t *thiz = (face_options_t *) data;
if (!thiz->font_file)
{
thiz->font_file = g_strdup (arg);
return true;
}
return true;
}
gboolean
subset_main_t::collect_rest (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *thiz = (subset_main_t *) data;
if (!thiz->font_file)
{
thiz->font_file = g_strdup (arg);
return true;
}
parse_text (name, arg, data, error);
return true;
}
void
subset_main_t::add_options ()
{
set_summary ("Subset fonts to specification.");
face_options_t::add_options (this);
GOptionEntry glyphset_entries[] =
{
{"gids", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids,
"Specify glyph IDs or ranges to include in the subset.\n"
" "
"Use --gids-=... to subtract codepoints from the current set.", "list of glyph indices/ranges or *"},
{"gids-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to remove from the subset", "list of glyph indices/ranges or *"},
{"gids+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of glyph indices/ranges or *"},
{"gids-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_gids>, "Specify file to read glyph IDs or ranges from", "filename"},
{"glyphs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset. Use --glyphs-=... to subtract glyphs from the current set.", "list of glyph names or *"},
{"glyphs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset", "list of glyph names"},
{"glyphs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to remove from the subset", "list of glyph names"},
{"glyphs-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_glyphs>, "Specify file to read glyph names from", "filename"},
{"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset. Use --text-=... to subtract codepoints from the current set.", "string"},
{"text-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to remove from the subset", "string"},
{"text+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset", "string"},
{"text-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_text, false>,"Specify file to read text from", "filename"},
{"unicodes", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes,
"Specify Unicode codepoints or ranges to include in the subset. Use * to include all codepoints.\n"
" "
"--unicodes-=... can be used to subtract codepoints from the current set.\n"
" "
"For example: --unicodes=* --unicodes-=41,42,43 would create a subset with all codepoints\n"
" "
"except for 41, 42, 43.",
"list of hex numbers/ranges or *"},
{"unicodes-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to remove from the subset", "list of hex numbers/ranges or *"},
{"unicodes+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to include in the subset", "list of hex numbers/ranges or *"},
{"unicodes-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_unicodes>,"Specify file to read Unicode codepoints or ranges from", "filename"},
{nullptr}
};
add_group (glyphset_entries,
"subset-glyphset",
"Subset glyph-set option:",
"Subsetting glyph-set options",
this);
GOptionEntry other_entries[] =
{
{"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids. Use --name-IDs-=... to subtract from the current set.", "list of int numbers or *"},
{"name-IDs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"},
{"name-IDs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"},
{"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs. Use --name-languages-=... to subtract from the current set.", "list of int numbers or *"},
{"name-languages-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"},
{"name-languages+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"},
{"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved. Use --layout-features-=... to subtract from the current set.", "list of string table tags or *"},
{"layout-features+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"},
{"layout-features-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"},
{"layout-scripts", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved. Use --layout-scripts-=... to subtract from the current set.", "list of string table tags or *"},
{"layout-scripts+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"},
{"layout-scripts-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"},
{"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables. Use --drop-tables-=... to subtract from the current set.", "list of string table tags or *"},
{"drop-tables+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"},
{"drop-tables-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"},
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
{"instance", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance,
"(Partially|Fully) Instantiate a variable font. A location consists of the tag of a variation axis, followed by '=', followed by a\n"
"number or the literal string 'drop'\n"
" "
"For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=drop\"\n"
"Note: currently only fully instancing to the default location is supported\n",
"list of comma separated axis-locations"},
#endif
#endif
{nullptr}
};
add_group (other_entries,
"subset-other",
"Subset other option:",
"Subsetting other options",
this);
GOptionEntry flag_entries[] =
{
{"no-hinting", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_HINTING>, "Whether to drop hints", nullptr},
{"retain-gids", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_RETAIN_GIDS>, "If set don't renumber glyph ids in the subset.", nullptr},
{"desubroutinize", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_DESUBROUTINIZE>, "Remove CFF/CFF2 use of subroutines", nullptr},
{"name-legacy", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NAME_LEGACY>, "Keep legacy (non-Unicode) 'name' table entries", nullptr},
{"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG>, "Set the overlaps flag on each glyph.", nullptr},
{"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NOTDEF_OUTLINE>, "Keep the outline of \'.notdef\' glyph", nullptr},
{"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES>, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr},
{"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_GLYPH_NAMES>, "Keep PS glyph names in TT-flavored fonts. ", nullptr},
{"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED>, "Do not drop tables that the tool does not know how to subset.", nullptr},
{"preprocess-face", 0, 0, G_OPTION_ARG_NONE, &this->preprocess,
"If set preprocesses the face with the add accelerator option before actually subsetting.", nullptr},
{nullptr}
};
add_group (flag_entries,
"subset-flags",
"Subset boolean option:",
"Subsetting boolean options",
this);
GOptionEntry app_entries[] =
{
{"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT,
&this->num_iterations,
"Run subsetter N times (default: 1)", "N"},
{nullptr}
};
add_group (app_entries,
"subset-app",
"Subset app option:",
"Subsetting application options",
this);
output_options_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 ();
}
int
main (int argc, char **argv)
{
return batch_main<subset_main_t, true> (argc, argv);
}
+43
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);
}
+223
View File
@@ -0,0 +1,223 @@
/*
* 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)
{
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 */
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 */
fwrite (gs->str, sizeof (char), gs->len, stdout);
if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS)
fputc ('\n', stdout);
/* 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);
else
#endif
ansi_print_image_rgb24 (data, width, height, stride / 4);
}
cairo_surface_destroy (surface);
return CAIRO_STATUS_SUCCESS;
}
#endif
+120
View File
@@ -0,0 +1,120 @@
/*
* 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 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
{
#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
+316
View File
@@ -0,0 +1,316 @@
/*
* 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_USER_HH
#define HELPER_CAIRO_USER_HH
#include "font-options.hh"
#include <cairo.h>
#include <hb.h>
#include "hb-blob.hh"
static void
move_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
cairo_move_to (cr,
(double) to_x, (double) to_y);
}
static void
line_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
cairo_line_to (cr,
(double) to_x, (double) to_y);
}
static void
cubic_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y,
void *)
{
cairo_curve_to (cr,
(double) control1_x, (double) control1_y,
(double) control2_x, (double) control2_y,
(double) to_x, (double) to_y);
}
static void
close_path (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
void *)
{
cairo_close_path (cr);
}
static hb_draw_funcs_t *
get_cairo_draw_funcs ()
{
static hb_draw_funcs_t *funcs;
if (!funcs)
{
funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr);
}
return funcs;
}
static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0};
static cairo_status_t
render_glyph (cairo_scaled_font_t *scaled_font,
unsigned long glyph,
cairo_t *cr,
cairo_text_extents_t *extents)
{
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&_hb_font_cairo_user_data_key));
hb_position_t x_scale, y_scale;
hb_font_get_scale (font, &x_scale, &y_scale);
cairo_scale (cr, +1./x_scale, -1./y_scale);
hb_font_get_glyph_shape (font, glyph, get_cairo_draw_funcs (), cr);
cairo_fill (cr);
return CAIRO_STATUS_SUCCESS;
}
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
#ifdef CAIRO_HAS_PNG_FUNCTIONS
static inline cairo_status_t
_hb_bytes_read_func (hb_bytes_t *src,
char *data,
unsigned length)
{
if (unlikely (src->length < length))
return CAIRO_STATUS_READ_ERROR;
memcpy (data, src->arrayZ, length);
*src += length;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
render_color_glyph_png (cairo_scaled_font_t *scaled_font,
unsigned long glyph,
cairo_t *cr,
cairo_text_extents_t *extents)
{
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&_hb_font_cairo_user_data_key));
hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph);
if (blob == hb_blob_get_empty ())
return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
hb_position_t x_scale, y_scale;
hb_font_get_scale (font, &x_scale, &y_scale);
cairo_scale (cr, +1./x_scale, -1./y_scale);
/* Draw PNG. */
hb_bytes_t bytes = blob->as_bytes ();
cairo_surface_t *surface = cairo_image_surface_create_from_png_stream ((cairo_read_func_t) _hb_bytes_read_func,
std::addressof (bytes));
hb_blob_destroy (blob);
if (unlikely (cairo_surface_status (surface)) != CAIRO_STATUS_SUCCESS)
{
cairo_surface_destroy (surface);
return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
}
int width = cairo_image_surface_get_width (surface);
int height = cairo_image_surface_get_width (surface);
hb_glyph_extents_t hb_extents;
if (unlikely (!hb_font_get_glyph_extents (font, glyph, &hb_extents)))
{
cairo_surface_destroy (surface);
return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
}
cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0};
cairo_pattern_set_matrix (pattern, &matrix);
cairo_translate (cr, hb_extents.x_bearing, hb_extents.y_bearing);
cairo_scale (cr, hb_extents.width, hb_extents.height);
cairo_set_source (cr, pattern);
cairo_rectangle (cr, 0, 0, 1, 1);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
cairo_surface_destroy (surface);
return CAIRO_STATUS_SUCCESS;
}
#endif
static cairo_status_t
render_color_glyph_layers (cairo_scaled_font_t *scaled_font,
unsigned long glyph,
cairo_t *cr,
cairo_text_extents_t *extents)
{
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&_hb_font_cairo_user_data_key));
hb_face_t *face = hb_font_get_face (font);
unsigned count = hb_ot_color_glyph_get_layers (face, glyph, 0, nullptr, nullptr);
if (!count)
return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
hb_ot_color_layer_t layers[16];
unsigned offset = 0, len;
do {
len = ARRAY_LENGTH (layers);
hb_ot_color_glyph_get_layers (face, glyph,
offset,
&len,
layers);
for (unsigned i = 0; i < len; i++)
{
hb_color_t color;
unsigned clen = 1;
unsigned color_index = layers[i].color_index;
bool is_foreground = color_index == 65535;
if (!is_foreground)
{
hb_ot_color_palette_get_colors (face,
0/*palette_index*/,
color_index/*start_offset*/,
&clen/*color_count*/,
&color);
if (clen < 1)
continue;
}
cairo_save (cr);
{
if (!is_foreground)
cairo_set_source_rgba (cr,
hb_color_get_red (color) / 255.,
hb_color_get_green (color) / 255.,
hb_color_get_blue (color) / 255.,
hb_color_get_alpha (color) / 255.);
cairo_status_t ret = render_glyph (scaled_font, layers[i].glyph, cr, extents);
if (ret != CAIRO_STATUS_SUCCESS)
return ret;
}
cairo_restore (cr);
}
}
while (len == ARRAY_LENGTH (layers));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
render_color_glyph (cairo_scaled_font_t *scaled_font,
unsigned long glyph,
cairo_t *cr,
cairo_text_extents_t *extents)
{
cairo_status_t ret = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
#ifdef CAIRO_HAS_PNG_FUNCTIONS
ret = render_color_glyph_png (scaled_font, glyph, cr, extents);
if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
return ret;
#endif
ret = render_color_glyph_layers (scaled_font, glyph, cr, extents);
if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
return ret;
return render_glyph (scaled_font, glyph, cr, extents);
}
#endif
static inline cairo_font_face_t *
helper_cairo_create_user_font_face (const font_options_t *font_opts)
{
cairo_font_face_t *cairo_face = cairo_user_font_face_create ();
cairo_font_face_set_user_data (cairo_face,
&_hb_font_cairo_user_data_key,
hb_font_reference (font_opts->font),
(cairo_destroy_func_t) hb_font_destroy);
cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph);
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
hb_face_t *face = hb_font_get_face (font_opts->font);
if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face))
cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph);
#endif
return cairo_face;
}
static inline bool
helper_cairo_user_font_face_has_data (cairo_font_face_t *font_face)
{
return cairo_font_face_get_user_data (font_face, &_hb_font_cairo_user_data_key);
}
static inline bool
helper_cairo_user_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
/* Ignoring SVG for now, since we cannot render it. */
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&_hb_font_cairo_user_data_key));
hb_face_t *face = hb_font_get_face (font);
return hb_ot_color_has_png (face) || hb_ot_color_has_layers (face);
}
#endif
+656
View File
@@ -0,0 +1,656 @@
/*
* 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"
#include "helper-cairo-ft.hh"
#include "helper-cairo-user.hh"
#include <cairo.h>
#include <hb.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)
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)
{
bool use_hb_draw = helper_cairo_use_hb_draw (font_opts);
hb_font_t *font = hb_font_reference (font_opts->font);
cairo_font_face_t *cairo_face;
if (use_hb_draw)
cairo_face = helper_cairo_create_user_font_face (font_opts);
else
cairo_face = helper_cairo_create_ft_font_face (font_opts);
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);
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);
static cairo_user_data_key_t key;
if (cairo_scaled_font_set_user_data (scaled_font,
&key,
(void *) font,
(cairo_destroy_func_t) hb_font_destroy))
hb_font_destroy (font);
return scaled_font;
}
static inline bool
helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
if (helper_cairo_user_font_face_has_data (cairo_scaled_font_get_font_face (scaled_font)))
return helper_cairo_user_scaled_font_has_color (scaled_font);
else
return helper_cairo_ft_scaled_font_has_color (scaled_font);
}
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 = 0; ba = 255;
color = view_opts->back ? view_opts->back : DEFAULT_BACK;
sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
fr = fg = fb = 0; fa = 255;
color = view_opts->fore ? view_opts->fore : DEFAULT_FORE;
sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
if (content == CAIRO_CONTENT_ALPHA)
{
if (view_opts->annotate ||
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;
unsigned int num_glyphs;
char *utf8;
unsigned int utf8_len;
cairo_text_cluster_t *clusters;
unsigned int num_clusters;
cairo_text_cluster_flags_t cluster_flags;
void finish () {
if (glyphs)
cairo_glyph_free (glyphs);
if (clusters)
cairo_text_cluster_free (clusters);
if (utf8)
g_free (utf8);
}
void get_advance (double *x_advance, double *y_advance) {
*x_advance = glyphs[num_glyphs].x;
*y_advance = glyphs[num_glyphs].y;
}
};
static inline void
helper_cairo_line_from_buffer (helper_cairo_line_t *l,
hb_buffer_t *buffer,
const char *text,
unsigned int text_len,
int scale_bits,
hb_bool_t utf8_clusters)
{
memset (l, 0, sizeof (*l));
l->num_glyphs = hb_buffer_get_length (buffer);
hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr);
hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr);
l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
if (text) {
l->utf8 = g_strndup (text, text_len);
l->utf8_len = text_len;
l->num_clusters = l->num_glyphs ? 1 : 0;
for (unsigned int i = 1; i < l->num_glyphs; i++)
if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
l->num_clusters++;
l->clusters = cairo_text_cluster_allocate (l->num_clusters);
}
if ((l->num_glyphs && !l->glyphs) ||
(l->utf8_len && !l->utf8) ||
(l->num_clusters && !l->clusters))
{
l->finish ();
return;
}
hb_position_t x = 0, y = 0;
int i;
for (i = 0; i < (int) l->num_glyphs; i++)
{
l->glyphs[i].index = hb_glyph[i].codepoint;
l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits);
l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits);
x += hb_position->x_advance;
y += -hb_position->y_advance;
hb_position++;
}
l->glyphs[i].index = -1;
l->glyphs[i].x = scalbn ((double) x, scale_bits);
l->glyphs[i].y = scalbn ((double) y, scale_bits);
if (l->num_clusters) {
memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
unsigned int cluster = 0;
const char *start = l->utf8, *end;
l->clusters[cluster].num_glyphs++;
if (backward) {
for (i = l->num_glyphs - 2; i >= 0; i--) {
if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
if (utf8_clusters)
end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
else
end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster);
l->clusters[cluster].num_bytes = end - start;
start = end;
cluster++;
}
l->clusters[cluster].num_glyphs++;
}
l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
} else {
for (i = 1; i < (int) l->num_glyphs; i++) {
if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
if (utf8_clusters)
end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
else
end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster);
l->clusters[cluster].num_bytes = end - start;
start = end;
cluster++;
}
l->clusters[cluster].num_glyphs++;
}
l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
}
}
}
#endif
+104
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
+58
View File
@@ -0,0 +1,58 @@
hb_view_sources = [
'hb-view.cc',
]
hb_shape_sources = [
'hb-shape.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_FREETYPE', 0) == 1 and conf.get('HAVE_CAIRO_FT', 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],
install: true,
)
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,
)
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,
)
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,
)
else
# Disable tests that use this
hb_shape = disabler()
hb_subset = disabler()
endif
+244
View File
@@ -0,0 +1,244 @@
/*
* 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>
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for isatty() */
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
#include <io.h> /* for setmode() under Windows */
#endif
#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
#endif
+117
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
+101
View File
@@ -0,0 +1,101 @@
/*
* 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--)
{
const char *error = nullptr;
populate_buffer (buffer, text, text_len, app.text_before, app.text_after);
if (n == 1)
output.consume_text (buffer, text, text_len, utf8_clusters);
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;
}
}
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
+214
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, "%d: ", 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
+329
View File
@@ -0,0 +1,329 @@
/*
* 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_buffer_clear_contents (buffer);
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 (!hb_shape_full (font, buffer, features, num_features, shapers))
{
if (error)
*error = "Shaping failed.";
goto fail;
}
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;
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 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},
{"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},
{"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
+159
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 (out_fp, "%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
+370
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
+206
View File
@@ -0,0 +1,206 @@
/*
* 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));
scale_bits = - (int) 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;
helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale_bits, utf8_clusters);
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;
int scale_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, scale_bits);
font_extents.descent = -scalbn ((double) hb_extents.descender, scale_bits);
font_extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_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);
/* 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 (annotate) {
cairo_save (cr);
/* Draw actual glyph origins */
cairo_set_source_rgba (cr, 1., 0., 0., .5);
cairo_set_line_width (cr, 5);
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);
}
if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) {
/* cairo_show_glyphs() doesn't support subpixel positioning */
cairo_glyph_path (cr, l.glyphs, l.num_glyphs);
cairo_fill (cr);
} else 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
+123
View File
@@ -0,0 +1,123 @@
/*
* 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);
}
void add_options (option_parser_t *parser);
hb_bool_t annotate = false;
char *fore = nullptr;
char *back = 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};
};
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, 0, G_OPTION_ARG_NONE, &this->annotate, "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"},
{"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"},
{nullptr}
};
parser->add_group (entries,
"view",
"View options:",
"Options for output rendering",
this);
}
#endif