diff --git a/configure.ac b/configure.ac index 5baad1fc7..965a06cf8 100644 --- a/configure.ac +++ b/configure.ac @@ -235,6 +235,24 @@ AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft) dnl ========================================================================== +AC_ARG_WITH(fontconfig, + [AS_HELP_STRING([--with-fontconfig=@<:@yes/no/auto@:>@], + [Use fontconfig @<:@default=auto@:>@])],, + [with_fontconfig=auto]) +have_fontconfig=false +if test "x$with_fontconfig" = "xyes" -o "x$with_fontconfig" = "xauto"; then + PKG_CHECK_MODULES(FONTCONFIG, fontconfig, have_fontconfig=true, :) +fi +if test "x$with_fontconfig" = "xyes" -a "x$have_fontconfig" != "xtrue"; then + AC_MSG_ERROR([fontconfig support requested but not found]) +fi +if $have_fontconfig; then + AC_DEFINE(HAVE_FONTCONFIG, 1, [Have fontconfig library]) +fi +AM_CONDITIONAL(HAVE_FONTCONFIG, $have_fontconfig) + +dnl ========================================================================== + AC_ARG_WITH(icu, [AS_HELP_STRING([--with-icu=@<:@yes/no/auto@:>@], [Use ICU @<:@default=auto@:>@])],, @@ -438,6 +456,7 @@ Font callbacks (the more the better): Tools used for command-line utilities: Cairo: ${have_cairo} + Fontconfig: ${have_fontconfig} Additional shapers (the more the better): Graphite2: ${have_graphite2} diff --git a/src/hb-face.cc b/src/hb-face.cc index 9348af7bf..f38f04734 100644 --- a/src/hb-face.cc +++ b/src/hb-face.cc @@ -165,8 +165,8 @@ hb_face_create (hb_blob_t *blob, { hb_face_t *face; - if (unlikely (!blob || !hb_blob_get_length (blob))) - return hb_face_get_empty (); + if (unlikely (!blob)) + blob = hb_blob_get_empty (); hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer::sanitize (hb_blob_reference (blob)), index); diff --git a/src/hb-font.cc b/src/hb-font.cc index d42db5985..3df41bd38 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -854,8 +854,6 @@ hb_font_create (hb_face_t *face) if (unlikely (!face)) face = hb_face_get_empty (); - if (unlikely (hb_object_is_inert (face))) - return hb_font_get_empty (); if (!(font = hb_object_create ())) return hb_font_get_empty (); @@ -880,7 +878,7 @@ hb_font_t * hb_font_create_sub_font (hb_font_t *parent) { if (unlikely (!parent)) - return hb_font_get_empty (); + parent = hb_font_get_empty (); hb_font_t *font = hb_font_create (parent->face); diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc index 2166173f9..4ccf90ef1 100644 --- a/src/hb-shape-plan.cc +++ b/src/hb-shape-plan.cc @@ -126,7 +126,7 @@ hb_shape_plan_create (hb_face_t *face, if (unlikely (!face)) face = hb_face_get_empty (); - if (unlikely (!props || hb_object_is_inert (face))) + if (unlikely (!props)) return hb_shape_plan_get_empty (); if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t)))) return hb_shape_plan_get_empty (); @@ -294,7 +294,6 @@ hb_shape_plan_execute (hb_shape_plan_t *shape_plan, shape_plan->shaper_func); if (unlikely (hb_object_is_inert (shape_plan) || - hb_object_is_inert (font) || hb_object_is_inert (buffer))) return false; @@ -453,6 +452,10 @@ retry: hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list); + /* Don't add to the cache if face is inert. */ + if (unlikely (hb_object_is_inert (face))) + return shape_plan; + /* Don't add the plan to the cache if there were user features with non-global ranges */ if (hb_non_global_user_features_present (user_features, num_user_features)) diff --git a/test/api/test-font.c b/test/api/test-font.c index 6b6a5039f..2fc06313b 100644 --- a/test/api/test-font.c +++ b/test/api/test-font.c @@ -36,8 +36,8 @@ static void test_face_empty (void) { g_assert (hb_face_get_empty ()); - g_assert (hb_face_get_empty () == hb_face_create (hb_blob_get_empty (), 0)); - g_assert (hb_face_get_empty () == hb_face_create (NULL, 0)); + g_assert (hb_face_get_empty () != hb_face_create (hb_blob_get_empty (), 0)); + g_assert (hb_face_get_empty () != hb_face_create (NULL, 0)); g_assert (hb_face_reference_table (hb_face_get_empty (), HB_TAG ('h','e','a','d')) == hb_blob_get_empty ()); @@ -348,9 +348,9 @@ static void test_font_empty (void) { g_assert (hb_font_get_empty ()); - g_assert (hb_font_get_empty () == hb_font_create (hb_face_get_empty ())); - g_assert (hb_font_get_empty () == hb_font_create (NULL)); - g_assert (hb_font_get_empty () == hb_font_create_sub_font (NULL)); + g_assert (hb_font_get_empty () != hb_font_create (hb_face_get_empty ())); + g_assert (hb_font_get_empty () != hb_font_create (NULL)); + g_assert (hb_font_get_empty () != hb_font_create_sub_font (NULL)); g_assert (hb_font_is_immutable (hb_font_get_empty ())); g_assert (hb_font_get_face (hb_font_get_empty ()) == hb_face_get_empty ()); diff --git a/test/api/test-object.c b/test/api/test-object.c index 3afe6ae87..4ea6f7cbf 100644 --- a/test/api/test-object.c +++ b/test/api/test-object.c @@ -36,7 +36,7 @@ create_blob (void) return hb_blob_create (data, sizeof (data), HB_MEMORY_MODE_READONLY, NULL, NULL); } static void * -create_blob_inert (void) +create_blob_from_inert (void) { return hb_blob_create (NULL, 0, HB_MEMORY_MODE_DUPLICATE, NULL, NULL); } @@ -47,7 +47,7 @@ create_buffer (void) return hb_buffer_create (); } static void * -create_buffer_inert (void) +create_buffer_from_inert (void) { return NULL; } @@ -58,7 +58,7 @@ create_set (void) return hb_set_create (); } static void * -create_set_inert (void) +create_set_from_inert (void) { return NULL; } @@ -72,7 +72,7 @@ create_face (void) return face; } static void * -create_face_inert (void) +create_face_from_inert (void) { return hb_face_create (hb_blob_get_empty (), 0); } @@ -86,7 +86,7 @@ create_font (void) return font; } static void * -create_font_inert (void) +create_font_from_inert (void) { return hb_font_create (hb_face_get_empty ()); } @@ -97,7 +97,7 @@ create_font_funcs (void) return hb_font_funcs_create (); } static void * -create_font_funcs_inert (void) +create_font_funcs_from_inert (void) { return NULL; } @@ -108,9 +108,9 @@ create_unicode_funcs (void) return hb_unicode_funcs_create (NULL); } static void * -create_unicode_funcs_inert (void) +create_unicode_funcs_from_inert (void) { - return hb_unicode_funcs_get_default (); + return hb_unicode_funcs_create (hb_unicode_funcs_get_empty ()); } @@ -125,7 +125,7 @@ typedef hb_bool_t (*is_immutable_func_t) (void *obj); typedef struct { create_func_t create; - create_func_t create_inert; + create_func_t create_from_inert; create_func_t get_empty; reference_func_t reference; destroy_func_t destroy; @@ -139,7 +139,7 @@ typedef struct { #define OBJECT_WITHOUT_IMMUTABILITY(name) \ { \ (create_func_t) create_##name, \ - (create_func_t) create_##name##_inert, \ + (create_func_t) create_##name##_from_inert, \ (create_func_t) hb_##name##_get_empty, \ (reference_func_t) hb_##name##_reference, \ (destroy_func_t) hb_##name##_destroy, \ @@ -152,7 +152,7 @@ typedef struct { #define OBJECT_WITH_IMMUTABILITY(name) \ { \ (create_func_t) create_##name, \ - (create_func_t) create_##name##_inert, \ + (create_func_t) create_##name##_from_inert, \ (create_func_t) hb_##name##_get_empty, \ (reference_func_t) hb_##name##_reference, \ (destroy_func_t) hb_##name##_destroy, \ @@ -340,8 +340,8 @@ test_object (void) { data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; - g_test_message ("->create_inert()"); - obj = o->create_inert (); + g_test_message ("->create_from_inert()"); + obj = o->create_from_inert (); if (!obj) continue; if (obj == o->get_empty ()) @@ -351,10 +351,10 @@ test_object (void) o->destroy (obj); if (o->is_immutable) - g_assert (o->is_immutable (obj)); + g_assert (!o->is_immutable (obj)); - g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert (!o->get_user_data (obj, &key[0])); + g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); + g_assert (o->get_user_data (obj, &key[0])); o->destroy (obj); o->destroy (obj); @@ -362,7 +362,7 @@ test_object (void) o->destroy (obj); o->destroy (obj); - g_assert (!data[0].freed); + g_assert (data[0].freed); } } } diff --git a/util/Makefile.am b/util/Makefile.am index 3f23babf3..c179d7bb7 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -76,4 +76,19 @@ endif # HAVE_OT endif # HAVE_GLIB +if HAVE_OT +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 +endif # HAVE_OT + -include $(top_srcdir)/git.mk diff --git a/util/hb-fc-list.c b/util/hb-fc-list.c new file mode 100644 index 000000000..573d11e7c --- /dev/null +++ b/util/hb-fc-list.c @@ -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 +#include +#include +#include +#include +#ifdef HAVE_CONFIG_H +#include +#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 +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; +} diff --git a/util/hb-fc.cc b/util/hb-fc.cc new file mode 100644 index 000000000..e99b1aefc --- /dev/null +++ b/util/hb-fc.cc @@ -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 +#include + +#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 (void) +{ + 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, NULL, NULL); + + /* XXX MT-unsafe */ + if (fc_ffuncs) + hb_font_funcs_destroy (newfuncs); + else + fc_ffuncs = ffuncs = newfuncs; + } + + return const_cast (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", NULL}; + + 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, NULL, 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; +} diff --git a/util/hb-fc.h b/util/hb-fc.h new file mode 100644 index 000000000..bb2f78a82 --- /dev/null +++ b/util/hb-fc.h @@ -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 + +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 */