diff options
54 files changed, 1147 insertions, 286 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d4a265f..dffa8056 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ stages: - build + - analysis - docs - deploy @@ -9,8 +10,9 @@ variables: MESON_TEST_TIMEOUT_MULTIPLIER: 2 linux-fedora: - image: registry.gitlab.gnome.org/gnome/pango/fedora:v1 + image: registry.gitlab.gnome.org/gnome/pango/fedora:v2 stage: build + needs: [] variables: EXTRA_MESON_FLAGS: "--buildtype=debug --default-library=both" script: @@ -31,8 +33,32 @@ linux-fedora: - "${CI_PROJECT_DIR}/_build/hello.png" - "${CI_PROJECT_DIR}/_build/fontlist.txt" +asan-build: + image: registry.gitlab.gnome.org/gnome/pango/fedora:v2 + tags: [ asan ] + stage: analysis + needs: [] + variables: + script: + - CC=clang meson --buildtype=debugoptimized -Db_sanitize=address -Db_lundef=false -Dintrospection=false _build + - ninja -C _build + - .gitlab-ci/run-tests.sh _build + allow_failure: true + artifacts: + when: always + reports: + junit: + - "${CI_PROJECT_DIR}/_build/report.xml" + name: "gtk-${CI_COMMIT_REF_NAME}" + paths: + - "${CI_PROJECT_DIR}/_build/meson-logs" + - "${CI_PROJECT_DIR}/_build/report.xml" + - "${CI_PROJECT_DIR}/_build/hello.png" + - "${CI_PROJECT_DIR}/_build/fontlist.txt" + msys2-mingw64: stage: build + needs: [] tags: - win32-ps variables: @@ -54,6 +80,7 @@ msys2-mingw64: reference: image: registry.gitlab.gnome.org/gnome/pango/fedora:v1 stage: docs + needs: [] variables: EXTRA_MESON_FLAGS: "" script: @@ -67,6 +94,7 @@ reference: pages: stage: deploy + needs: ['reference'] script: - mv _reference/ public/ artifacts: diff --git a/.gitlab-ci/fedora.Dockerfile b/.gitlab-ci/fedora.Dockerfile index e7ff2feb..f2240cac 100644 --- a/.gitlab-ci/fedora.Dockerfile +++ b/.gitlab-ci/fedora.Dockerfile @@ -26,8 +26,10 @@ RUN dnf -y install \ harfbuzz-devel \ hicolor-icon-theme \ itstool \ + libasan \ lcov \ libthai-devel \ + libubsan \ libXft-devel \ ninja-build \ python3 \ diff --git a/.gitlab-ci/run-tests.sh b/.gitlab-ci/run-tests.sh index cca589b4..e5c8e9af 100755 --- a/.gitlab-ci/run-tests.sh +++ b/.gitlab-ci/run-tests.sh @@ -6,6 +6,9 @@ set +e srcdir=$( pwd ) builddir=$1 +# Ignore memory leaks lower in dependencies +export LSAN_OPTIONS=suppressions=$srcdir/lsan.supp + meson test -C ${builddir} \ --print-errorlogs \ --suite=pango @@ -1,3 +1,40 @@ +Overview of changes in 1.46.2 +============================= +- Fix pango_win32_font_map_load_font with falback families +- Fix an assertion in pango_language_get_scripts +- Fix a crash in get_items_log_attrs +- Fix attribute iterators with overlapping attributes +- Fix rendering of Emoji keycap sequences +- ci: Run the testsuite under asan and fix all reported issues +- build: Make libthai, cairo, xft, fontconfig, freetype + dependencies meson features + +Overview of changes in 1.46.1 +============================= +- Revert an unintentional PangoRenderer abi break in 1.46.0 +- Various small fixes + +Overview of changes in 1.46.0 +============================= +- Bump version to 1.46 + +Overview of changes in 1.45.5 +============================= +- Export pango_color_parse_with_alpha +- Stop using hb-glib + +Overview of changes in 1.45.4 +============================= +- Fix build on Windows +- Fix a pidgin crash +- fc: Always reject unsupported font formats +- coretext: Fix cairo scaling + +Overview of changes in 1.45.3 +============================= +- Fix pango_attr_list_change +- Fix crashes with empty attribute lists + Overview of changes in 1.45.2 ============================= - Fix several crashes in gtk2 applications diff --git a/docs/pango-sections.txt b/docs/pango-sections.txt index 81a83b81..3d45510d 100644 --- a/docs/pango-sections.txt +++ b/docs/pango-sections.txt @@ -423,6 +423,7 @@ PangoShowFlags pango_attr_show_new PangoColor pango_color_parse +pango_color_parse_with_alpha pango_color_copy pango_color_free pango_color_to_string @@ -643,6 +644,7 @@ pango_language_matches pango_language_includes_script pango_language_get_scripts pango_language_get_default +pango_language_get_preferred pango_language_get_sample_string <SUBSECTION Private> @@ -1012,6 +1014,7 @@ pango_fc_font_has_char pango_fc_font_get_glyph pango_fc_font_get_unknown_glyph pango_fc_font_kern_glyphs +pango_fc_font_get_languages <SUBSECTION Standard> PANGO_FC_FONT PANGO_IS_FC_FONT diff --git a/lsan.supp b/lsan.supp new file mode 100644 index 00000000..06a74df1 --- /dev/null +++ b/lsan.supp @@ -0,0 +1,7 @@ +leak:g_quark_init +leak:libc.so +leak:libfontconfig.so +leak:libcairo.so +leak:libpixman-1.so +leak:libthai.so +leak:libdatrie.so diff --git a/make-release.sh b/make-release.sh new file mode 100755 index 00000000..9d5a6607 --- /dev/null +++ b/make-release.sh @@ -0,0 +1,28 @@ +#! /bin/sh + +version=$(head -5 meson.build | grep version | sed -e "s/[^']*'//" -e "s/'.*$//") +release_build_dir="release_build" +branch=$(git branch --show-current) + +if [ -d ${release_build_dir} ]; then + echo "Please remove ./${release_build_dir} first" + exit 1 +fi + +# we include gtk-doc since we need the gtk-doc-for-gtk4 branch +meson setup --force-fallback-for gtk-doc ${release_build_dir} || exit + +# make the release tarball +meson dist -C${release_build_dir} --include-subprojects || exit + +# now build the docs +meson configure -Dgtk_doc=true ${release_build_dir} || exit +ninja -C${release_build_dir} pango-doc || exit + +tar cf ${release_build_dir}/meson-dist/pango-docs-${version}.tar.xz ${release_build_dir}/docs/ + +echo -e "\n\nPango ${version} release on branch ${branch} in ./${release_build_dir}/:\n" + +ls -l --sort=time -r "${release_build_dir}/meson-dist" + +echo -e "\nPlease sanity-check these tarballs before uploading them." diff --git a/meson.build b/meson.build index 9b6e1180..36fc4112 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('pango', 'c', 'cpp', - version: '1.45.2', + version: '1.47.0', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', @@ -57,7 +57,7 @@ common_ldflags = [] if cc.get_id() == 'msvc' # Compiler options taken from msvc_recommended_pragmas.h # in GLib, based on _Win32_Programming_ by Rector and Newcomer - test_cflags = ['-FImsvc_recommended_pragmas', '-utf-8'] + test_cflags = ['-FImsvc_recommended_pragmas.h', '-utf-8'] add_project_arguments(cc.get_supported_arguments(test_cflags), language: 'c') test_c_only_flags = [] elif cc.get_id() == 'gcc' or cc.get_id() == 'clang' @@ -216,7 +216,7 @@ fribidi_dep = dependency('fribidi', version: fribidi_req_version, default_options: ['docs=false']) pango_deps += fribidi_dep -thai_dep = dependency('libthai', version: libthai_req_version, required: false) +thai_dep = dependency('libthai', version: libthai_req_version, required: get_option('libthai')) if thai_dep.found() pango_conf.set('HAVE_LIBTHAI', 1) pango_deps += thai_dep @@ -270,16 +270,24 @@ endif pango_deps += harfbuzz_dep # Only use FontConfig fallback when required or requested -fontconfig_required = (host_system != 'windows' and host_system != 'darwin') or get_option('use_fontconfig') -fontconfig_dep = dependency('fontconfig', version: fontconfig_req_version, required: false) +fontconfig_option = get_option('fontconfig') + +fontconfig_sys_required = (host_system != 'windows' and host_system != 'darwin') +if fontconfig_sys_required and fontconfig_option.disabled() + error('Fontconfig is required on this platform (pass -Dfontconfig=enabled or -Dfontconfig=auto)') +endif + +fontconfig_required = fontconfig_sys_required or fontconfig_option.enabled() + +fontconfig_dep = dependency('fontconfig', version: fontconfig_req_version, required: fontconfig_option) if fontconfig_dep.found() fontconfig_pc = 'fontconfig' else if cc.get_id() == 'msvc' and cc.has_header('fontconfig/fontconfig.h') # Look for the Visual Studio-style import library if FontConfig's .pc file cannot be # found on Visual Studio - fontconfig_dep = cc.find_library('fontconfig', required: false) + fontconfig_dep = cc.find_library('fontconfig', required: fontconfig_option) if fontconfig_dep.found() fontconfig_lib = '-lfontconfig' endif @@ -312,7 +320,7 @@ message('fontconfig has FcWeightFromOpenTypeDouble: ' + res) # The first version of freetype with a pkg-config file is 2.1.5 # We require both fontconfig and freetype if we are to have either. -freetype_dep = dependency('freetype2', required: false) +freetype_dep = dependency('freetype2', required: get_option('freetype')) if freetype_dep.found() freetype2_pc = 'freetype2' @@ -320,7 +328,7 @@ else if cc.get_id() == 'msvc' and cc.has_header('ft2build.h') foreach ft2_lib: ['freetype', 'freetypemt'] if not freetype_dep.found() - freetype_dep = cc.find_library(ft2_lib, required: false) + freetype_dep = cc.find_library(ft2_lib, required: get_option('freetype')) if freetype_dep.found() freetype2_lib = '-l@0@'.format(ft2_lib) endif @@ -330,7 +338,7 @@ else endif if fontconfig_required and not freetype_dep.found() - freetype_dep = dependency('freetype2', required: false, + freetype_dep = dependency('freetype2', required: get_option('freetype'), fallback: ['freetype2', 'freetype_dep']) endif @@ -341,7 +349,7 @@ if build_pangoft2 pango_deps += freetype_dep endif -xft_dep = dependency('xft', version: xft_req_version, required: false) +xft_dep = dependency('xft', version: xft_req_version, required: get_option('xft')) if xft_dep.found() and fontconfig_dep.found() and freetype_dep.found() pango_conf.set('HAVE_XFT', 1) pango_deps += dependency('xrender', required: false) @@ -364,7 +372,7 @@ if host_system == 'darwin' endif cairo_found_type = '' -cairo_dep = dependency('cairo', version: cairo_req_version, required: false) +cairo_dep = dependency('cairo', version: cairo_req_version, required: get_option('cairo')) if cairo_dep.found() cairo_found_type = cairo_dep.type_name() @@ -379,7 +387,7 @@ endif # in a declarative way if not cairo_dep.found() cairo_dep = dependency('cairo', version: cairo_req_version, - fallback: ['cairo', 'libcairo_dep']) + fallback: ['cairo', 'libcairo_dep'], required: get_option('cairo')) cairo_found_type = cairo_dep.type_name() endif @@ -538,6 +546,23 @@ if cairo_dep.found() endif endif +# libsysprof-capture support +libsysprof_capture_dep = dependency('sysprof-capture-4', + required: get_option('sysprof'), + default_options: [ + 'enable_examples=false', + 'enable_gtk=false', + 'enable_tests=false', + 'enable_tools=false', + 'libsysprof=false', + 'with_sysprofd=none', + 'help=false', + ], + fallback: ['sysprof', 'libsysprof_capture_dep'], +) +pango_conf.set('HAVE_SYSPROF', libsysprof_capture_dep.found()) +pango_deps += libsysprof_capture_dep + gnome = import('gnome') pkgconfig = import('pkgconfig') diff --git a/meson_options.txt b/meson_options.txt index 7a59fa2b..5aa7c795 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,7 +10,27 @@ option('install-tests', description : 'Install tests', type: 'boolean', value: 'false') -option('use_fontconfig', - description : 'Force using FontConfig where it is optional, on Windows and macOS. This is ignored on platforms where it is required', - type: 'boolean', - value: 'false') +option('fontconfig', + description : 'Build with FontConfig support. Passing \'auto\' or \'disabled\' disables fontconfig where it is optional, i.e. on Windows and macOS. Passing \'disabled\' on platforms where fontconfig is required results in error.', + type: 'feature', + value: 'auto') +option('sysprof', + type : 'feature', + value : 'disabled', + description : 'include tracing support for sysprof') +option('libthai', + type : 'feature', + value : 'auto', + description : 'Build with libthai support') +option('cairo', + type : 'feature', + value : 'auto', + description : 'Build with cairo support') +option('xft', + type : 'feature', + value : 'auto', + description : 'Build with xft support') +option('freetype', + type : 'feature', + value : 'auto', + description : 'Build with freetype support') diff --git a/pango/fonts.c b/pango/fonts.c index e83abbbe..1695366b 100644 --- a/pango/fonts.c +++ b/pango/fonts.c @@ -1799,7 +1799,7 @@ pango_font_get_glyph_extents (PangoFont *font, } if (logical_rect) { - logical_rect->x = logical_rect->y = 0; + logical_rect->x = 0; logical_rect->y = - PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE; logical_rect->height = PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE; logical_rect->width = PANGO_UNKNOWN_GLYPH_WIDTH * PANGO_SCALE; diff --git a/pango/meson.build b/pango/meson.build index 11578ddf..4c055f52 100644 --- a/pango/meson.build +++ b/pango/meson.build @@ -177,6 +177,7 @@ if build_pangoft2 'pangofc-font.c', 'pangofc-fontmap.c', 'pangofc-decoder.c', + 'pango-trace.c', ] pangoot_headers = [ diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 1fa8c399..4f96135b 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -1253,7 +1253,7 @@ pango_attr_show_new (PangoShowFlags flags) * Return value: (transfer full): the newly allocated #PangoAttribute, * which should be freed with pango_attribute_destroy(). * - * Since: 1.45 + * Since: 1.46 **/ PangoAttribute * pango_attr_overline_new (PangoOverline overline) @@ -1281,7 +1281,7 @@ pango_attr_overline_new (PangoOverline overline) * Return value: (transfer full): the newly allocated #PangoAttribute, * which should be freed with pango_attribute_destroy(). * - * Since: 1.45 + * Since: 1.46 **/ PangoAttribute * pango_attr_overline_color_new (guint16 red, @@ -1523,11 +1523,12 @@ pango_attr_list_insert_before (PangoAttrList *list, **/ void pango_attr_list_change (PangoAttrList *list, - PangoAttribute *attr) + PangoAttribute *attr) { guint i, p; guint start_index = attr->start_index; guint end_index = attr->end_index; + gboolean inserted; g_return_if_fail (list != NULL); @@ -1543,6 +1544,7 @@ pango_attr_list_change (PangoAttrList *list, return; } + inserted = FALSE; for (i = 0, p = list->attributes->len; i < p; i++) { PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i); @@ -1550,6 +1552,7 @@ pango_attr_list_change (PangoAttrList *list, if (tmp_attr->start_index > start_index) { g_ptr_array_insert (list->attributes, i, attr); + inserted = TRUE; break; } @@ -1559,8 +1562,8 @@ pango_attr_list_change (PangoAttrList *list, if (tmp_attr->end_index < start_index) continue; /* This attr does not overlap with the new one */ + g_assert (tmp_attr->start_index <= start_index); g_assert (tmp_attr->end_index >= start_index); - g_assert (start_index <= tmp_attr->end_index); if (pango_attribute_equal (tmp_attr, attr)) { @@ -1579,21 +1582,22 @@ pango_attr_list_change (PangoAttrList *list, pango_attribute_destroy (attr); attr = tmp_attr; + inserted = TRUE; break; } else { /* Split, truncate, or remove the old attribute */ - if (tmp_attr->end_index > attr->end_index) + if (tmp_attr->end_index > end_index) { PangoAttribute *end_attr = pango_attribute_copy (tmp_attr); - end_attr->start_index = attr->end_index; + end_attr->start_index = end_index; pango_attr_list_insert (list, end_attr); } - if (tmp_attr->start_index == attr->start_index) + if (tmp_attr->start_index == start_index) { pango_attribute_destroy (tmp_attr); g_ptr_array_remove_index (list->attributes, i); @@ -1601,12 +1605,12 @@ pango_attr_list_change (PangoAttrList *list, } else { - tmp_attr->end_index = attr->start_index; + tmp_attr->end_index = start_index; } } } - if (i == p) + if (!inserted) { /* we didn't insert attr yet */ pango_attr_list_insert (list, attr); @@ -1693,40 +1697,41 @@ pango_attr_list_update (PangoAttrList *list, { guint i, p; - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - - if (attr->start_index >= pos && - attr->end_index < pos + remove) - { - pango_attribute_destroy (attr); - g_ptr_array_remove_index (list->attributes, i); - i--; /* Look at this index again */ - p--; - continue; - } - - if (attr->start_index >= pos && - attr->start_index < pos + remove) - { - attr->start_index = pos + add; - } - else if (attr->start_index >= pos + remove) - { - attr->start_index += add - remove; - } + if (list->attributes) + for (i = 0, p = list->attributes->len; i < p; i++) + { + PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - if (attr->end_index >= pos && + if (attr->start_index >= pos && attr->end_index < pos + remove) - { - attr->end_index = pos; - } - else if (attr->end_index >= pos + remove) - { - attr->end_index += add - remove; - } - } + { + pango_attribute_destroy (attr); + g_ptr_array_remove_index (list->attributes, i); + i--; /* Look at this index again */ + p--; + continue; + } + + if (attr->start_index >= pos && + attr->start_index < pos + remove) + { + attr->start_index = pos + add; + } + else if (attr->start_index >= pos + remove) + { + attr->start_index += add - remove; + } + + if (attr->end_index >= pos && + attr->end_index < pos + remove) + { + attr->end_index = pos; + } + else if (attr->end_index >= pos + remove) + { + attr->end_index += add - remove; + } + } } /** @@ -1771,26 +1776,27 @@ pango_attr_list_splice (PangoAttrList *list, */ #define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b)) - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i);; - - if (attr->start_index <= upos) - { - if (attr->end_index > upos) - attr->end_index = CLAMP_ADD (attr->end_index, ulen); - } - else - { - /* This could result in a zero length attribute if it - * gets squashed up against G_MAXUINT, but deleting such - * an element could (in theory) suprise the caller, so - * we don't delete it. - */ - attr->start_index = CLAMP_ADD (attr->start_index, ulen); - attr->end_index = CLAMP_ADD (attr->end_index, ulen); - } - } + if (list->attributes) + for (i = 0, p = list->attributes->len; i < p; i++) + { + PangoAttribute *attr = g_ptr_array_index (list->attributes, i);; + + if (attr->start_index <= upos) + { + if (attr->end_index > upos) + attr->end_index = CLAMP_ADD (attr->end_index, ulen); + } + else + { + /* This could result in a zero length attribute if it + * gets squashed up against G_MAXUINT, but deleting such + * an element could (in theory) suprise the caller, so + * we don't delete it. + */ + attr->start_index = CLAMP_ADD (attr->start_index, ulen); + attr->end_index = CLAMP_ADD (attr->end_index, ulen); + } + } if (!other->attributes || other->attributes->len == 0) return; @@ -1862,6 +1868,7 @@ pango_attr_list_equal (PangoAttrList *list, { GPtrArray *attrs, *other_attrs; guint64 skip_bitmask = 0; + guint i; if (list == other_list) return TRUE; @@ -1878,12 +1885,13 @@ pango_attr_list_equal (PangoAttrList *list, if (attrs->len != other_attrs->len) return FALSE; - for (guint i = 0; i < attrs->len; i++) + for (i = 0; i < attrs->len; i++) { PangoAttribute *attr = g_ptr_array_index (attrs, i); gboolean attr_equal = FALSE; + guint other_attr_index; - for (guint other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++) + for (other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++) { PangoAttribute *other_attr = g_ptr_array_index (other_attrs, other_attr_index); guint64 other_attr_bitmask = other_attr_index < 64 ? 1 << other_attr_index : 0; @@ -1995,7 +2003,7 @@ pango_attr_iterator_range (PangoAttrIterator *iterator, gboolean pango_attr_iterator_next (PangoAttrIterator *iterator) { - guint i; + int i; g_return_val_if_fail (iterator != NULL, FALSE); @@ -2008,19 +2016,14 @@ pango_attr_iterator_next (PangoAttrIterator *iterator) if (iterator->attribute_stack) { - for (i = 0; i < iterator->attribute_stack->len; i++) + for (i = iterator->attribute_stack->len - 1; i>= 0; i--) { const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); if (attr->end_index == iterator->start_index) - { - g_ptr_array_remove_index (iterator->attribute_stack, i); /* Can't use index_fast :( */; - i--; - } + g_ptr_array_remove_index (iterator->attribute_stack, i); /* Can't use index_fast :( */ else - { - iterator->end_index = MIN (iterator->end_index, attr->end_index); - } + iterator->end_index = MIN (iterator->end_index, attr->end_index); } } @@ -2128,14 +2131,14 @@ PangoAttribute * pango_attr_iterator_get (PangoAttrIterator *iterator, PangoAttrType type) { - guint i; + int i; g_return_val_if_fail (iterator != NULL, NULL); if (!iterator->attribute_stack) return NULL; - for (i = 0; i < iterator->attribute_stack->len; i++) + for (i = iterator->attribute_stack->len - 1; i>= 0; i--) { PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index 6f6622ab..dd38aee2 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -65,7 +65,12 @@ PANGO_AVAILABLE_IN_ALL void pango_color_free (PangoColor *color); PANGO_AVAILABLE_IN_ALL gboolean pango_color_parse (PangoColor *color, - const char *spec); + const char *spec); +PANGO_AVAILABLE_IN_1_46 +gboolean pango_color_parse_with_alpha + (PangoColor *color, + guint16 *alpha, + const char *spec); PANGO_AVAILABLE_IN_1_16 gchar *pango_color_to_string(const PangoColor *color); diff --git a/pango/pango-color.c b/pango/pango-color.c index 3c37c3d0..a530719f 100644 --- a/pango/pango-color.c +++ b/pango/pango-color.c @@ -207,20 +207,42 @@ hex (const char *spec, } -/* Like pango_color_parse, but allow strings of the form +/** + * pango_color_parse_with_alpha: + * @color: (nullable): a #PangoColor structure in which to store the + * result, or %NULL + * @alpha: (out) (optional): return location for alpha, or %NULL + * @spec: a string specifying the new color + * + * Fill in the fields of a color from a string specification. The + * string can either one of a large set of standard names. (Taken + * from the CSS <ulink url="http://dev.w3.org/csswg/css-color/#named-colors">specification</ulink>), or it can be a hexadecimal + * value in the + * form '#rgb' '#rrggbb' '#rrrgggbbb' or '#rrrrggggbbbb' where + * 'r', 'g' and 'b' are hex digits of the red, green, and blue + * components of the color, respectively. (White in the four + * forms is '#fff' '#ffffff' '#fffffffff' and '#ffffffffffff') + * + * Additionally, parse strings of the form * '#rgba', '#rrggbbaa', '#rrrrggggbbbbaaaa', - * if alpha is not NULL. If no alpha component is found - * in the string, *alpha is set to 0. + * if @alpha is not %NULL, and set @alpha to the value specified + * by the hex digits for 'a'. If no alpha component is found + * in @spec, @alpha is set to 0xffff (for a solid color). + * + * Return value: %TRUE if parsing of the specifier succeeded, + * otherwise false. + * + * Since: 1.46 */ gboolean -_pango_color_parse_with_alpha (PangoColor *color, - guint16 *alpha, - const char *spec) +pango_color_parse_with_alpha (PangoColor *color, + guint16 *alpha, + const char *spec) { g_return_val_if_fail (spec != NULL, FALSE); if (alpha) - *alpha = 0; + *alpha = 0xffff; if (spec[0] == '#') { @@ -248,52 +270,53 @@ _pango_color_parse_with_alpha (PangoColor *color, has_alpha = TRUE; break; default: - return FALSE; + return FALSE; } if (!hex (spec, len, &r) || - !hex (spec + len, len, &g) || - !hex (spec + len * 2, len, &b) || + !hex (spec + len, len, &g) || + !hex (spec + len * 2, len, &b) || (has_alpha && !hex (spec + len * 3, len, &a))) - return FALSE; + return FALSE; if (color) - { - int bits = len * 4; - r <<= 16 - bits; - g <<= 16 - bits; - b <<= 16 - bits; - while (bits < 16) - { - r |= (r >> bits); - g |= (g >> bits); - b |= (b >> bits); - bits *= 2; - } - color->red = r; - color->green = g; - color->blue = b; - } + { + int bits = len * 4; + r <<= 16 - bits; + g <<= 16 - bits; + b <<= 16 - bits; + while (bits < 16) + { + r |= (r >> bits); + g |= (g >> bits); + b |= (b >> bits); + bits *= 2; + } + color->red = r; + color->green = g; + color->blue = b; + } if (alpha && has_alpha) { - int bits = len * 4; + int bits = len * 4; a <<= 16 - bits; - while (bits < 16) - { + while (bits < 16) + { a |= (a >> bits); - bits *= 2; - } + bits *= 2; + } *alpha = a; } } else { if (!find_color (spec, color)) - return FALSE; + return FALSE; } return TRUE; } + /** * pango_color_parse: * @color: (nullable): a #PangoColor structure in which to store the @@ -316,5 +339,5 @@ gboolean pango_color_parse (PangoColor *color, const char *spec) { - return _pango_color_parse_with_alpha (color, NULL, spec); + return pango_color_parse_with_alpha (color, NULL, spec); } diff --git a/pango/pango-context.c b/pango/pango-context.c index bebe804a..1fcdf366 100644 --- a/pango/pango-context.c +++ b/pango/pango-context.c @@ -1065,6 +1065,9 @@ itemize_state_init (ItemizeState *state, width_iter_init (&state->width_iter, text + start_index, length); _pango_emoji_iter_init (&state->emoji_iter, text + start_index, length); + if (state->emoji_iter.is_emoji) + state->width_iter.end = MAX (state->width_iter.end, state->emoji_iter.end); + update_end (state); if (pango_font_description_get_set_fields (state->font_desc) & PANGO_FONT_MASK_GRAVITY) @@ -1111,15 +1114,18 @@ itemize_state_next (ItemizeState *state) &state->script_end, &state->script); state->changed |= SCRIPT_CHANGED; } - if (state->run_end == state->width_iter.end) - { - width_iter_next (&state->width_iter); - state->changed |= WIDTH_CHANGED; - } if (state->run_end == state->emoji_iter.end) { _pango_emoji_iter_next (&state->emoji_iter); state->changed |= EMOJI_CHANGED; + + if (state->emoji_iter.is_emoji) + state->width_iter.end = MAX (state->width_iter.end, state->emoji_iter.end); + } + if (state->run_end == state->width_iter.end) + { + width_iter_next (&state->width_iter); + state->changed |= WIDTH_CHANGED; } update_end (state); diff --git a/pango/pango-language-sample-table.h b/pango/pango-language-sample-table.h index b0cf414d..e92427ba 100644 --- a/pango/pango-language-sample-table.h +++ b/pango/pango-language-sample-table.h @@ -24,10 +24,14 @@ * http://en.wikipedia.org/wiki/Sample_Font_Displays_In_Other_Languages * Fetched on 2008-08-19 * + * WP + * Wikipedia, Article about the language + * Fetched on 2020-09-08 + * * GLASS * Kermit project's "I Can Eat Glass" list, also available in pango-view/ * http://www.columbia.edu/kermit/utf8.html#glass - * Fetched on 2008-08-19 + * Fetched on 2008-08-19, updates on 2020-09-08 * * KERMIT * Kermit project's Quick-Brown-Fox equivalents for other languages @@ -144,6 +148,12 @@ LANGUAGE( /* Twelve boxing fighters drive Viktor over the great. */ ) LANGUAGE( + dv /* Maldivian */, + WP, + "މާއްދާ 1 – ހުރިހާ އިންސާނުން ވެސް އުފަންވަނީ، ދަރަޖަ އާއި ޙައްޤު ތަކުގައި މިނިވަންކަމާއި ހަމަހަމަކަން ލިބިގެންވާ ބައެއްގެ ގޮތުގައެވެ." + /* Beginning of UDHR */ + ) +LANGUAGE( el /* Greek */, WP-SFD, "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)" @@ -320,6 +330,12 @@ LANGUAGE( /* I can eat glass and it doesn't hurt me. */ ) LANGUAGE( + km /* Khmer */, + GLASS, + "ខ្ញុំអាចញុំកញ្ចក់បាន ដោយគ្មានបញ្ហារ" + /* I can eat glass and it doesn't hurt me. */ + ) +LANGUAGE( kn /* Kannada */, GLASS, "ನಾನು ಗಾಜನ್ನು ತಿನ್ನಬಲ್ಲೆ ಮತ್ತು ಅದರಿಂದ ನನಗೆ ನೋವಾಗುವುದಿಲ್ಲ." @@ -343,6 +359,12 @@ LANGUAGE( "Sic surgens, dux, zelotypos quam karus haberis" ) LANGUAGE( + lo /* Lao */, + GLASS, + "ຂອ້ຍກິນແກ້ວໄດ້ໂດຍທີ່ມັນບໍ່ໄດ້ເຮັດໃຫ້ຂອ້ຍເຈັບ" + /* I can eat glass and it doesn't hurt me. */ + ) +LANGUAGE( lt /* Lithuanian */, WP-PANG, "Įlinkdama fechtuotojo špaga sublykčiojusi pragręžė apvalų arbūzą." @@ -391,6 +413,12 @@ LANGUAGE( /* I can eat glass and it doesn't hurt me. */ ) LANGUAGE( + my /* Burmese */, + WP, + "ဘာသာပြန်နှင့် စာပေပြုစုရေး ကော်မရှင်" + /* Literary and Translation Commission */ + ) +LANGUAGE( nap /* Neapolitan */, GLASS, "M' pozz magna' o'vetr, e nun m' fa mal." @@ -491,6 +519,11 @@ LANGUAGE( /* I can eat glass and it doesn't hurt me. */ ) LANGUAGE( + si /* Sinhalese */, + WP, + "මනොපුබ්බඞ්ගමා ධම්මා, මනොසෙට්ඨා මනොමයා; මනසා චෙ පදුට්ඨෙන, භාසති වා කරොති වා; තතො නං දුක්ඛමන්වෙති, චක්කංව වහතො පදං." + ) +LANGUAGE( sk /* Slovak */, KERMIT, "Starý kôň na hŕbe kníh žuje tíško povädnuté ruže, na stĺpe sa ďateľ učí kvákať novú ódu o živote." diff --git a/pango/pango-language.c b/pango/pango-language.c index 575d4652..04c3e0ca 100644 --- a/pango/pango-language.c +++ b/pango/pango-language.c @@ -66,7 +66,7 @@ pango_language_get_private (PangoLanguage *language) if (!language) return NULL; - priv = (PangoLanguagePrivate *) ((char *)language - sizeof (PangoLanguagePrivate)); + priv = (PangoLanguagePrivate *)(void *)((char *)language - sizeof (PangoLanguagePrivate)); if (G_UNLIKELY (priv->magic != PANGO_LANGUAGE_PRIVATE_MAGIC)) { @@ -662,7 +662,7 @@ pango_language_get_scripts (PangoLanguage *language, script_for_lang, pango_script_for_lang); - if (!script_for_lang) + if (!script_for_lang || script_for_lang->scripts[0] == 0) { if (num_scripts) *num_scripts = 0; @@ -791,13 +791,14 @@ parse_default_languages (void) return (PangoLanguage **) g_array_free (langs, FALSE); } +G_LOCK_DEFINE_STATIC (languages); +static gboolean initialized = FALSE; /* MT-safe */ +static PangoLanguage * const * languages = NULL; /* MT-safe */ +static GHashTable *hash = NULL; /* MT-safe */ + static PangoLanguage * _pango_script_get_default_language (PangoScript script) { - G_LOCK_DEFINE_STATIC (languages); - static gboolean initialized = FALSE; /* MT-safe */ - static PangoLanguage * const * languages = NULL; /* MT-safe */ - static GHashTable *hash = NULL; /* MT-safe */ PangoLanguage *result, * const * p; G_LOCK (languages); @@ -835,6 +836,33 @@ out: } /** + * pango_language_get_preferred: + * + * Returns the list of languages that the user prefers, as specified + * by the PANGO_LANGUAGE or LANGUAGE environment variables, in order + * of preference. Note that this list does not necessarily include + * the language returned by pango_language_get_default(). + * + * When choosing language-specific resources, such as the sample + * text returned by pango_language_get_sample_string(), you should + * first try the default language, followed by the languages returned + * by this function. + * + * Returns: (transfer none) (nullable): a %NULL-terminated array of + * PangoLanguage* + * + * Since: 1.48 + */ +PangoLanguage ** +pango_language_get_preferred (void) +{ + /* We call this just for its side-effect of initializing languages */ + _pango_script_get_default_language (PANGO_SCRIPT_COMMON); + + return languages; +} + +/** * pango_script_get_sample_language: * @script: a #PangoScript * diff --git a/pango/pango-language.h b/pango/pango-language.h index 2ab07bc0..16e6512c 100644 --- a/pango/pango-language.h +++ b/pango/pango-language.h @@ -53,6 +53,9 @@ const char *pango_language_get_sample_string (PangoLanguage *language) G_GNUC PANGO_AVAILABLE_IN_1_16 PangoLanguage *pango_language_get_default (void) G_GNUC_CONST; +PANGO_AVAILABLE_IN_1_48 +PangoLanguage **pango_language_get_preferred (void) G_GNUC_CONST; + PANGO_AVAILABLE_IN_ALL gboolean pango_language_matches (PangoLanguage *language, const char *range_list) G_GNUC_PURE; diff --git a/pango/pango-layout.c b/pango/pango-layout.c index b07c8487..68ffd190 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -1173,6 +1173,7 @@ pango_layout_set_text (PangoLayout *layout, g_warning ("Invalid UTF-8 string passed to pango_layout_set_text()"); layout->n_chars = pango_utf8_strlen (layout->text, -1); + layout->length = strlen (layout->text); layout_changed (layout); @@ -3608,6 +3609,9 @@ find_hyphen_width (PangoItem *item) hb_font_t *hb_font; hb_codepoint_t glyph; + if (!item->analysis.font) + return 0; + /* This is not technically correct, since * a) we may end up inserting a different hyphen * b) we should reshape the entire run @@ -4084,6 +4088,7 @@ process_line (PangoLayout *layout, static void get_items_log_attrs (const char *text, + int start, int length, GList *items, PangoLogAttr *log_attrs, @@ -4092,11 +4097,13 @@ get_items_log_attrs (const char *text, int offset = 0; GList *l; - pango_default_break (text, length, NULL, log_attrs, log_attrs_len); + pango_default_break (text + start, length, NULL, log_attrs, log_attrs_len); for (l = items; l; l = l->next) { PangoItem *item = l->data; + g_assert (item->offset <= start + length); + g_assert (item->length <= (start + length) - item->offset); pango_tailor_break (text + item->offset, item->length, @@ -4367,7 +4374,8 @@ pango_layout_check_lines (PangoLayout *layout) apply_attributes_to_items (state.items, shape_attrs); - get_items_log_attrs (start, + get_items_log_attrs (layout->text, + start - layout->text, delimiter_index + delim_len, state.items, layout->log_attrs + start_offset, diff --git a/pango/pango-markup.c b/pango/pango-markup.c index a67e10fd..b74c1ad4 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -1199,7 +1199,7 @@ span_parse_color (const char *attr_name, int line_number, GError **error) { - if (!_pango_color_parse_with_alpha (color, alpha, attr_val)) + if (!pango_color_parse_with_alpha (color, alpha, attr_val)) { g_set_error (error, G_MARKUP_ERROR, @@ -1622,7 +1622,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, goto error; add_attribute (tag, pango_attr_foreground_new (color.red, color.green, color.blue)); - if (alpha != 0) + if (alpha != 0xffff) add_attribute (tag, pango_attr_foreground_alpha_new (alpha)); } @@ -1635,7 +1635,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, goto error; add_attribute (tag, pango_attr_background_new (color.red, color.green, color.blue)); - if (alpha != 0) + if (alpha != 0xffff) add_attribute (tag, pango_attr_background_alpha_new (alpha)); } diff --git a/pango/pango-ot-private.h b/pango/pango-ot-private.h index 0d803ec1..d9d86644 100644 --- a/pango/pango-ot-private.h +++ b/pango/pango-ot-private.h @@ -22,12 +22,12 @@ #ifndef __PANGO_OT_PRIVATE_H__ #define __PANGO_OT_PRIVATE_H__ +#include <glib.h> #include <glib-object.h> #include <pango/pango-ot.h> #include <hb-ot.h> #include <hb-ft.h> -#include <hb-glib.h> #include "pangofc-private.h" diff --git a/pango/pango-ot-tag.c b/pango/pango-ot-tag.c index c4f337e8..5f50b77c 100644 --- a/pango/pango-ot-tag.c +++ b/pango/pango-ot-tag.c @@ -49,7 +49,7 @@ pango_ot_tag_from_script (PangoScript script) unsigned int count = 1; hb_tag_t tags[1]; - hb_ot_tags_from_script_and_language (hb_glib_script_to_script ((GUnicodeScript)script), + hb_ot_tags_from_script_and_language ((hb_script_t) g_unicode_script_to_iso15924 ((GUnicodeScript) script), HB_LANGUAGE_INVALID, &count, tags, @@ -84,7 +84,7 @@ pango_ot_tag_from_script (PangoScript script) PangoScript pango_ot_tag_to_script (PangoOTTag script_tag) { - return (PangoScript) hb_glib_script_from_script (hb_ot_tag_to_script ((hb_tag_t) script_tag)); + return (PangoScript) g_unicode_script_from_iso15924 (hb_ot_tag_to_script ((hb_tag_t) script_tag)); } diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c index 07f81a88..432875a4 100644 --- a/pango/pango-renderer.c +++ b/pango/pango-renderer.c @@ -62,6 +62,7 @@ struct _PangoRendererPrivate PangoLayoutLine *line; LineState *line_state; + PangoOverline overline; }; static void pango_renderer_finalize (GObject *gobject); @@ -319,7 +320,7 @@ handle_line_state_change (PangoRenderer *renderer, rect->width = state->logical_rect_end - rect->x; draw_overline (renderer, state); - state->overline = renderer->overline; + state->overline = renderer->priv->overline; rect->x = state->logical_rect_end; rect->width = 0; } @@ -418,14 +419,14 @@ add_overline (PangoRenderer *renderer, new_rect.height = underline_thickness; new_rect.y = base_y; - switch (renderer->overline) + switch (renderer->priv->overline) { case PANGO_OVERLINE_NONE: g_assert_not_reached (); break; case PANGO_OVERLINE_SINGLE: new_rect.y -= ascent; - if (state->overline == renderer->overline) + if (state->overline == renderer->priv->overline) { new_rect.y = MIN (current_rect->y, new_rect.y); new_rect.height = MAX (current_rect->height, new_rect.height); @@ -435,7 +436,7 @@ add_overline (PangoRenderer *renderer, break; } - if (renderer->overline == state->overline && + if (renderer->priv->overline == state->overline && new_rect.y == current_rect->y && new_rect.height == current_rect->height) { @@ -446,7 +447,7 @@ add_overline (PangoRenderer *renderer, draw_overline (renderer, state); *current_rect = new_rect; - state->overline = renderer->overline; + state->overline = renderer->priv->overline; } } @@ -626,7 +627,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, else { if (renderer->underline != PANGO_UNDERLINE_NONE || - renderer->overline != PANGO_OVERLINE_NONE || + renderer->priv->overline != PANGO_OVERLINE_NONE || renderer->strikethrough) { ink = &ink_rect; @@ -684,7 +685,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, } if (renderer->underline != PANGO_UNDERLINE_NONE || - renderer->overline != PANGO_OVERLINE_NONE || + renderer->priv->overline != PANGO_OVERLINE_NONE || renderer->strikethrough) { metrics = pango_font_get_metrics (run->item->analysis.font, @@ -695,7 +696,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, x + x_off, y - rise, ink, logical); - if (renderer->overline != PANGO_OVERLINE_NONE) + if (renderer->priv->overline != PANGO_OVERLINE_NONE) add_overline (renderer, &state,metrics, x + x_off, y - rise, ink, logical); @@ -712,7 +713,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, state.underline != PANGO_UNDERLINE_NONE) draw_underline (renderer, &state); - if (renderer->overline == PANGO_OVERLINE_NONE && + if (renderer->priv->overline == PANGO_OVERLINE_NONE && state.overline != PANGO_OVERLINE_NONE) draw_overline (renderer, &state); @@ -1449,7 +1450,7 @@ pango_renderer_default_prepare_run (PangoRenderer *renderer, GSList *l; renderer->underline = PANGO_UNDERLINE_NONE; - renderer->overline = PANGO_OVERLINE_NONE; + renderer->priv->overline = PANGO_OVERLINE_NONE; renderer->strikethrough = FALSE; for (l = run->item->analysis.extra_attrs; l; l = l->next) @@ -1463,7 +1464,7 @@ pango_renderer_default_prepare_run (PangoRenderer *renderer, break; case PANGO_ATTR_OVERLINE: - renderer->overline = ((PangoAttrInt *)attr)->value; + renderer->priv->overline = ((PangoAttrInt *)attr)->value; break; case PANGO_ATTR_STRIKETHROUGH: diff --git a/pango/pango-renderer.h b/pango/pango-renderer.h index 4dae6a92..89107fd1 100644 --- a/pango/pango-renderer.h +++ b/pango/pango-renderer.h @@ -77,7 +77,6 @@ struct _PangoRenderer GObject parent_instance; PangoUnderline underline; - PangoOverline overline; gboolean strikethrough; int active_count; diff --git a/pango/pango-trace-private.h b/pango/pango-trace-private.h new file mode 100644 index 00000000..5d2a4fdf --- /dev/null +++ b/pango/pango-trace-private.h @@ -0,0 +1,53 @@ +/* Pango + * pango-trace-private.h: + * + * Copyright (C) 2020 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + + +#ifdef HAVE_SYSPROF +#include <sysprof-capture.h> +#endif + +#include <glib.h> + +G_BEGIN_DECLS + +#ifdef HAVE_SYSPROF +#define PANGO_TRACE_CURRENT_TIME SYSPROF_CAPTURE_CURRENT_TIME +#else +#define PANGO_TRACE_CURRENT_TIME 0 +#endif + +void pango_trace_mark (gint64 begin_time, + const gchar *name, + const gchar *message_format, + ...) G_GNUC_PRINTF (3, 4); + +#ifndef HAVE_SYSPROF +/* Optimise the whole call out */ +#if defined(G_HAVE_ISO_VARARGS) +#define g_trace_mark(b, n, m, ...) +#elif defined(G_HAVE_GNUC_VARARGS) +#define g_trace_mark(b, n, m...) +#else +/* no varargs macro support; the call will have to be optimised out by the compiler */ +#endif +#endif + +G_END_DECLS diff --git a/pango/pango-trace.c b/pango/pango-trace.c new file mode 100644 index 00000000..9f37376d --- /dev/null +++ b/pango/pango-trace.c @@ -0,0 +1,40 @@ +/* Pango + * pango-trace.c: + * + * Copyright (C) 2020 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "pango-trace-private.h" + +#include <stdarg.h> + +void +(pango_trace_mark) (gint64 begin_time, + const gchar *name, + const gchar *message_format, + ...) +{ +#ifdef HAVE_SYSPROF + gint64 end_time = PANGO_TRACE_CURRENT_TIME; + va_list args; + + va_start (args, message_format); + sysprof_collector_mark_vprintf (begin_time, end_time - begin_time, "Pango", name, message_format, args); + va_end (args); +#endif /* HAVE_SYSPROF */ +} diff --git a/pango/pango-utils-internal.h b/pango/pango-utils-internal.h index 56340215..0bc355e0 100644 --- a/pango/pango-utils-internal.h +++ b/pango/pango-utils-internal.h @@ -43,10 +43,6 @@ gboolean pango_parse_flags (GType type, char *_pango_trim_string (const char *str); -gboolean _pango_color_parse_with_alpha (PangoColor *color, - guint16 *alpha, - const char *spec); - G_END_DECLS diff --git a/pango/pango-version-macros.h b/pango/pango-version-macros.h index 52b37049..4008579c 100644 --- a/pango/pango-version-macros.h +++ b/pango/pango-version-macros.h @@ -262,6 +262,16 @@ */ #define PANGO_VERSION_1_46 (G_ENCODE_VERSION (1, 46)) +/** + * PANGO_VERSION_1_48: + * + * A macro that evaluates to the 1.48 version of Pango, in a format + * that can be used by the C pre-processor. + * + * Since: 1.48 + */ +#define PANGO_VERSION_1_48 (G_ENCODE_VERSION (1, 48)) + /* evaluates to the current stable version; for development cycles, * this means the next stable target */ @@ -681,4 +691,18 @@ # define PANGO_AVAILABLE_IN_1_46 _PANGO_EXTERN #endif +#if PANGO_VERSION_MIN_REQUIRED >= PANGO_VERSION_1_48 +# define PANGO_DEPRECATED_IN_1_48 PANGO_DEPRECATED +# define PANGO_DEPRECATED_IN_1_48_FOR(f) PANGO_DEPRECATED_FOR(f) +#else +# define PANGO_DEPRECATED_IN_1_48 _PANGO_EXTERN +# define PANGO_DEPRECATED_IN_1_48_FOR(f) _PANGO_EXTERN +#endif + +#if PANGO_VERSION_MAX_ALLOWED < PANGO_VERSION_1_48 +# define PANGO_AVAILABLE_IN_1_48 PANGO_UNAVAILABLE(1, 48) +#else +# define PANGO_AVAILABLE_IN_1_48 _PANGO_EXTERN +#endif + #endif /* __PANGO_VERSION_H__ */ diff --git a/pango/pangocairo-fcfontmap.c b/pango/pangocairo-fcfontmap.c index 5fe61f54..dec59c8b 100644 --- a/pango/pangocairo-fcfontmap.c +++ b/pango/pangocairo-fcfontmap.c @@ -103,7 +103,7 @@ pango_cairo_fc_font_map_fontset_key_substitute (PangoFcFontMap *fcfontmap G_G PangoFcFontsetKey *fontkey, FcPattern *pattern) { - FcConfigSubstitute (NULL, pattern, FcMatchPattern); + FcConfigSubstitute (pango_fc_font_map_get_config (fcfontmap), pattern, FcMatchPattern); if (fcfontmap->substitute_func) fcfontmap->substitute_func (pattern, fcfontmap->substitute_data); diff --git a/pango/pangocairo-fontmap.c b/pango/pangocairo-fontmap.c index dce640e3..47c2245f 100644 --- a/pango/pangocairo-fontmap.c +++ b/pango/pangocairo-fontmap.c @@ -198,8 +198,8 @@ pango_cairo_font_map_get_default (void) * * Note that since Pango 1.32.6, the default fontmap is per-thread. * This function only changes the default fontmap for - * the current thread. Default fontmaps of exisiting threads - * are not changed. Default fontmaps of any new threads will + * the current thread. Default fontmaps of existing threads + * are not changed. Default fontmaps of any new threads will * still be created using pango_cairo_font_map_new(). * * A value of %NULL for @fontmap will cause the current default diff --git a/pango/pangocoretext.c b/pango/pangocoretext.c index c261615e..44d2805a 100644 --- a/pango/pangocoretext.c +++ b/pango/pangocoretext.c @@ -176,12 +176,16 @@ pango_core_text_font_create_hb_font (PangoFont *font) if (ctfont->priv->font_ref) { + const PangoMatrix *matrix; hb_font_t *hb_font; + double x_scale, y_scale; int size; + matrix = pango_core_text_font_key_get_matrix (ctfont->priv->key); + pango_matrix_get_font_scale_factors (matrix, &x_scale, &y_scale); size = pango_core_text_font_key_get_size (ctfont->priv->key); hb_font = hb_coretext_font_create (ctfont->priv->font_ref); - hb_font_set_scale (hb_font, size, size); + hb_font_set_scale (hb_font, size / x_scale, size / y_scale); return hb_font; } diff --git a/pango/pangofc-font.c b/pango/pangofc-font.c index 4b6a34f7..6c5492ad 100644 --- a/pango/pangofc-font.c +++ b/pango/pangofc-font.c @@ -993,7 +993,7 @@ pango_fc_font_create_hb_font (PangoFont *font) if (key) { - FcPattern *pattern = pango_fc_font_key_get_pattern (key); + const FcPattern *pattern = pango_fc_font_key_get_pattern (key); const char *variations; int index; unsigned int n_axes; @@ -1035,3 +1035,35 @@ pango_fc_font_create_hb_font (PangoFont *font) done: return hb_font; } + +/** + * pango_fc_font_get_languages: + * @font: a #PangoFcFont + * + * Returns the languages that are supported by @font. + * + * This corresponds to the FC_LANG member of the FcPattern. + * + * The returned array is only valid as long as the font + * and its fontmap are valid. + * + * Returns: (transfer none) (nullable): a %NULL-terminated + * array of PangoLanguage* + * + * Since: 1.48 + */ +PangoLanguage ** +pango_fc_font_get_languages (PangoFcFont *font) +{ + PangoFcFontMap *fontmap; + PangoLanguage **languages; + + fontmap = g_weak_ref_get ((GWeakRef *) &font->fontmap); + if (!fontmap) + return NULL; + + languages = _pango_fc_font_map_get_languages (fontmap, font); + g_object_unref (fontmap); + + return languages; +} diff --git a/pango/pangofc-font.h b/pango/pangofc-font.h index 25a0277c..aa4fd3b0 100644 --- a/pango/pangofc-font.h +++ b/pango/pangofc-font.h @@ -94,6 +94,10 @@ gboolean pango_fc_font_has_char (PangoFcFont *font, PANGO_AVAILABLE_IN_1_4 guint pango_fc_font_get_glyph (PangoFcFont *font, gunichar wc); +PANGO_AVAILABLE_IN_1_48 +PangoLanguage ** + pango_fc_font_get_languages (PangoFcFont *font); + PANGO_DEPRECATED_FOR(PANGO_GET_UNKNOWN_GLYPH) PangoGlyph pango_fc_font_get_unknown_glyph (PangoFcFont *font, gunichar wc); diff --git a/pango/pangofc-fontmap.c b/pango/pangofc-fontmap.c index 41da194d..e120d305 100644 --- a/pango/pangofc-fontmap.c +++ b/pango/pangofc-fontmap.c @@ -166,8 +166,9 @@ struct _PangoFcFontFaceData int id; /* needed to handle TTC files with multiple faces */ /* Data */ - FcPattern *pattern; /* Referenced pattern that owns filename */ + FcPattern *pattern; /* Referenced pattern that owns filename */ PangoCoverage *coverage; + PangoLanguage **languages; hb_face_t *hb_face; }; @@ -307,6 +308,8 @@ pango_fc_font_face_data_free (PangoFcFontFaceData *data) if (data->coverage) pango_coverage_unref (data->coverage); + g_free (data->languages); + hb_face_destroy (data->hb_face); g_slice_free (PangoFcFontFaceData, data); @@ -817,8 +820,15 @@ pango_fc_patterns_get_pattern (PangoFcPatterns *pats) } static gboolean -pango_fc_is_supported_font_format (const char *fontformat) +pango_fc_is_supported_font_format (FcPattern* pattern) { + FcResult res; + const char *fontformat; + + res = FcPatternGetString (pattern, FC_FONTFORMAT, 0, (FcChar8 **)(void*)&fontformat); + if (res != FcResultMatch) + return FALSE; + /* harfbuzz supports only SFNT fonts. */ /* FIXME: "CFF" is used for both CFF in OpenType and bare CFF files, but * HarfBuzz does not support the later and FontConfig does not seem @@ -840,12 +850,11 @@ filter_fontset_by_format (FcFontSet *fontset) for (i = 0; i < fontset->nfont; i++) { - FcResult res; - const char *s; - - res = FcPatternGetString (fontset->fonts[i], FC_FONTFORMAT, 0, (FcChar8 **)(void*)&s); - if (res == FcResultMatch && pango_fc_is_supported_font_format (s)) - FcFontSetAdd (result, FcPatternDuplicate (fontset->fonts[i])); + if (pango_fc_is_supported_font_format (fontset->fonts[i])) + { + FcPatternReference (fontset->fonts[i]); + FcFontSetAdd (result, fontset->fonts[i]); + } } return result; @@ -860,34 +869,37 @@ pango_fc_patterns_get_font_pattern (PangoFcPatterns *pats, int i, gboolean *prep if (!pats->match && !pats->fontset) pats->match = FcFontMatch (pats->fontmap->priv->config, pats->pattern, &result); - if (pats->match) + if (pats->match && pango_fc_is_supported_font_format (pats->match)) { *prepare = FALSE; return pats->match; } } - else + + if (!pats->fontset) { - if (!pats->fontset) + FcResult result; + FcFontSet *filtered[2] = { NULL, }; + int i, n = 0; + + for (i = 0; i < 2; i++) { - FcResult result; - FcFontSet *fontset; - FcFontSet *filtered; + FcFontSet *fonts = FcConfigGetFonts (pats->fontmap->priv->config, i); + if (fonts) + filtered[n++] = filter_fontset_by_format (fonts); + } - fontset = FcFontSort (pats->fontmap->priv->config, pats->pattern, FcFalse, NULL, &result); - filtered = filter_fontset_by_format (fontset); - FcFontSetDestroy (fontset); + pats->fontset = FcFontSetSort (pats->fontmap->priv->config, filtered, n, pats->pattern, FcTrue, NULL, &result); - pats->fontset = FcFontSetSort (pats->fontmap->priv->config, &filtered, 1, pats->pattern, FcTrue, NULL, &result); + for (i = 0; i < n; i++) + FcFontSetDestroy (filtered[i]); - FcFontSetDestroy (filtered); - if (pats->match) - { - FcPatternDestroy (pats->match); - pats->match = NULL; - } - } + if (pats->match) + { + FcPatternDestroy (pats->match); + pats->match = NULL; + } } *prepare = TRUE; @@ -1447,8 +1459,7 @@ ensure_families (PangoFcFontMap *fcfontmap) int variable; PangoFcFamily *temp_family; - res = FcPatternGetString (fontset->fonts[i], FC_FONTFORMAT, 0, (FcChar8 **)(void*)&s); - if (res != FcResultMatch || !pango_fc_is_supported_font_format (s)) + if (!pango_fc_is_supported_font_format (fontset->fonts[i])) continue; res = FcPatternGetString (fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **)(void*)&s); @@ -1937,14 +1948,12 @@ pango_fc_fontset_cache (PangoFcFontset *fontset, { /* Add to cache initially */ -#if 1 if (cache->length == FONTSET_CACHE_SIZE) { PangoFcFontset *tmp_fontset = g_queue_pop_tail (cache); tmp_fontset->cache_link = NULL; g_hash_table_remove (priv->fontset_hash, tmp_fontset->key); } -#endif fontset->cache_link = g_list_prepend (NULL, fontset); } @@ -2264,6 +2273,57 @@ _pango_fc_font_map_fc_to_coverage (FcCharSet *charset) return (PangoCoverage *)coverage; } +static PangoLanguage ** +_pango_fc_font_map_fc_to_languages (FcLangSet *langset) +{ + FcStrSet *strset; + FcStrList *list; + FcChar8 *s; + GArray *langs; + + langs = g_array_new (TRUE, FALSE, sizeof (PangoLanguage *)); + + strset = FcLangSetGetLangs (langset); + list = FcStrListCreate (strset); + + FcStrListFirst (list); + while ((s = FcStrListNext (list))) + { + PangoLanguage *l = pango_language_from_string ((const char *)s); + g_array_append_val (langs, l); + } + + FcStrListDone (list); + FcStrSetDestroy (strset); + + return (PangoLanguage **) g_array_free (langs, FALSE); +} + +PangoLanguage ** +_pango_fc_font_map_get_languages (PangoFcFontMap *fcfontmap, + PangoFcFont *fcfont) +{ + PangoFcFontFaceData *data; + FcLangSet *langset; + + data = pango_fc_font_map_get_font_face_data (fcfontmap, fcfont->font_pattern); + if (G_UNLIKELY (!data)) + return NULL; + + if (G_UNLIKELY (data->languages == NULL)) + { + /* + * Pull the languages out of the pattern, this + * doesn't require loading the font + */ + if (FcPatternGetLangSet (fcfont->font_pattern, FC_LANG, 0, &langset) != FcResultMatch) + return NULL; + + data->languages = _pango_fc_font_map_fc_to_languages (langset); + } + + return data->languages; +} /** * pango_fc_font_map_create_context: * @fcfontmap: a #PangoFcFontMap @@ -2867,6 +2927,12 @@ pango_fc_family_list_faces (PangoFontFamily *family, { PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family); + if (faces) + *faces = NULL; + + if (n_faces) + *n_faces = 0; + if (G_UNLIKELY (!fcfamily->fontmap)) return; diff --git a/pango/pangofc-private.h b/pango/pangofc-private.h index 27b96df4..7e216ed9 100644 --- a/pango/pangofc-private.h +++ b/pango/pangofc-private.h @@ -78,6 +78,9 @@ _PANGO_EXTERN PangoFontMetrics *pango_fc_font_create_base_metrics_for_context (PangoFcFont *font, PangoContext *context); +PangoLanguage **_pango_fc_font_map_get_languages (PangoFcFontMap *fcfontmap, + PangoFcFont *fcfont); + G_END_DECLS #endif /* __PANGOFC_PRIVATE_H__ */ diff --git a/pango/pangofc-shape.c b/pango/pangofc-shape.c index 9fe193f6..b6f74ca1 100644 --- a/pango/pangofc-shape.c +++ b/pango/pangofc-shape.c @@ -29,7 +29,7 @@ #include "pangohb-private.h" #include "pango-impl-utils.h" -#include <hb-glib.h> +#include <glib.h> /* cache a single hb_buffer_t */ static hb_buffer_t *cached_buffer = NULL; /* MT-safe */ @@ -359,7 +359,7 @@ pango_hb_shape (PangoFont *font, /* setup buffer */ hb_buffer_set_direction (hb_buffer, hb_direction); - hb_buffer_set_script (hb_buffer, hb_glib_script_to_script (analysis->script)); + hb_buffer_set_script (hb_buffer, (hb_script_t) g_unicode_script_to_iso15924 (analysis->script)); hb_buffer_set_language (hb_buffer, hb_language_from_string (pango_language_to_string (analysis->language), -1)); hb_buffer_set_cluster_level (hb_buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); hb_buffer_set_flags (hb_buffer, hb_buffer_flags); diff --git a/pango/pangowin32-fontmap.c b/pango/pangowin32-fontmap.c index b43685c7..6bc10a7a 100644 --- a/pango/pangowin32-fontmap.c +++ b/pango/pangowin32-fontmap.c @@ -1016,44 +1016,51 @@ pango_win32_font_map_load_font (PangoFontMap *fontmap, const PangoFontDescription *description) { PangoWin32FontMap *win32fontmap = (PangoWin32FontMap *)fontmap; - PangoWin32Family *win32family; + PangoWin32Face *best_match = NULL; PangoFont *result = NULL; GSList *tmp_list; const char *family; + char **families; + int i; g_return_val_if_fail (description != NULL, NULL); family = pango_font_description_get_family (description); - family = family ? family : ""; - PING (("name=%s", family)); + families = g_strsplit (family ? family : "", ",", -1); - win32family = g_hash_table_lookup (win32fontmap->families, family); - if (win32family) + for (i = 0; families[i] && !best_match; ++i) { - PangoWin32Face *best_match = NULL; + PangoWin32Family *win32family; + PING (("name=%s", families[i])); - PING (("got win32family")); - tmp_list = win32family->faces; - while (tmp_list) + win32family = g_hash_table_lookup (win32fontmap->families, families[i]); + if (win32family) { - PangoWin32Face *face = tmp_list->data; + PING (("got win32family")); + tmp_list = win32family->faces; + while (tmp_list) + { + PangoWin32Face *face = tmp_list->data; - if (pango_font_description_better_match (description, - best_match ? best_match->description : NULL, - face->description)) - best_match = face; + if (pango_font_description_better_match (description, + best_match ? best_match->description : NULL, + face->description)) + best_match = face; - tmp_list = tmp_list->next; + tmp_list = tmp_list->next; + } } + } + + g_strfreev (families); - if (best_match) - result = PANGO_WIN32_FONT_MAP_GET_CLASS (win32fontmap)->find_font (win32fontmap, context, - best_match, - description); + if (best_match) + result = PANGO_WIN32_FONT_MAP_GET_CLASS (win32fontmap)->find_font (win32fontmap, context, + best_match, + description); /* TODO: Handle the case that result == NULL. */ - else - PING (("no best match!")); - } + else + PING (("no best match!")); return result; } diff --git a/pango/pangowin32.c b/pango/pangowin32.c index 8849d4af..213a665e 100644 --- a/pango/pangowin32.c +++ b/pango/pangowin32.c @@ -269,7 +269,7 @@ pango_win32_render (HDC hdc, dX = g_new (INT, glyphs->num_glyphs); /* Render glyphs using one ExtTextOutW() call for each run of glyphs - * that have the same y offset. The big majoroty of glyphs will have + * that have the same y offset. The big majority of glyphs will have * y offset of zero, so in general, the whole glyph string will be * rendered by one call to ExtTextOutW(). * diff --git a/subprojects/freetype2.wrap b/subprojects/freetype2.wrap index 613f7d62..21ed35ce 100644 --- a/subprojects/freetype2.wrap +++ b/subprojects/freetype2.wrap @@ -8,3 +8,5 @@ source_hash = ec391504e55498adceb30baceebd147a6e963f636eb617424bcfc47a169898ce patch_url = https://wrapdb.mesonbuild.com/v1/projects/freetype2/2.9.1/1/get_zip patch_filename = freetype2-2.9.1-1-wrap.zip patch_hash = 06222607775e707c6d7b8d21ffdb04c7672f676a18c5ebb9880545130ab0407b + +depth=1 diff --git a/subprojects/fribidi.wrap b/subprojects/fribidi.wrap index 8d4e4bf4..0132d4ec 100644 --- a/subprojects/fribidi.wrap +++ b/subprojects/fribidi.wrap @@ -3,3 +3,4 @@ directory=fribidi url=https://github.com/fribidi/fribidi.git push-url=git@github.com:fribidi/fribidi.git revision=master +depth=1 diff --git a/subprojects/glib.wrap b/subprojects/glib.wrap index 76aa0a02..e2e44d56 100644 --- a/subprojects/glib.wrap +++ b/subprojects/glib.wrap @@ -3,3 +3,4 @@ directory=glib url=https://gitlab.gnome.org/GNOME/glib.git push-url=ssh://git@gitlab.gnome.org:GNOME/glib.git revision=master +depth=1 diff --git a/subprojects/harfbuzz.wrap b/subprojects/harfbuzz.wrap index e739b5e7..6fdcb633 100644 --- a/subprojects/harfbuzz.wrap +++ b/subprojects/harfbuzz.wrap @@ -3,3 +3,4 @@ directory=harfbuzz url=https://github.com/harfbuzz/harfbuzz.git push-url=git@github.com:harfbuzz/harfbuzz.git revision=master +depth=1 diff --git a/subprojects/sysprof.wrap b/subprojects/sysprof.wrap new file mode 100644 index 00000000..fb669463 --- /dev/null +++ b/subprojects/sysprof.wrap @@ -0,0 +1,5 @@ +[wrap-git] +directory=sysprof +url=https://gitlab.gnome.org/GNOME/sysprof.git +revision=6b1cd7a722fcebae1ac392562c47957477ade8bf +depth=1 diff --git a/tests/layouts/valid-6.expected b/tests/layouts/valid-6.expected new file mode 100644 index 00000000..b8f90c34 --- /dev/null +++ b/tests/layouts/valid-6.expected @@ -0,0 +1,25 @@ + 0️⃣ Keycap Digit Zero + +--- parameters + +wrapped: 0 +ellipsized: 0 +lines: 2 + +--- attributes + +range 0 2147483647 + +--- lines + +i=1, index=0, paragraph-start=1, dir=ltr ' 0️⃣ Keycap Digit Zero +' +i=2, index=27, paragraph-start=1, dir=ltr '' + +--- runs + +i=1, index=0, chars=1, level=0, gravity=south, flags=0, font=OMITTED, script=latin, language=en-us, ' ' +i=2, index=1, chars=3, level=0, gravity=south, flags=0, font=OMITTED, script=latin, language=en-us, '0️⃣' +i=3, index=8, chars=18, level=0, gravity=south, flags=0, font=OMITTED, script=latin, language=en-us, ' Keycap Digit Zero' +i=4, index=26, no run, line end +i=5, index=27, no run, line end diff --git a/tests/layouts/valid-6.markup b/tests/layouts/valid-6.markup new file mode 100644 index 00000000..92c53e28 --- /dev/null +++ b/tests/layouts/valid-6.markup @@ -0,0 +1,2 @@ + + 0️⃣ Keycap Digit Zero diff --git a/tests/meson.build b/tests/meson.build index 234fbf63..6e10e2b7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -20,11 +20,7 @@ tests = [ [ 'testboundaries' ], [ 'testboundaries_ucd' ], [ 'testcolor' ], - [ 'testmisc', [ 'testmisc.c' ], [ libpangocairo_dep ] ], - [ 'testscript' ], - [ 'test-harfbuzz', [ 'test-harfbuzz.c' ], [ libpangocairo_dep ] ], - [ 'cxx-test', [ 'cxx-test.cpp' ], [ libpangocairo_dep ] ], - [ 'test-break', [ 'test-break.c', 'test-common.c' ], [libpangocairo_dep ] ], + [ 'testscript' ] ] if build_pangoft2 @@ -45,6 +41,10 @@ if cairo_dep.found() [ 'test-shape', [ 'test-shape.c', 'test-common.c' ], [ libpangocairo_dep ] ], [ 'test-font', [ 'test-font.c' ], [ libpangocairo_dep ] ], [ 'testattributes', [ 'testattributes.c', 'test-common.c' ], [ libpangocairo_dep ] ], + [ 'testmisc', [ 'testmisc.c' ], [ libpangocairo_dep, glib_dep, harfbuzz_dep ] ], + [ 'cxx-test', [ 'cxx-test.cpp' ], [ libpangocairo_dep, gobject_dep, harfbuzz_dep ] ], + [ 'test-harfbuzz', [ 'test-harfbuzz.c' ], [ libpangocairo_dep, gobject_dep, harfbuzz_dep ] ], + [ 'test-break', [ 'test-break.c', 'test-common.c' ], [libpangocairo_dep, glib_dep, harfbuzz_dep ] ] ] if pango_cairo_backends.contains('png') diff --git a/tests/test-break.c b/tests/test-break.c index df9b78e2..c4359643 100644 --- a/tests/test-break.c +++ b/tests/test-break.c @@ -34,7 +34,7 @@ static PangoContext *context; -static void +static gboolean test_file (const gchar *filename, GString *string) { gchar *contents; @@ -62,7 +62,6 @@ test_file (const gchar *filename, GString *string) length = strlen (test); len = g_utf8_strlen (test, -1) + 1; - attrs = g_new (PangoLogAttr, len); pango_parse_markup (test, -1, 0, &attributes, &text, NULL, &error); g_assert_no_error (error); @@ -73,9 +72,17 @@ test_file (const gchar *filename, GString *string) if (pango_layout_get_unknown_glyphs_count (layout) > 0) { +#if 0 + // See https://github.com/mesonbuild/meson/issues/7515 char *msg = g_strdup_printf ("Missing glyphs - skipping %s. Maybe fonts are missing?", filename); g_test_skip (msg); g_free (msg); +#endif + g_free (contents); + g_object_unref (layout); + pango_attr_list_unref (attributes); + g_free (text); + return FALSE; } pango_layout_get_log_attrs (layout, &attrs, &len); @@ -221,7 +228,10 @@ test_file (const gchar *filename, GString *string) g_object_unref (layout); g_free (attrs); g_free (contents); + g_free (text); pango_attr_list_unref (attributes); + + return TRUE; } static gchar * @@ -249,26 +259,36 @@ test_break (gconstpointer d) GString *dump; gchar *diff; - const char *old_locale = setlocale (LC_ALL, NULL); + char *old_locale = g_strdup (setlocale (LC_ALL, NULL)); setlocale (LC_ALL, "en_US.utf8"); if (strstr (setlocale (LC_ALL, NULL), "en_US") == NULL) { +#if 0 + // See https://github.com/mesonbuild/meson/issues/7515 char *msg = g_strdup_printf ("Locale en_US.UTF-8 not available, skipping break %s", filename); g_test_skip (msg); g_free (msg); +#endif + g_free (old_locale); return; } - expected_file = get_expected_filename (filename); - dump = g_string_sized_new (0); - test_file (filename, dump); + if (!test_file (filename, dump)) + { + g_free (old_locale); + g_string_free (dump, TRUE); + return; + } + + expected_file = get_expected_filename (filename); diff = diff_with_file (expected_file, dump->str, dump->len, &error); g_assert_no_error (error); setlocale (LC_ALL, old_locale); + g_free (old_locale); if (diff && diff[0]) { @@ -284,9 +304,9 @@ test_break (gconstpointer d) g_test_fail (); g_strfreev (lines); - g_free (diff); } + g_free (diff); g_string_free (dump, TRUE); g_free (expected_file); } @@ -308,11 +328,26 @@ main (int argc, char *argv[]) /* allow to easily generate expected output for new test cases */ if (argc > 1) { - GString *string; + if (strcmp (argv[1], "--help") == 0) + { + g_print ("test-break uses the following symbols for log attrs\n\n"); + g_print ("Breaks: Words:\n" + " L - mandatory break b - word boundary\n" + " l - line break s - word start\n" + " c - char break e - word end\n" + "\n" + "Whitespace: Sentences:\n" + " x - expandable space s - sentence start\n" + " w - whitespace e - sentence end\n"); + } + else + { + GString *string; - string = g_string_sized_new (0); - test_file (argv[1], string); - g_test_message ("%s", string->str); + string = g_string_sized_new (0); + test_file (argv[1], string); + g_print ("%s", string->str); + } return 0; } diff --git a/tests/test-common.c b/tests/test-common.c index 786973f1..60ecb7e1 100644 --- a/tests/test-common.c +++ b/tests/test-common.c @@ -125,14 +125,22 @@ print_attribute (PangoAttribute *attr, GString *string) g_string_append_printf (string, "%d", ((PangoAttrInt *)attr)->value); break; case PANGO_ATTR_FONT_DESC: - g_string_append_printf (string, "%s", pango_font_description_to_string (((PangoAttrFontDesc *)attr)->desc)); + { + char *text = pango_font_description_to_string (((PangoAttrFontDesc *)attr)->desc); + g_string_append_printf (string, "%s", text); + g_free (text); + } break; case PANGO_ATTR_FOREGROUND: case PANGO_ATTR_BACKGROUND: case PANGO_ATTR_UNDERLINE_COLOR: case PANGO_ATTR_OVERLINE_COLOR: case PANGO_ATTR_STRIKETHROUGH_COLOR: - g_string_append_printf (string, "%s", pango_color_to_string (&((PangoAttrColor *)attr)->color)); + { + char *text = pango_color_to_string (&((PangoAttrColor *)attr)->color); + g_string_append_printf (string, "%s", text); + g_free (text); + } break; case PANGO_ATTR_SHAPE: g_string_append_printf (string, "shape"); diff --git a/tests/test-itemize.c b/tests/test-itemize.c index 3c58e18f..b22f3c2f 100644 --- a/tests/test-itemize.c +++ b/tests/test-itemize.c @@ -241,9 +241,12 @@ test_itemize (gconstpointer d) setlocale (LC_ALL, "en_US.utf8"); if (strstr (setlocale (LC_ALL, NULL), "en_US") == NULL) { +#if 0 + // See https://github.com/mesonbuild/meson/issues/7515 char *msg = g_strdup_printf ("Locale en_US.UTF-8 not available, skipping itemization %s", filename); g_test_skip (msg); g_free (msg); +#endif return; } @@ -274,9 +277,9 @@ test_itemize (gconstpointer d) g_test_fail (); g_strfreev (lines); - g_free (diff); } + g_free (diff); g_string_free (dump, TRUE); g_free (expected_file); } diff --git a/tests/test-layout.c b/tests/test-layout.c index 52617ce6..a5b6d279 100644 --- a/tests/test-layout.c +++ b/tests/test-layout.c @@ -238,6 +238,9 @@ test_file (const gchar *filename, GString *string) PangoWrapMode wrap = PANGO_WRAP_WORD; PangoFontDescription *desc; + if (context == NULL) + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + g_file_get_contents (filename, &contents, &length, &error); g_assert_no_error (error); @@ -308,9 +311,12 @@ test_layout (gconstpointer d) setlocale (LC_ALL, "en_US.utf8"); if (strstr (setlocale (LC_ALL, NULL), "en_US") == NULL) { +#if 0 + // See https://github.com/mesonbuild/meson/issues/7515 char *msg = g_strdup_printf ("Locale en_US.UTF-8 not available, skipping layout %s", filename); g_test_skip (msg); g_free (msg); +#endif return; } @@ -341,9 +347,9 @@ test_layout (gconstpointer d) g_test_fail (); g_strfreev (lines); - g_free (diff); } + g_free (diff); g_string_free (dump, TRUE); g_free (expected_file); } @@ -356,13 +362,13 @@ main (int argc, char *argv[]) const gchar *name; gchar *path; - g_test_init (&argc, &argv, NULL); - /* allow to easily generate expected output for new test cases */ - if (argc > 1) + if (argc > 1 && argv[1][0] != '-') { GString *string; + setlocale (LC_ALL, "en_US.utf8"); + string = g_string_sized_new (0); test_file (argv[1], string); g_test_message ("%s", string->str); @@ -370,6 +376,8 @@ main (int argc, char *argv[]) return 0; } + g_test_init (&argc, &argv, NULL); + path = g_test_build_filename (G_TEST_DIST, "layouts", NULL); dir = g_dir_open (path, 0, &error); g_free (path); diff --git a/tests/testattributes.c b/tests/testattributes.c index d6c8c87c..6131f38c 100644 --- a/tests/testattributes.c +++ b/tests/testattributes.c @@ -99,7 +99,12 @@ assert_attributes (GSList *attrs, s = g_string_new (""); print_attributes (attrs, s); - g_assert_cmpstr (s->str, ==, expected); + if (strcmp (s->str, expected) != 0) + { + g_print ("-----\nattribute list mismatch\nexpected:\n%s-----\nreceived:\n%s-----\n", + expected, s->str); + g_assert_not_reached (); + } g_string_free (s, TRUE); } @@ -298,6 +303,41 @@ test_list_splice (void) pango_attr_list_unref (base); } +/* Test that empty lists work in pango_attr_list_splice */ +static void +test_list_splice2 (void) +{ + PangoAttrList *list; + PangoAttrList *other; + PangoAttribute *attr; + + list = pango_attr_list_new (); + other = pango_attr_list_new (); + + pango_attr_list_splice (list, other, 11, 5); + + g_assert_null (pango_attr_list_get_attributes (list)); + + attr = pango_attr_size_new (10); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert (other, attr); + + pango_attr_list_splice (list, other, 11, 5); + + assert_attr_list (list, "[11,-1]size=10\n"); + + pango_attr_list_unref (other); + other = pango_attr_list_new (); + + pango_attr_list_splice (list, other, 11, 5); + + assert_attr_list (list, "[11,-1]size=10\n"); + + pango_attr_list_unref (other); + pango_attr_list_unref (list); +} + static gboolean never_true (PangoAttribute *attribute, gpointer user_data) { @@ -618,6 +658,20 @@ test_list_update (void) pango_attr_list_unref (list); } +/* Test that empty lists work in pango_attr_list_update */ +static void +test_list_update2 (void) +{ + PangoAttrList *list; + + list = pango_attr_list_new (); + pango_attr_list_update (list, 8, 10, 20); + + g_assert_null (pango_attr_list_get_attributes (list)); + + pango_attr_list_unref (list); +} + static void test_list_equal (void) { @@ -830,6 +884,154 @@ test_merge (void) pango_attr_list_unref (list2); } +/* reproduce what the links example in gtk4-demo does + * with the colored Google link + */ +static void +test_merge2 (void) +{ + PangoAttrList *list; + PangoAttribute *attr; + + list = pango_attr_list_new (); + attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + attr->start_index = 0; + attr->end_index = 10; + pango_attr_list_insert (list, attr); + attr = pango_attr_foreground_new (0, 0, 0xffff); + attr->start_index = 0; + attr->end_index = 10; + pango_attr_list_insert (list, attr); + + assert_attr_list (list, "[0,10]underline=1\n" + "[0,10]foreground=#00000000ffff\n"); + + attr = pango_attr_foreground_new (0xffff, 0, 0); + attr->start_index = 2; + attr->end_index = 3; + + pango_attr_list_change (list, attr); + + assert_attr_list (list, "[0,10]underline=1\n" + "[0,2]foreground=#00000000ffff\n" + "[2,3]foreground=#ffff00000000\n" + "[3,10]foreground=#00000000ffff\n"); + + attr = pango_attr_foreground_new (0, 0xffff, 0); + attr->start_index = 3; + attr->end_index = 4; + + pango_attr_list_change (list, attr); + + assert_attr_list (list, "[0,10]underline=1\n" + "[0,2]foreground=#00000000ffff\n" + "[2,3]foreground=#ffff00000000\n" + "[3,4]foreground=#0000ffff0000\n" + "[4,10]foreground=#00000000ffff\n"); + + attr = pango_attr_foreground_new (0, 0, 0xffff); + attr->start_index = 4; + attr->end_index = 5; + + pango_attr_list_change (list, attr); + + assert_attr_list (list, "[0,10]underline=1\n" + "[0,2]foreground=#00000000ffff\n" + "[2,3]foreground=#ffff00000000\n" + "[3,4]foreground=#0000ffff0000\n" + "[4,10]foreground=#00000000ffff\n"); + + pango_attr_list_unref (list); +} + +/* This only prints rise, size and scale, which are the + * only relevant attributes in the test that uses this + * function. + */ +static void +print_tags_for_attributes (PangoAttrIterator *iter, + GString *s) +{ + PangoAttribute *attr; + + attr = pango_attr_iterator_get (iter, PANGO_ATTR_RISE); + if (attr) + g_string_append_printf (s, "[%d, %d]rise=%d\n", + attr->start_index, attr->end_index, + ((PangoAttrInt*)attr)->value); + + attr = pango_attr_iterator_get (iter, PANGO_ATTR_SIZE); + if (attr) + g_string_append_printf (s, "[%d, %d]size=%d\n", + attr->start_index, attr->end_index, + ((PangoAttrInt*)attr)->value); + + attr = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE); + if (attr) + g_string_append_printf (s, "[%d, %d]scale=%f\n", + attr->start_index, attr->end_index, + ((PangoAttrFloat*)attr)->value); +} + +static void +test_iter_epsilon_zero (void) +{ + const char *markup = "𝜀<span rise=\"-6000\" size=\"x-small\" font_desc=\"italic\">0</span> = 𝜔<span rise=\"8000\" size=\"smaller\">𝜔<span rise=\"14000\" size=\"smaller\">𝜔<span rise=\"20000\">.<span rise=\"23000\">.<span rise=\"26000\">.</span></span></span></span></span>"; + PangoAttrList *attributes; + PangoAttrIterator *attr; + char *text; + GError *error = NULL; + GString *s; + + s = g_string_new (""); + + pango_parse_markup (markup, -1, 0, &attributes, &text, NULL, &error); + g_assert_no_error (error); + g_assert_cmpstr (text, ==, "𝜀0 = 𝜔𝜔𝜔..."); + + attr = pango_attr_list_get_iterator (attributes); + do + { + int start, end; + + pango_attr_iterator_range (attr, &start, &end); + + g_string_append_printf (s, "range: [%d, %d]\n", start, end); + + print_tags_for_attributes (attr, s); + } + while (pango_attr_iterator_next (attr)); + + g_assert_cmpstr (s->str, ==, + "range: [0, 4]\n" + "range: [4, 5]\n" + "[4, 5]rise=-6000\n" + "[4, 5]scale=0.694444\n" + "range: [5, 12]\n" + "range: [12, 16]\n" + "[12, 23]rise=8000\n" + "[12, 23]scale=0.833333\n" + "range: [16, 20]\n" + "[16, 23]rise=14000\n" + "[16, 23]scale=0.694444\n" + "range: [20, 21]\n" + "[20, 23]rise=20000\n" + "[16, 23]scale=0.694444\n" + "range: [21, 22]\n" + "[21, 23]rise=23000\n" + "[16, 23]scale=0.694444\n" + "range: [22, 23]\n" + "[22, 23]rise=26000\n" + "[16, 23]scale=0.694444\n" + "range: [23, 2147483647]\n"); + + g_free (text); + pango_attr_list_unref (attributes); + pango_attr_iterator_destroy (attr); + + g_string_free (s, TRUE); +} + int main (int argc, char *argv[]) { @@ -840,15 +1042,19 @@ main (int argc, char *argv[]) g_test_add_func ("/attributes/list/basic", test_list); g_test_add_func ("/attributes/list/change", test_list_change); g_test_add_func ("/attributes/list/splice", test_list_splice); + g_test_add_func ("/attributes/list/splice2", test_list_splice2); g_test_add_func ("/attributes/list/filter", test_list_filter); g_test_add_func ("/attributes/list/update", test_list_update); + g_test_add_func ("/attributes/list/update2", test_list_update2); g_test_add_func ("/attributes/list/equal", test_list_equal); g_test_add_func ("/attributes/list/insert", test_insert); g_test_add_func ("/attributes/list/merge", test_merge); + g_test_add_func ("/attributes/list/merge2", test_merge2); g_test_add_func ("/attributes/iter/basic", test_iter); g_test_add_func ("/attributes/iter/get", test_iter_get); g_test_add_func ("/attributes/iter/get_font", test_iter_get_font); g_test_add_func ("/attributes/iter/get_attrs", test_iter_get_attrs); + g_test_add_func ("/attributes/iter/epsilon_zero", test_iter_epsilon_zero); return g_test_run (); } diff --git a/tests/testboundaries_ucd.c b/tests/testboundaries_ucd.c index f77abdcd..1f0276e6 100644 --- a/tests/testboundaries_ucd.c +++ b/tests/testboundaries_ucd.c @@ -232,7 +232,9 @@ do_test (const gchar *filename, channel = g_io_channel_new_file (filename, "r", &error); if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { +#if 0 g_test_skip ("Test file not found"); +#endif return; } @@ -298,6 +300,7 @@ do_test (const gchar *filename, } g_free (string); g_free (expected_attrs); + g_free (line); i++; } diff --git a/tests/testcolor.c b/tests/testcolor.c index 36f2dbf3..62bbe4bf 100644 --- a/tests/testcolor.c +++ b/tests/testcolor.c @@ -25,57 +25,92 @@ typedef struct _ColorSpec { const gchar *spec; gboolean valid; + int color_or_alpha; guint16 red; guint16 green; guint16 blue; + guint16 alpha; } ColorSpec; -static gboolean test_one_color (ColorSpec *spec) +#define COLOR 1 +#define ALPHA 2 +#define BOTH 3 + +static void +test_one_color (ColorSpec *spec) { PangoColor color; gboolean accepted; + guint16 alpha; - accepted = pango_color_parse (&color, spec->spec); + if (spec->color_or_alpha & COLOR) + { + accepted = pango_color_parse (&color, spec->spec); - if (accepted == spec->valid && - (!accepted || - (color.red == spec->red && - color.green == spec->green && - color.blue == spec->blue))) - return TRUE; - else - return FALSE; -} + if (!spec->valid) + { + g_assert_false (accepted); + } + else + { + g_assert_true (accepted); + g_assert_cmpuint (color.red, ==, spec->red); + g_assert_cmpuint (color.green, ==, spec->green); + g_assert_cmpuint (color.blue, ==, spec->blue); + } + } + if (spec->color_or_alpha & ALPHA) + { + accepted = pango_color_parse_with_alpha (&color, &alpha, spec->spec); + + if (!spec->valid) + { + g_assert_false (accepted); + } + else + { + g_assert_true (accepted); + g_assert_cmpuint (color.red, ==, spec->red); + g_assert_cmpuint (color.green, ==, spec->green); + g_assert_cmpuint (color.blue, ==, spec->blue); + g_assert_cmpuint (alpha, ==, spec->alpha); + } + } +} ColorSpec specs [] = { - { "#abc", 1, 0xaaaa, 0xbbbb, 0xcccc }, - { "#aabbcc", 1, 0xaaaa, 0xbbbb, 0xcccc }, - { "#aaabbbccc", 1, 0xaaaa, 0xbbbb, 0xcccc }, - { "#100100100", 1, 0x1001, 0x1001, 0x1001 }, - { "#aaaabbbbcccc", 1, 0xaaaa, 0xbbbb, 0xcccc }, - { "#fff", 1, 0xffff, 0xffff, 0xffff }, - { "#ffffff", 1, 0xffff, 0xffff, 0xffff }, - { "#fffffffff", 1, 0xffff, 0xffff, 0xffff }, - { "#ffffffffffff", 1, 0xffff, 0xffff, 0xffff }, - { "#000", 1, 0x0000, 0x0000, 0x0000 }, - { "#000000", 1, 0x0000, 0x0000, 0x0000 }, - { "#000000000", 1, 0x0000, 0x0000, 0x0000 }, - { "#000000000000", 1, 0x0000, 0x0000, 0x0000 }, - { "#AAAABBBBCCCC", 1, 0xaaaa, 0xbbbb, 0xcccc }, - { "#aa bb cc ", 0, 0, 0, 0 }, - { "#aa bb ccc", 0, 0, 0, 0 }, - { "#ab", 0, 0, 0, 0 }, - { "#aabb", 0, 0, 0, 0 }, - { "#aaabb", 0, 0, 0, 0 }, - { "aaabb", 0, 0, 0, 0 }, - { "", 0, 0, 0, 0 }, - { "#", 0, 0, 0, 0 }, - { "##fff", 0, 0, 0, 0 }, - { "#0000ff+", 0, 0, 0, 0 }, - { "#0000f+", 0, 0, 0, 0 }, - { "#0x00x10x2", 0, 0, 0, 0 }, - { NULL, 0, 0, 0, 0 } + { "#abc", 1, BOTH, 0xaaaa, 0xbbbb, 0xcccc, 0xffff }, + { "#aabbcc", 1, BOTH, 0xaaaa, 0xbbbb, 0xcccc, 0xffff }, + { "#aaabbbccc", 1, BOTH, 0xaaaa, 0xbbbb, 0xcccc, 0xffff }, + { "#100100100", 1, BOTH, 0x1001, 0x1001, 0x1001, 0xffff }, + { "#aaaabbbbcccc", 1, COLOR, 0xaaaa, 0xbbbb, 0xcccc, 0xffff }, + { "#fff", 1, BOTH, 0xffff, 0xffff, 0xffff, 0xffff }, + { "#ffffff", 1, BOTH, 0xffff, 0xffff, 0xffff, 0xffff }, + { "#fffffffff", 1, BOTH, 0xffff, 0xffff, 0xffff, 0xffff }, + { "#ffffffffffff", 1, COLOR, 0xffff, 0xffff, 0xffff, 0xffff }, + { "#000", 1, BOTH, 0x0000, 0x0000, 0x0000, 0xffff }, + { "#000000", 1, BOTH, 0x0000, 0x0000, 0x0000, 0xffff }, + { "#000000000", 1, BOTH, 0x0000, 0x0000, 0x0000, 0xffff }, + { "#000000000000", 1, COLOR, 0x0000, 0x0000, 0x0000, 0xffff }, + { "#AAAABBBBCCCC", 1, COLOR, 0xaaaa, 0xbbbb, 0xcccc, 0xffff }, + { "#aa bb cc ", 0, BOTH, 0, 0, 0, 0 }, + { "#aa bb ccc", 0, BOTH, 0, 0, 0, 0 }, + { "#ab", 0, BOTH, 0, 0, 0, 0 }, + { "#aabb", 0, COLOR, 0, 0, 0, 0 }, + { "#aaabb", 0, BOTH, 0, 0, 0, 0 }, + { "aaabb", 0, BOTH, 0, 0, 0, 0 }, + { "", 0, BOTH, 0, 0, 0, 0 }, + { "#", 0, BOTH, 0, 0, 0, 0 }, + { "##fff", 0, BOTH, 0, 0, 0, 0 }, + { "#0000ff+", 0, BOTH, 0, 0, 0, 0 }, + { "#0000f+", 0, BOTH, 0, 0, 0, 0 }, + { "#0x00x10x2", 0, BOTH, 0, 0, 0, 0 }, + { "#abcd", 1, ALPHA, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd }, + { "#aabbccdd", 1, ALPHA, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd }, + { "#aaaabbbbccccdddd", + 1, ALPHA, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd }, + { NULL, 0, BOTH, 0, 0, 0, 0 } }; static void @@ -84,8 +119,7 @@ test_color (void) ColorSpec *spec; for (spec = specs; spec->spec; spec++) - g_assert (test_one_color (spec)); - + test_one_color (spec); } int diff --git a/tests/testmisc.c b/tests/testmisc.c index f5583cab..2f6c148b 100644 --- a/tests/testmisc.c +++ b/tests/testmisc.c @@ -54,6 +54,39 @@ test_itemize_empty_crash (void) g_object_unref (context); } +/* Test that pango_layout_set_text (layout, "short", 200) + * does not lead to a crash. (pidgin does this) + */ +static void +test_short_string_crash (void) +{ + PangoContext *context; + PangoLayout *layout; + int width, height; + + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + layout = pango_layout_new (context); + pango_layout_set_text (layout, "short text", 200); + pango_layout_get_pixel_size (layout, &width, &height); + + g_object_unref (layout); + g_object_unref (context); +} + +static void +test_language_emoji_crash (void) +{ + PangoLanguage *lang; + const PangoScript *scripts; + int num; + + lang = pango_language_from_string ("und-zsye"); + scripts = pango_language_get_scripts (lang, &num); + + g_assert (num >= 0); + g_assert (scripts == NULL || num > 0); +} + int main (int argc, char *argv[]) { @@ -61,6 +94,8 @@ main (int argc, char *argv[]) g_test_add_func ("/layout/shape-tab-crash", test_shape_tab_crash); g_test_add_func ("/layout/itemize-empty-crash", test_itemize_empty_crash); + g_test_add_func ("/layout/short-string-crash", test_short_string_crash); + g_test_add_func ("/language/emoji-crash", test_language_emoji_crash); return g_test_run (); } |