[util] Refactor hb-view completely
Now we can use the same code to do other utils...
This commit is contained in:
parent
bc4b07b05e
commit
b9b10ad78b
@ -49,6 +49,8 @@ m4_define([hb_libtool_current],
|
|||||||
HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age
|
HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age
|
||||||
AC_SUBST(HB_LIBTOOL_VERSION_INFO)
|
AC_SUBST(HB_LIBTOOL_VERSION_INFO)
|
||||||
|
|
||||||
|
GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
|
||||||
|
|
||||||
# Functions and headers
|
# Functions and headers
|
||||||
AC_CHECK_FUNCS(mprotect sysconf getpagesize mmap)
|
AC_CHECK_FUNCS(mprotect sysconf getpagesize mmap)
|
||||||
AC_CHECK_HEADERS(unistd.h sys/mman.h)
|
AC_CHECK_HEADERS(unistd.h sys/mman.h)
|
||||||
|
@ -18,6 +18,8 @@ hb_view_SOURCES = \
|
|||||||
common.hh \
|
common.hh \
|
||||||
options.cc \
|
options.cc \
|
||||||
options.hh \
|
options.hh \
|
||||||
|
view-cairo.cc \
|
||||||
|
view-cairo.hh \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
hb_view_CPPFLAGS = \
|
hb_view_CPPFLAGS = \
|
||||||
-I$(top_srcdir)/src/ \
|
-I$(top_srcdir)/src/ \
|
||||||
|
@ -27,14 +27,17 @@
|
|||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
|
|
||||||
void
|
void
|
||||||
fail (const char *format, ...)
|
fail (hb_bool_t suggest_help, const char *format, ...)
|
||||||
{
|
{
|
||||||
const char *msg;
|
const char *msg;
|
||||||
|
|
||||||
va_list vap;
|
va_list vap;
|
||||||
va_start (vap, format);
|
va_start (vap, format);
|
||||||
msg = g_strdup_vprintf (format, vap);
|
msg = g_strdup_vprintf (format, vap);
|
||||||
g_printerr ("%s: %s\n", g_get_prgname (), msg);
|
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);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
|
|
||||||
void fail (const char *format, ...) G_GNUC_NORETURN;
|
void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
250
util/hb-view.cc
250
util/hb-view.cc
@ -26,234 +26,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
|
|
||||||
|
|
||||||
#include <cairo-ft.h>
|
|
||||||
#include <hb-ft.h>
|
|
||||||
|
|
||||||
#include "options.hh"
|
#include "options.hh"
|
||||||
|
|
||||||
|
#include "view-cairo.hh"
|
||||||
/* Ugh, global vars. Ugly, but does the job */
|
|
||||||
static int width = 0;
|
|
||||||
static int height = 0;
|
|
||||||
static cairo_surface_t *surface = NULL;
|
|
||||||
static cairo_pattern_t *fore_pattern = NULL;
|
|
||||||
static cairo_pattern_t *back_pattern = NULL;
|
|
||||||
static cairo_font_face_t *cairo_face;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static cairo_glyph_t *
|
|
||||||
_hb_cr_text_glyphs (cairo_t *cr,
|
|
||||||
const char *utf8, int len,
|
|
||||||
unsigned int *pnum_glyphs)
|
|
||||||
{
|
|
||||||
cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
|
|
||||||
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
|
|
||||||
hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
|
|
||||||
hb_buffer_t *hb_buffer;
|
|
||||||
cairo_glyph_t *cairo_glyphs;
|
|
||||||
hb_glyph_info_t *hb_glyph;
|
|
||||||
hb_glyph_position_t *hb_position;
|
|
||||||
unsigned int num_glyphs, i;
|
|
||||||
hb_position_t x, y;
|
|
||||||
|
|
||||||
hb_buffer = hb_buffer_create ();
|
|
||||||
|
|
||||||
|
|
||||||
hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len);
|
|
||||||
|
|
||||||
if (!shape_opts->shape (hb_font, hb_buffer))
|
|
||||||
fail ("All shapers failed");
|
|
||||||
|
|
||||||
num_glyphs = hb_buffer_get_length (hb_buffer);
|
|
||||||
hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
|
|
||||||
hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
|
|
||||||
cairo_glyphs = cairo_glyph_allocate (num_glyphs);
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
for (i = 0; i < num_glyphs; i++)
|
|
||||||
{
|
|
||||||
cairo_glyphs[i].index = hb_glyph->codepoint;
|
|
||||||
cairo_glyphs[i].x = ( hb_position->x_offset + x) * (1./64);
|
|
||||||
cairo_glyphs[i].y = (-hb_position->y_offset + y) * (1./64);
|
|
||||||
x += hb_position->x_advance;
|
|
||||||
y += -hb_position->y_advance;
|
|
||||||
|
|
||||||
hb_glyph++;
|
|
||||||
hb_position++;
|
|
||||||
}
|
|
||||||
hb_buffer_destroy (hb_buffer);
|
|
||||||
hb_font_destroy (hb_font);
|
|
||||||
cairo_ft_scaled_font_unlock_face (scaled_font);
|
|
||||||
|
|
||||||
if (pnum_glyphs)
|
|
||||||
*pnum_glyphs = num_glyphs;
|
|
||||||
return cairo_glyphs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cairo_t *
|
|
||||||
create_context (void)
|
|
||||||
{
|
|
||||||
cairo_t *cr;
|
|
||||||
unsigned int fr, fg, fb, fa, br, bg, bb, ba;
|
|
||||||
|
|
||||||
if (surface)
|
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
if (back_pattern)
|
|
||||||
cairo_pattern_destroy (back_pattern);
|
|
||||||
if (fore_pattern)
|
|
||||||
cairo_pattern_destroy (fore_pattern);
|
|
||||||
|
|
||||||
br = bg = bb = ba = 255;
|
|
||||||
sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
|
|
||||||
fr = fg = fb = 0; fa = 255;
|
|
||||||
sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
|
|
||||||
|
|
||||||
if (!view_opts->annotate && ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
|
|
||||||
/* grayscale. use A8 surface */
|
|
||||||
surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
|
|
||||||
cr = cairo_create (surface);
|
|
||||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
||||||
cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
|
|
||||||
cairo_paint (cr);
|
|
||||||
back_pattern = cairo_pattern_reference (cairo_get_source (cr));
|
|
||||||
cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
|
|
||||||
fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
|
|
||||||
} else {
|
|
||||||
/* color. use (A)RGB surface */
|
|
||||||
if (ba != 255)
|
|
||||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
|
||||||
else
|
|
||||||
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
|
|
||||||
cr = cairo_create (surface);
|
|
||||||
cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
|
|
||||||
cairo_paint (cr);
|
|
||||||
back_pattern = cairo_pattern_reference (cairo_get_source (cr));
|
|
||||||
cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
|
|
||||||
fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
|
|
||||||
}
|
|
||||||
|
|
||||||
cairo_set_font_face (cr, cairo_face);
|
|
||||||
|
|
||||||
return cr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
draw (void)
|
|
||||||
{
|
|
||||||
cairo_t *cr;
|
|
||||||
cairo_font_extents_t font_extents;
|
|
||||||
|
|
||||||
cairo_glyph_t *glyphs = NULL;
|
|
||||||
unsigned int num_glyphs = 0;
|
|
||||||
|
|
||||||
const char *end, *p = text;
|
|
||||||
double x, y;
|
|
||||||
|
|
||||||
cr= create_context ();
|
|
||||||
|
|
||||||
cairo_set_font_size (cr, font_opts->font_size);
|
|
||||||
cairo_font_extents (cr, &font_extents);
|
|
||||||
|
|
||||||
height = 0;
|
|
||||||
width = 0;
|
|
||||||
|
|
||||||
x = view_opts->margin.l;
|
|
||||||
y = view_opts->margin.t;
|
|
||||||
|
|
||||||
do {
|
|
||||||
cairo_text_extents_t extents;
|
|
||||||
|
|
||||||
end = strchr (p, '\n');
|
|
||||||
if (!end)
|
|
||||||
end = p + strlen (p);
|
|
||||||
|
|
||||||
if (p != text)
|
|
||||||
y += view_opts->line_space;
|
|
||||||
|
|
||||||
if (p != end) {
|
|
||||||
glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
|
|
||||||
|
|
||||||
cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
|
|
||||||
|
|
||||||
y += ceil (font_extents.ascent);
|
|
||||||
width = MAX (width, extents.x_advance);
|
|
||||||
cairo_save (cr);
|
|
||||||
cairo_translate (cr, x, y);
|
|
||||||
if (view_opts->annotate) {
|
|
||||||
unsigned int i;
|
|
||||||
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 (i = 0; i < num_glyphs; i++) {
|
|
||||||
cairo_move_to (cr, glyphs[i].x, glyphs[i].y);
|
|
||||||
cairo_rel_line_to (cr, 0, 0);
|
|
||||||
}
|
|
||||||
cairo_stroke (cr);
|
|
||||||
|
|
||||||
cairo_restore (cr);
|
|
||||||
}
|
|
||||||
cairo_show_glyphs (cr, glyphs, num_glyphs);
|
|
||||||
cairo_restore (cr);
|
|
||||||
y += ceil (font_extents.height - ceil (font_extents.ascent));
|
|
||||||
|
|
||||||
cairo_glyph_free (glyphs);
|
|
||||||
}
|
|
||||||
|
|
||||||
p = end + 1;
|
|
||||||
} while (*end);
|
|
||||||
|
|
||||||
height = y + view_opts->margin.b;
|
|
||||||
width += view_opts->margin.l + view_opts->margin.r;
|
|
||||||
|
|
||||||
cairo_destroy (cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
static FT_Library ft_library;
|
|
||||||
static FT_Face ft_face;
|
|
||||||
cairo_status_t status;
|
|
||||||
|
|
||||||
setlocale (LC_ALL, "");
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
parse_options (argc, argv);
|
option_parser_t options ("[FONT-FILE] [TEXT]");
|
||||||
|
|
||||||
FT_Init_FreeType (&ft_library);
|
shape_options_t shaper (&options);
|
||||||
if (FT_New_Face (ft_library, font_opts->font_file, font_opts->face_index, &ft_face)) {
|
font_options_t font_opts (&options);
|
||||||
fprintf (stderr, "Failed to open font file `%s'\n", font_opts->font_file);
|
text_options_t input (&options);
|
||||||
exit (1);
|
|
||||||
|
view_cairo_t output (&options);
|
||||||
|
|
||||||
|
options.parse (&argc, &argv);
|
||||||
|
|
||||||
|
argc--, argv++;
|
||||||
|
if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++;
|
||||||
|
if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++;
|
||||||
|
if (argc)
|
||||||
|
fail (TRUE, "Too many arguments on the command line");
|
||||||
|
if (!font_opts.font_file || (!input.text && !input.text_file))
|
||||||
|
options.usage ();
|
||||||
|
|
||||||
|
output.init (&font_opts);
|
||||||
|
|
||||||
|
hb_buffer_t *buffer = hb_buffer_create ();
|
||||||
|
unsigned int text_len;
|
||||||
|
const char *text;
|
||||||
|
while ((text = input.get_line (&text_len)))
|
||||||
|
{
|
||||||
|
if (!shaper.shape (text, text_len,
|
||||||
|
font_opts.get_font (),
|
||||||
|
buffer))
|
||||||
|
fail (FALSE, "All shapers failed");
|
||||||
|
|
||||||
|
output.consume_line (buffer, text, text_len);
|
||||||
}
|
}
|
||||||
cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
|
hb_buffer_destroy (buffer);
|
||||||
|
|
||||||
draw ();
|
output.finish (&font_opts);
|
||||||
draw ();
|
|
||||||
|
|
||||||
status = cairo_surface_write_to_png (surface, out_file);
|
|
||||||
if (status != CAIRO_STATUS_SUCCESS) {
|
|
||||||
fprintf (stderr, "Failed to write output file `%s': %s\n",
|
|
||||||
out_file, cairo_status_to_string (status));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
cairo_pattern_destroy (fore_pattern);
|
|
||||||
cairo_pattern_destroy (back_pattern);
|
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
cairo_font_face_destroy (cairo_face);
|
|
||||||
cairo_debug_reset_static_data ();
|
|
||||||
|
|
||||||
FT_Done_Face (ft_face);
|
|
||||||
FT_Done_FreeType (ft_library);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
313
util/options.cc
313
util/options.cc
@ -26,14 +26,107 @@
|
|||||||
|
|
||||||
#include "options.hh"
|
#include "options.hh"
|
||||||
|
|
||||||
|
#if HAVE_FREETYPE
|
||||||
|
#include <hb-ft.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
view_options_t view_opts[1];
|
|
||||||
shape_options_t shape_opts[1];
|
|
||||||
font_options_t font_opts[1];
|
|
||||||
|
|
||||||
const char *text;
|
bool debug = FALSE;
|
||||||
const char *out_file = "/dev/stdout";
|
|
||||||
hb_bool_t debug = FALSE;
|
static gchar *
|
||||||
|
shapers_to_string (void)
|
||||||
|
{
|
||||||
|
GString *shapers = g_string_new (NULL);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
option_parser_t::add_main_options (void)
|
||||||
|
{
|
||||||
|
GOptionEntry entries[] =
|
||||||
|
{
|
||||||
|
{"version", 0, G_OPTION_FLAG_NO_ARG,
|
||||||
|
G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", NULL},
|
||||||
|
{"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", NULL},
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
g_option_context_add_main_entries (context, entries, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
pre_parse (GOptionContext *context G_GNUC_UNUSED,
|
||||||
|
GOptionGroup *group G_GNUC_UNUSED,
|
||||||
|
gpointer data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
option_group_t *option_group = (option_group_t *) data;
|
||||||
|
option_group->pre_parse (error);
|
||||||
|
return *error == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
post_parse (GOptionContext *context G_GNUC_UNUSED,
|
||||||
|
GOptionGroup *group G_GNUC_UNUSED,
|
||||||
|
gpointer data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
option_group_t *option_group = static_cast<option_group_t *>(data);
|
||||||
|
option_group->post_parse (error);
|
||||||
|
return *error == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
option_parser_t::add_group (GOptionEntry *entries,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *description,
|
||||||
|
const gchar *help_description,
|
||||||
|
option_group_t *option_group)
|
||||||
|
{
|
||||||
|
GOptionGroup *group = g_option_group_new (name, description, help_description,
|
||||||
|
static_cast<gpointer>(option_group), NULL);
|
||||||
|
g_option_group_add_entries (group, entries);
|
||||||
|
g_option_group_set_parse_hooks (group, pre_parse, post_parse);
|
||||||
|
g_option_context_add_group (context, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
option_parser_t::parse (int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
GError *parse_error = NULL;
|
||||||
|
if (!g_option_context_parse (context, argc, argv, &parse_error))
|
||||||
|
{
|
||||||
|
if (parse_error != NULL)
|
||||||
|
fail (TRUE, "%s", parse_error->message);
|
||||||
|
else
|
||||||
|
fail (TRUE, "Option parse error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -232,54 +325,8 @@ parse_features (const char *name G_GNUC_UNUSED,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
shapers_to_string (void)
|
|
||||||
{
|
|
||||||
GString *shapers = g_string_new (NULL);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
option_context_add_entries (GOptionContext *context,
|
|
||||||
GOptionEntry *entries,
|
|
||||||
const gchar *name,
|
|
||||||
const gchar *description,
|
|
||||||
const gchar *help_description,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
GOptionGroup *group = g_option_group_new (name, description, help_description, user_data, NULL);
|
|
||||||
g_option_group_add_entries (group, entries);
|
|
||||||
g_option_context_add_group (context, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
view_options_t::add_options (GOptionContext *context)
|
view_options_t::add_options (option_parser_t *parser)
|
||||||
{
|
{
|
||||||
GOptionEntry entries[] =
|
GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
@ -290,7 +337,7 @@ view_options_t::add_options (GOptionContext *context)
|
|||||||
{"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
|
{"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
option_context_add_entries (context, entries,
|
parser->add_group (entries,
|
||||||
"view",
|
"view",
|
||||||
"View options:",
|
"View options:",
|
||||||
"Options controlling the output rendering",
|
"Options controlling the output rendering",
|
||||||
@ -298,7 +345,7 @@ view_options_t::add_options (GOptionContext *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
shape_options_t::add_options (GOptionContext *context)
|
shape_options_t::add_options (option_parser_t *parser)
|
||||||
{
|
{
|
||||||
GOptionEntry entries[] =
|
GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
@ -309,7 +356,7 @@ shape_options_t::add_options (GOptionContext *context)
|
|||||||
{"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, "Font features to apply to text", "TODO"},
|
{"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, "Font features to apply to text", "TODO"},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
option_context_add_entries (context, entries,
|
parser->add_group (entries,
|
||||||
"shape",
|
"shape",
|
||||||
"Shape options:",
|
"Shape options:",
|
||||||
"Options controlling the shaping process",
|
"Options controlling the shaping process",
|
||||||
@ -317,57 +364,141 @@ shape_options_t::add_options (GOptionContext *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
font_options_t::add_options (GOptionContext *context)
|
font_options_t::add_options (option_parser_t *parser)
|
||||||
{
|
{
|
||||||
GOptionEntry entries[] =
|
GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
|
{"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Font file-name", "filename"},
|
||||||
{"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Face index (default: 0)", "index"},
|
{"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Face index (default: 0)", "index"},
|
||||||
{"font-size", 0, 0, G_OPTION_ARG_DOUBLE, &this->font_size, "Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
|
{"font-size", 0, 0, G_OPTION_ARG_DOUBLE, &this->font_size, "Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
option_context_add_entries (context, entries,
|
parser->add_group (entries,
|
||||||
"font",
|
"font",
|
||||||
"Font options:",
|
"Font options:",
|
||||||
"Options controlling the font",
|
"Options controlling the font",
|
||||||
NULL);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
parse_options (int argc, char *argv[])
|
text_options_t::add_options (option_parser_t *parser)
|
||||||
{
|
{
|
||||||
GOptionEntry entries[] =
|
GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
{"version", 0, G_OPTION_FLAG_NO_ARG,
|
{"text", 0, 0, G_OPTION_ARG_STRING, &this->text, "Set input text", "string"},
|
||||||
G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", NULL},
|
{"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name", "filename"},
|
||||||
{"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", NULL},
|
|
||||||
{"output", 0, 0, G_OPTION_ARG_STRING, &out_file, "Set output file name", "filename"},
|
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
GError *parse_error = NULL;
|
parser->add_group (entries,
|
||||||
GOptionContext *context;
|
"text",
|
||||||
|
"Text options:",
|
||||||
context = g_option_context_new ("- FONT-FILE TEXT");
|
"Options controlling the input text",
|
||||||
|
this);
|
||||||
g_option_context_add_main_entries (context, entries, NULL);
|
}
|
||||||
view_opts->add_options (context);
|
|
||||||
shape_opts->add_options (context);
|
void
|
||||||
font_opts->add_options (context);
|
output_options_t::add_options (option_parser_t *parser)
|
||||||
|
{
|
||||||
if (!g_option_context_parse (context, &argc, &argv, &parse_error))
|
GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
if (parse_error != NULL)
|
{"output", 0, 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"},
|
||||||
fail ("%s", parse_error->message);
|
{"format", 0, 0, G_OPTION_ARG_STRING, &this->output_format, "Set output format", "format"},
|
||||||
else
|
{NULL}
|
||||||
fail ("Option parse error");
|
};
|
||||||
exit(1);
|
parser->add_group (entries,
|
||||||
}
|
"output",
|
||||||
g_option_context_free(context);
|
"Output options:",
|
||||||
|
"Options controlling the output",
|
||||||
if (argc != 3) {
|
this);
|
||||||
g_printerr ("Usage: %s [OPTION...] FONT-FILE TEXT\n", g_get_prgname ());
|
}
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
font_opts->font_file = argv[1];
|
hb_font_t *
|
||||||
text = argv[2];
|
font_options_t::get_font (void) const
|
||||||
|
{
|
||||||
|
if (font)
|
||||||
|
return font;
|
||||||
|
|
||||||
|
hb_blob_t *blob = NULL;
|
||||||
|
|
||||||
|
/* Create the blob */
|
||||||
|
{
|
||||||
|
const char *font_data;
|
||||||
|
unsigned int len;
|
||||||
|
hb_destroy_func_t destroy;
|
||||||
|
void *user_data;
|
||||||
|
hb_memory_mode_t mm;
|
||||||
|
|
||||||
|
if (!font_file)
|
||||||
|
fail (TRUE, "No font file set");
|
||||||
|
|
||||||
|
GMappedFile *mf = g_mapped_file_new (font_file, FALSE, NULL);
|
||||||
|
if (!mf)
|
||||||
|
fail (FALSE, "Failed opening font file `%s'", font_file);
|
||||||
|
font_data = g_mapped_file_get_contents (mf);
|
||||||
|
len = g_mapped_file_get_length (mf);
|
||||||
|
destroy = (hb_destroy_func_t) g_mapped_file_unref;
|
||||||
|
user_data = (void *) mf;
|
||||||
|
mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
|
||||||
|
|
||||||
|
blob = hb_blob_create (font_data, len, mm, user_data, destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the face */
|
||||||
|
hb_face_t *face = hb_face_create (blob, face_index);
|
||||||
|
hb_blob_destroy (blob);
|
||||||
|
|
||||||
|
|
||||||
|
font = hb_font_create (face);
|
||||||
|
|
||||||
|
unsigned int upem = hb_face_get_upem (face);
|
||||||
|
hb_font_set_scale (font, font_size * upem, font_size * upem);
|
||||||
|
hb_face_destroy (face);
|
||||||
|
|
||||||
|
#if HAVE_FREETYPE
|
||||||
|
hb_ft_font_set_funcs (font);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
text_options_t::get_line (unsigned int *len)
|
||||||
|
{
|
||||||
|
if (!text) {
|
||||||
|
if (!text_file)
|
||||||
|
fail (TRUE, "At least one of text or text-file must be set");
|
||||||
|
|
||||||
|
GMappedFile *mf = g_mapped_file_new (text_file, FALSE, NULL);
|
||||||
|
if (!mf)
|
||||||
|
fail (FALSE, "Failed opening text file `%s'", text_file);
|
||||||
|
text = g_mapped_file_get_contents (mf);
|
||||||
|
text_len = g_mapped_file_get_length (mf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text_len == (unsigned int) -1)
|
||||||
|
text_len = strlen (text);
|
||||||
|
|
||||||
|
if (!text_len) {
|
||||||
|
*len = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ret = text;
|
||||||
|
const char *p = (const char *) memchr (text, '\n', text_len);
|
||||||
|
unsigned int ret_len;
|
||||||
|
if (!p) {
|
||||||
|
ret_len = text_len;
|
||||||
|
text += ret_len;
|
||||||
|
text_len = 0;
|
||||||
|
} else {
|
||||||
|
ret_len = p - ret;
|
||||||
|
text += ret_len + 1;
|
||||||
|
text_len -= ret_len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = ret_len;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
179
util/options.hh
179
util/options.hh
@ -30,42 +30,94 @@
|
|||||||
#define OPTIONS_HH
|
#define OPTIONS_HH
|
||||||
|
|
||||||
|
|
||||||
|
extern bool debug;
|
||||||
|
|
||||||
|
struct option_group_t
|
||||||
|
{
|
||||||
|
virtual void add_options (struct option_parser_t *parser) = 0;
|
||||||
|
|
||||||
|
virtual void pre_parse (GError **error G_GNUC_UNUSED) {};
|
||||||
|
virtual void post_parse (GError **error G_GNUC_UNUSED) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct option_parser_t
|
||||||
|
{
|
||||||
|
option_parser_t (const char *usage) {
|
||||||
|
memset (this, 0, sizeof (*this));
|
||||||
|
usage_str = usage;
|
||||||
|
context = g_option_context_new (usage);
|
||||||
|
|
||||||
|
add_main_options ();
|
||||||
|
}
|
||||||
|
~option_parser_t (void) {
|
||||||
|
g_option_context_free (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_main_options (void);
|
||||||
|
|
||||||
|
void add_group (GOptionEntry *entries,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *description,
|
||||||
|
const gchar *help_description,
|
||||||
|
option_group_t *option_group);
|
||||||
|
|
||||||
|
void parse (int *argc, char ***argv);
|
||||||
|
|
||||||
|
G_GNUC_NORETURN void usage (void) {
|
||||||
|
g_printerr ("Usage: %s [OPTION...] %s\n", g_get_prgname (), usage_str);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *usage_str;
|
||||||
|
GOptionContext *context;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_MARGIN 18
|
#define DEFAULT_MARGIN 18
|
||||||
#define DEFAULT_FORE "#000000"
|
#define DEFAULT_FORE "#000000"
|
||||||
#define DEFAULT_BACK "#FFFFFF"
|
#define DEFAULT_BACK "#FFFFFF"
|
||||||
|
|
||||||
extern struct view_options_t
|
struct view_options_t : option_group_t
|
||||||
{
|
{
|
||||||
view_options_t (void) {
|
view_options_t (option_parser_t *parser) {
|
||||||
memset (this, 0, sizeof (*this));
|
annotate = false;
|
||||||
fore = DEFAULT_FORE;
|
fore = DEFAULT_FORE;
|
||||||
back = DEFAULT_BACK;
|
back = DEFAULT_BACK;
|
||||||
|
line_space = 0;
|
||||||
margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
|
margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
|
||||||
|
|
||||||
|
add_options (parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_options (GOptionContext *context);
|
void add_options (option_parser_t *parser);
|
||||||
|
|
||||||
hb_bool_t annotate;
|
bool annotate;
|
||||||
const char *fore;
|
const char *fore;
|
||||||
const char *back;
|
const char *back;
|
||||||
double line_space;
|
double line_space;
|
||||||
struct margin_t {
|
struct margin_t {
|
||||||
double t, r, b, l;
|
double t, r, b, l;
|
||||||
} margin;
|
} margin;
|
||||||
} view_opts[1];
|
};
|
||||||
|
|
||||||
|
|
||||||
extern struct shape_options_t
|
struct shape_options_t : option_group_t
|
||||||
{
|
{
|
||||||
shape_options_t (void) {
|
shape_options_t (option_parser_t *parser) {
|
||||||
memset (this, 0, sizeof (*this));
|
direction = language = script = NULL;
|
||||||
|
features = NULL;
|
||||||
|
num_features = 0;
|
||||||
|
shapers = NULL;
|
||||||
|
|
||||||
|
add_options (parser);
|
||||||
}
|
}
|
||||||
~shape_options_t (void) {
|
~shape_options_t (void) {
|
||||||
free (features);
|
free (features);
|
||||||
g_free (shapers);
|
g_free (shapers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_options (GOptionContext *context);
|
void add_options (option_parser_t *parser);
|
||||||
|
|
||||||
void setup_buffer (hb_buffer_t *buffer) {
|
void setup_buffer (hb_buffer_t *buffer) {
|
||||||
hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
|
hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
|
||||||
@ -73,7 +125,10 @@ extern struct shape_options_t
|
|||||||
hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
|
hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shape (hb_font_t *font, hb_buffer_t *buffer) {
|
bool shape (const char *text, int text_len,
|
||||||
|
hb_font_t *font, hb_buffer_t *buffer) {
|
||||||
|
hb_buffer_reset (buffer);
|
||||||
|
hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
|
||||||
setup_buffer (buffer);
|
setup_buffer (buffer);
|
||||||
return hb_shape_full (font, buffer, features, num_features, NULL, shapers);
|
return hb_shape_full (font, buffer, features, num_features, NULL, shapers);
|
||||||
}
|
}
|
||||||
@ -84,31 +139,113 @@ extern struct shape_options_t
|
|||||||
hb_feature_t *features;
|
hb_feature_t *features;
|
||||||
unsigned int num_features;
|
unsigned int num_features;
|
||||||
char **shapers;
|
char **shapers;
|
||||||
} shape_opts[1];
|
};
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_FONT_SIZE 36
|
#define DEFAULT_FONT_SIZE 36
|
||||||
|
|
||||||
extern struct font_options_t
|
struct font_options_t : option_group_t
|
||||||
{
|
{
|
||||||
font_options_t (void) {
|
font_options_t (option_parser_t *parser) {
|
||||||
memset (this, 0, sizeof (*this));
|
font_file = NULL;
|
||||||
|
face_index = 0;
|
||||||
font_size = DEFAULT_FONT_SIZE;
|
font_size = DEFAULT_FONT_SIZE;
|
||||||
|
|
||||||
|
font = NULL;
|
||||||
|
|
||||||
|
add_options (parser);
|
||||||
|
}
|
||||||
|
~font_options_t (void) {
|
||||||
|
hb_font_destroy (font);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_options (GOptionContext *context);
|
void add_options (option_parser_t *parser);
|
||||||
|
|
||||||
|
hb_font_t *get_font (void) const;
|
||||||
|
|
||||||
const char *font_file;
|
const char *font_file;
|
||||||
int face_index;
|
int face_index;
|
||||||
double font_size;
|
double font_size;
|
||||||
} font_opts[1];
|
|
||||||
|
private:
|
||||||
|
mutable hb_font_t *font;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
extern const char *text;
|
struct text_options_t : option_group_t
|
||||||
extern const char *out_file;
|
{
|
||||||
extern hb_bool_t debug;
|
text_options_t (option_parser_t *parser) {
|
||||||
|
text = NULL;
|
||||||
|
text_file = NULL;
|
||||||
|
|
||||||
void parse_options (int argc, char *argv[]);
|
file = NULL;
|
||||||
|
text_len = (unsigned int) -1;
|
||||||
|
|
||||||
|
add_options (parser);
|
||||||
|
}
|
||||||
|
~text_options_t (void) {
|
||||||
|
if (file)
|
||||||
|
g_mapped_file_unref (file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_options (option_parser_t *parser);
|
||||||
|
|
||||||
|
void post_parse (GError **error G_GNUC_UNUSED) {
|
||||||
|
if (text && text_file)
|
||||||
|
g_set_error (error,
|
||||||
|
G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||||||
|
"Only one of text and text-file must be set");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *get_line (unsigned int *len);
|
||||||
|
|
||||||
|
const char *text;
|
||||||
|
const char *text_file;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable GMappedFile *file;
|
||||||
|
mutable unsigned int text_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct output_options_t : option_group_t
|
||||||
|
{
|
||||||
|
output_options_t (option_parser_t *parser) {
|
||||||
|
output_file = NULL;
|
||||||
|
output_format = NULL;
|
||||||
|
|
||||||
|
add_options (parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_options (option_parser_t *parser);
|
||||||
|
|
||||||
|
void post_parse (GError **error G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
if (output_file && !output_format) {
|
||||||
|
output_format = strrchr (output_file, '.');
|
||||||
|
if (output_format)
|
||||||
|
output_format++; /* skip the dot */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!output_file) {
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
output_file = "CON"; /* XXX right? */
|
||||||
|
#else
|
||||||
|
output_file = "/dev/stdout";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void init (const font_options_t *font_opts) = 0;
|
||||||
|
virtual void consume_line (hb_buffer_t *buffer,
|
||||||
|
const char *text,
|
||||||
|
unsigned int text_len) = 0;
|
||||||
|
virtual void finish (const font_options_t *font_opts) = 0;
|
||||||
|
|
||||||
|
const char *output_file;
|
||||||
|
const char *output_format;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
440
util/view-cairo.cc
Normal file
440
util/view-cairo.cc
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "view-cairo.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 (const char *filename,
|
||||||
|
double width,
|
||||||
|
double height)
|
||||||
|
{
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
|
||||||
|
surface = cairo_ps_surface_create (filename, width, height);
|
||||||
|
cairo_ps_surface_set_eps (surface, TRUE);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
# else
|
||||||
|
# undef HAS_EPS
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct 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 (void) {
|
||||||
|
if (glyphs)
|
||||||
|
cairo_glyph_free (glyphs);
|
||||||
|
if (clusters)
|
||||||
|
cairo_text_cluster_free (clusters);
|
||||||
|
if (utf8)
|
||||||
|
g_free (utf8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::init (const font_options_t *font_opts)
|
||||||
|
{
|
||||||
|
lines = g_array_new (FALSE, FALSE, sizeof (line_t));
|
||||||
|
upem = hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::consume_line (hb_buffer_t *buffer,
|
||||||
|
const char *text,
|
||||||
|
unsigned int text_len)
|
||||||
|
{
|
||||||
|
line_t l = {0};
|
||||||
|
|
||||||
|
l.num_glyphs = hb_buffer_get_length (buffer);
|
||||||
|
hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
|
||||||
|
hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
|
||||||
|
l.glyphs = cairo_glyph_allocate (l.num_glyphs + 1);
|
||||||
|
l.utf8 = g_strndup (text, text_len);
|
||||||
|
l.utf8_len = text_len;
|
||||||
|
l.num_clusters = 1;
|
||||||
|
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 = ( hb_position->x_offset + x) / double (upem);
|
||||||
|
l.glyphs[i].y = (-hb_position->y_offset + y) / double (upem);
|
||||||
|
x += hb_position->x_advance;
|
||||||
|
y += -hb_position->y_advance;
|
||||||
|
|
||||||
|
hb_position++;
|
||||||
|
}
|
||||||
|
l.glyphs[i].index = 0;
|
||||||
|
l.glyphs[i].x = x;
|
||||||
|
l.glyphs[i].y = y;
|
||||||
|
|
||||||
|
memset ((void *) l.clusters, 0, l.num_clusters * sizeof (l.clusters[0]));
|
||||||
|
bool 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;
|
||||||
|
g_assert (l.num_glyphs);
|
||||||
|
unsigned int cluster = 0;
|
||||||
|
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);
|
||||||
|
l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
|
||||||
|
cluster++;
|
||||||
|
}
|
||||||
|
l.clusters[cluster].num_glyphs++;
|
||||||
|
}
|
||||||
|
l.clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
|
||||||
|
} 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);
|
||||||
|
l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
|
||||||
|
cluster++;
|
||||||
|
}
|
||||||
|
l.clusters[cluster].num_glyphs++;
|
||||||
|
}
|
||||||
|
l.clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_append_val (lines, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::finish (const font_options_t *font_opts)
|
||||||
|
{
|
||||||
|
render (font_opts);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < lines->len; i++) {
|
||||||
|
line_t &line = g_array_index (lines, line_t, i);
|
||||||
|
line.finish ();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_unref (lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
view_cairo_t::line_width (unsigned int i)
|
||||||
|
{
|
||||||
|
line_t &line = g_array_index (lines, line_t, i);
|
||||||
|
return line.glyphs[line.num_glyphs].x / double (upem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font,
|
||||||
|
double *w, double *h)
|
||||||
|
{
|
||||||
|
cairo_font_extents_t font_extents;
|
||||||
|
|
||||||
|
cairo_scaled_font_extents (scaled_font, &font_extents);
|
||||||
|
|
||||||
|
*h = font_extents.ascent + font_extents.descent + ((int) lines->len - 1) * font_extents.height;
|
||||||
|
*w = 0;
|
||||||
|
for (unsigned int i = 0; i < lines->len; i++)
|
||||||
|
*w = MAX (*w, line_width (i));
|
||||||
|
|
||||||
|
*w += margin.l + margin.r;
|
||||||
|
*h += margin.t + margin.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_scaled_font_t *
|
||||||
|
view_cairo_t::create_scaled_font (const font_options_t *font_opts)
|
||||||
|
{
|
||||||
|
hb_font_t *font = hb_font_reference (font_opts->get_font ());
|
||||||
|
|
||||||
|
cairo_font_face_t *cairo_face = cairo_ft_font_face_create_for_ft_face (hb_ft_font_get_face (font), 0);
|
||||||
|
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, font_opts->font_size);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct finalize_closure_t {
|
||||||
|
void (*callback)(finalize_closure_t *);
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
const char *filename;
|
||||||
|
};
|
||||||
|
static cairo_user_data_key_t finalize_closure_key;
|
||||||
|
|
||||||
|
#ifdef CAIRO_HAS_PNG_FUNCTIONS
|
||||||
|
|
||||||
|
static void
|
||||||
|
finalize_png (finalize_closure_t *closure)
|
||||||
|
{
|
||||||
|
cairo_status_t status;
|
||||||
|
status = cairo_surface_write_to_png (closure->surface, closure->filename);
|
||||||
|
if (status != CAIRO_STATUS_SUCCESS)
|
||||||
|
fail (FALSE, "Failed to write output to `%s': %s",
|
||||||
|
closure->filename, cairo_status_to_string (status));
|
||||||
|
}
|
||||||
|
|
||||||
|
static cairo_surface_t *
|
||||||
|
_cairo_png_surface_create (const char *filename,
|
||||||
|
double width,
|
||||||
|
double height,
|
||||||
|
cairo_content_t content)
|
||||||
|
{
|
||||||
|
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 *closure = g_new0 (finalize_closure_t, 1);
|
||||||
|
closure->callback = finalize_png;
|
||||||
|
closure->surface = surface;
|
||||||
|
closure->filename = filename;
|
||||||
|
|
||||||
|
if (cairo_surface_set_user_data (surface, &finalize_closure_key, (void *) closure, (cairo_destroy_func_t) g_free))
|
||||||
|
g_free ((void *) closure);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::render (const font_options_t *font_opts)
|
||||||
|
{
|
||||||
|
cairo_scaled_font_t *scaled_font = create_scaled_font (font_opts);
|
||||||
|
double w, h;
|
||||||
|
get_surface_size (scaled_font, &w, &h);
|
||||||
|
cairo_t *cr = create_context (w, h);
|
||||||
|
cairo_set_scaled_font (cr, scaled_font);
|
||||||
|
cairo_scaled_font_destroy (scaled_font);
|
||||||
|
|
||||||
|
draw (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_t *
|
||||||
|
view_cairo_t::create_context (double w, double h)
|
||||||
|
{
|
||||||
|
cairo_surface_t *(*constructor) (const char *filename,
|
||||||
|
double width,
|
||||||
|
double height) = NULL;
|
||||||
|
cairo_surface_t *(*constructor2) (const char *filename,
|
||||||
|
double width,
|
||||||
|
double height,
|
||||||
|
cairo_content_t content) = NULL;
|
||||||
|
|
||||||
|
const char *extension = output_format;
|
||||||
|
if (!extension)
|
||||||
|
extension = "png";
|
||||||
|
if (0)
|
||||||
|
;
|
||||||
|
#ifdef CAIRO_HAS_PNG_FUNCTIONS
|
||||||
|
else if (0 == strcasecmp (extension, "png"))
|
||||||
|
constructor2 = _cairo_png_surface_create;
|
||||||
|
#endif
|
||||||
|
#ifdef CAIRO_HAS_SVG_SURFACE
|
||||||
|
else if (0 == strcasecmp (extension, "svg"))
|
||||||
|
constructor = cairo_svg_surface_create;
|
||||||
|
#endif
|
||||||
|
#ifdef CAIRO_HAS_PDF_SURFACE
|
||||||
|
else if (0 == strcasecmp (extension, "pdf"))
|
||||||
|
constructor = cairo_pdf_surface_create;
|
||||||
|
#endif
|
||||||
|
#ifdef CAIRO_HAS_PS_SURFACE
|
||||||
|
else if (0 == strcasecmp (extension, "ps"))
|
||||||
|
constructor = cairo_ps_surface_create;
|
||||||
|
#ifdef HAS_EPS
|
||||||
|
else if (0 == strcasecmp (extension, "eps"))
|
||||||
|
constructor = _cairo_eps_surface_create;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int fr, fg, fb, fa, br, bg, bb, ba;
|
||||||
|
br = bg = bb = ba = 255;
|
||||||
|
sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
|
||||||
|
fr = fg = fb = 0; fa = 255;
|
||||||
|
sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
|
||||||
|
|
||||||
|
cairo_content_t content;
|
||||||
|
if (!annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
|
||||||
|
content = CAIRO_CONTENT_ALPHA;
|
||||||
|
else if (ba == 255)
|
||||||
|
content = CAIRO_CONTENT_COLOR;
|
||||||
|
else
|
||||||
|
content = CAIRO_CONTENT_COLOR_ALPHA;
|
||||||
|
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
if (constructor)
|
||||||
|
surface = constructor (output_file, w, h);
|
||||||
|
else if (constructor2)
|
||||||
|
surface = constructor2 (output_file, w, h, content);
|
||||||
|
else
|
||||||
|
fail (FALSE, "Unknown output format `%s'", extension);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_cairo_t::draw (cairo_t *cr)
|
||||||
|
{
|
||||||
|
cairo_save (cr);
|
||||||
|
|
||||||
|
cairo_font_extents_t font_extents;
|
||||||
|
cairo_font_extents (cr, &font_extents);
|
||||||
|
cairo_translate (cr, margin.l, margin.t);
|
||||||
|
for (unsigned int i = 0; i < lines->len; i++)
|
||||||
|
{
|
||||||
|
line_t &l = g_array_index (lines, line_t, i);
|
||||||
|
|
||||||
|
if (i)
|
||||||
|
cairo_translate (cr, 0, line_space);
|
||||||
|
|
||||||
|
cairo_translate (cr, 0, font_extents.ascent);
|
||||||
|
|
||||||
|
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 (cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) {
|
||||||
|
/* cairo_show_glyphs() doesn't support subpixel positioining */
|
||||||
|
cairo_glyph_path (cr, l.glyphs, l.num_glyphs);
|
||||||
|
cairo_fill (cr);
|
||||||
|
} else
|
||||||
|
cairo_show_text_glyphs (cr,
|
||||||
|
l.utf8, l.utf8_len,
|
||||||
|
l.glyphs, l.num_glyphs,
|
||||||
|
l.clusters, l.num_clusters,
|
||||||
|
l.cluster_flags);
|
||||||
|
|
||||||
|
cairo_translate (cr, 0, font_extents.height - font_extents.ascent);
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_restore (cr);
|
||||||
|
}
|
63
util/view-cairo.hh
Normal file
63
util/view-cairo.hh
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "options.hh"
|
||||||
|
|
||||||
|
#include <cairo-ft.h>
|
||||||
|
#include <hb-ft.h>
|
||||||
|
|
||||||
|
#ifndef VIEW_CAIRO_HH
|
||||||
|
#define VIEW_CAIRO_HH
|
||||||
|
|
||||||
|
struct view_cairo_t : output_options_t, view_options_t {
|
||||||
|
view_cairo_t (option_parser_t *parser)
|
||||||
|
: output_options_t (parser),
|
||||||
|
view_options_t (parser) {}
|
||||||
|
~view_cairo_t (void) {
|
||||||
|
if (debug)
|
||||||
|
cairo_debug_reset_static_data ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init (const font_options_t *font_opts);
|
||||||
|
void consume_line (hb_buffer_t *buffer,
|
||||||
|
const char *text,
|
||||||
|
unsigned int text_len);
|
||||||
|
void finish (const font_options_t *font_opts);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void render (const font_options_t *font_opts);
|
||||||
|
cairo_scaled_font_t *create_scaled_font (const font_options_t *font_opts);
|
||||||
|
void get_surface_size (cairo_scaled_font_t *scaled_font, double *w, double *h);
|
||||||
|
cairo_t *create_context (double w, double h);
|
||||||
|
void draw (cairo_t *cr);
|
||||||
|
double line_width (unsigned int i);
|
||||||
|
|
||||||
|
GArray *lines;
|
||||||
|
unsigned int upem;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user