diff options
| author | Jabier Arraiza <jabier.arraiza@marker.es> | 2017-11-03 00:10:02 +0000 |
|---|---|---|
| committer | Jabier Arraiza <jabier.arraiza@marker.es> | 2017-11-03 00:10:02 +0000 |
| commit | d2df0412f728dd5bb54537dfdfe7c35b34d40e0e (patch) | |
| tree | e2703384779e83312c456399999997fcc289c5cf /src/libnrtype | |
| parent | Merge branch 'master' into powerpencil (diff) | |
| parent | change assignment to equality (diff) | |
| download | inkscape-d2df0412f728dd5bb54537dfdfe7c35b34d40e0e.tar.gz inkscape-d2df0412f728dd5bb54537dfdfe7c35b34d40e0e.zip | |
Merge branch 'master' into powerpencil
Diffstat (limited to 'src/libnrtype')
| -rw-r--r-- | src/libnrtype/FontFactory.cpp | 380 | ||||
| -rw-r--r-- | src/libnrtype/FontFactory.h | 6 | ||||
| -rw-r--r-- | src/libnrtype/FontInstance.cpp | 12 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Compute.cpp | 121 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Output.cpp | 18 |
5 files changed, 322 insertions, 215 deletions
diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index 5c6a96694..90df81261 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -17,8 +17,11 @@ #endif #include <glibmm/i18n.h> +#include <fontconfig/fontconfig.h> +#include <pango/pangofc-fontmap.h> #include <pango/pangoft2.h> #include <pango/pango-ot.h> +#include "io/sys.h" #include "libnrtype/FontFactory.h" #include "libnrtype/font-instance.h" #include "util/unordered-containers.h" @@ -89,26 +92,20 @@ font_factory::font_factory(void) : nbEnt(0), // Note: this "ents" cache only keeps fonts from being unreffed, does not speed up access maxEnt(32), ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))), - #ifdef USE_PANGO_WIN32 fontServer(pango_win32_font_map_for_display()), - fontContext(pango_win32_get_context()), pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)), hScreenDC(pango_win32_get_dc()), #else fontServer(pango_ft2_font_map_new()), - fontContext(0), #endif + fontContext(pango_font_map_create_context(fontServer)), fontSize(512), loadedPtr(new FaceMapType()) { -#ifdef USE_PANGO_WIN32 -#else +#ifndef USE_PANGO_WIN32 pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fontServer), 72, 72); - - fontContext = pango_font_map_create_context(fontServer); - pango_ft2_font_map_set_default_substitute(PANGO_FT2_FONT_MAP(fontServer), FactorySubstituteFunc, this, @@ -627,6 +624,154 @@ Glib::ustring extract_tag( guint32 *tag ) { return tag_name; } +// Extract which OpenType tables are in the font. A list of all tables (regardless of which script and langauge +// they are in) will be stored as a std::map in the openTypeTables field of the font_instance +// This Harfbuzz code replaces an earlier Pango version as the Pango functions are deprecated. +void extract_openTypeTables(font_instance *res) { + // Empty map... bitmap fonts seem to be loaded multiple times. + res->openTypeTables.clear(); + +#ifndef USE_PANGO_WIN32 + auto const face = hb_ft_face_create(res->theFace, NULL); + + // First time to get size of array + auto script_count = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL); + auto const scripts_hb = g_new(hb_tag_t, script_count + 1); + + // Second time to fill array (this two step process was not necessary with Pango). + hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &script_count, scripts_hb); + + for(unsigned int i = 0; i < script_count; ++i) { + auto language_count = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); + + if(language_count > 0) { + auto const languages_hb = g_new(hb_tag_t, language_count + 1); + hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &language_count, languages_hb); + + for(unsigned int j = 0; j < language_count; ++j) { + auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, NULL, NULL); + auto const features_hb = g_new(hb_tag_t, feature_count + 1); + hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, &feature_count, features_hb); + + for(unsigned int k = 0; k < feature_count; ++k) { + ++(res->openTypeTables[ extract_tag(&features_hb[k])]); + } + + g_free(features_hb); + } + + g_free(languages_hb); + } + else { + // Even if no languages are present there is still the default. + auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + 0, NULL, NULL); + auto const features_hb = g_new(hb_tag_t, feature_count + 1); + hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + 0, &feature_count, features_hb); + + for(unsigned int k = 0; k < feature_count; ++k) { + ++(res->openTypeTables[ extract_tag(&features_hb[k])]); + } + + g_free(features_hb); + } + } + +// TODO: Ideally, we should use the HB_VERSION_ATLEAST macro here, +// but this was only released in harfbuzz >= 0.9.30 +// #if HB_VERSION_ATLEAST(1,2,3) +#if HB_VERSION_MAJOR*10000 + HB_VERSION_MINOR*100 + HB_VERSION_MICRO >= 10203 + // Find glyphs in OpenType substitution tables ('gsub'). + // Note that pango's functions are just dummies. Must use harfbuzz. + + // Loop over all tables + for (auto table: res->openTypeTables) { + + // Only look at style substitution tables ('salt', 'ss01', etc. but not 'ssty'). + if (table.first == "salt" || + (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't') ) ) { + // std::cout << " Table: " << table.first << std::endl; + + Glib::ustring unicode_characters; + + unsigned int feature_index; + if ( hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB, + 0, // Assume one script exists with index 0 + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, + HB_TAG(table.first[0], + table.first[1], + table.first[2], + table.first[3]), + &feature_index ) ) { + + // std::cout << " Found feature, number: " << feature_index << std::endl; + unsigned int lookup_indexes[32]; + unsigned int lookup_count = 32; + int count = hb_ot_layout_feature_get_lookups (face, HB_OT_TAG_GSUB, + feature_index, + 0, // Start + &lookup_count, + lookup_indexes ); + // std::cout << " Lookup count: " << count << " total: " << lookup_count << std::endl; + + if (count > 0) { + hb_set_t* glyphs_before = NULL; // hb_set_create(); + hb_set_t* glyphs_input = hb_set_create(); + hb_set_t* glyphs_after = NULL; // hb_set_create(); + hb_set_t* glyphs_output = NULL; // hb_set_create(); + + // For now, just look at first index + hb_ot_layout_lookup_collect_glyphs (face, HB_OT_TAG_GSUB, + lookup_indexes[0], + glyphs_before, + glyphs_input, + glyphs_after, + glyphs_output ); + + hb_font_t *font = hb_font_create (face); + + // Without this, all functions return 0, etc. + hb_ft_font_set_funcs (font); + + hb_codepoint_t codepoint = -1; + while (hb_set_next (glyphs_input, &codepoint)) { + + // There is a unicode to glyph mapping function but not the inverse! + for (hb_codepoint_t unicode_i = 0; unicode_i < 0xffff; ++unicode_i) { + hb_codepoint_t glyph = 0; + hb_font_get_nominal_glyph (font, unicode_i, &glyph); + if ( glyph == codepoint) { + unicode_characters += (gunichar)unicode_i; + continue; + } + } + } + res->openTypeSubstitutions[table.first] = unicode_characters; + + hb_set_destroy (glyphs_input); + hb_font_destroy (font); + } + } else { + // std::cout << " Did not find '" << table.first << "'!" << std::endl; + } + } + } + // for (auto table: res->openTypeSubstitutions) { + // std::cout << table.first << ": " << table.second << std::endl; + // } +#else + std::cerr << "Requires Harfbuzz 1.2.3 for visualizing alternative glyph OpenType tables. " + << "Compiled with: " << HB_VERSION_STRING << "." << std::endl; +#endif + + hb_face_destroy (face); + g_free(scripts_hb); +#endif // USE_PANGO_WIN32 +} + font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) { #ifdef USE_PANGO_WIN32 @@ -685,157 +830,18 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) // no match if ( canFail ) { PANGO_DEBUG("falling back to 'sans-serif'\n"); - descr = pango_font_description_new(); - pango_font_description_set_family(descr,"sans-serif"); - res = Face(descr,false); - pango_font_description_free(descr); - } - } - - // Extract which OpenType tables are in the font. We'll make a list of all tables - // regardless of which script and langauge they are in. This Harfbuzz code replaces - // an earlier Pango version as the Pango functions are deprecated. - - // Empty map... bitmap fonts seem to be loaded multiple times. - res->openTypeTables.clear(); - - auto const face = hb_ft_face_create(res->theFace, NULL); - - // First time to get size of array - auto script_count = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL); - auto const scripts_hb = g_new(hb_tag_t, script_count + 1); - - // Second time to fill array (this two step process was not necessary with Pango). - hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &script_count, scripts_hb); - - for(unsigned int i = 0; i < script_count; ++i) { - auto language_count = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); - - if(language_count > 0) { - auto const languages_hb = g_new(hb_tag_t, language_count + 1); - hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &language_count, languages_hb); - - for(unsigned int j = 0; j < language_count; ++j) { - auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, NULL, NULL); - auto const features_hb = g_new(hb_tag_t, feature_count + 1); - hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, j, 0, &feature_count, features_hb); - - for(unsigned int k = 0; k < feature_count; ++k) { - ++(res->openTypeTables[ extract_tag(&features_hb[k])]); - } - - g_free(features_hb); - } - - g_free(languages_hb); - } - else { - // Even if no languages are present there is still the default. - auto feature_count = hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - 0, NULL, NULL); - auto const features_hb = g_new(hb_tag_t, feature_count + 1); - hb_ot_layout_language_get_feature_tags(face, HB_OT_TAG_GSUB, i, - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - 0, &feature_count, features_hb); - - for(unsigned int k = 0; k < feature_count; ++k) { - ++(res->openTypeTables[ extract_tag(&features_hb[k])]); - } - - g_free(features_hb); + PangoFontDescription *new_descr = pango_font_description_new(); + pango_font_description_set_family(new_descr, "sans-serif"); + res = Face(new_descr, false); + pango_font_description_free(new_descr); + } else { + g_critical("Could not load any face for font '%s'.", pango_font_description_to_string(descr)); } } -// TODO: Ideally, we should use the HB_VERSION_ATLEAST macro here, -// but this was only released in harfbuzz >= 0.9.30 -// #if HB_VERSION_ATLEAST(1,2,3) -#if HB_VERSION_MAJOR*10000 + HB_VERSION_MINOR*100 + HB_VERSION_MICRO >= 10203 - // Find glyphs in OpenType substitution tables ('gsub'). - // Note that pango's functions are just dummies. Must use harfbuzz. - - // Loop over all tables - for (auto table: res->openTypeTables) { - - // Only look at style substitution tables ('salt', 'ss01', etc. but not 'ssty'). - if (table.first == "salt" || - (table.first[0] == 's' && table.first[1] == 's' && !(table.first[2] == 't') ) ) { - // std::cout << " Table: " << table.first << std::endl; - - Glib::ustring unicode_characters; - - unsigned int feature_index; - if ( hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB, - 0, // Assume one script exists with index 0 - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, - HB_TAG(table.first[0], - table.first[1], - table.first[2], - table.first[3]), - &feature_index ) ) { - - // std::cout << " Found feature, number: " << feature_index << std::endl; - unsigned int lookup_indexes[32]; - unsigned int lookup_count = 32; - int count = hb_ot_layout_feature_get_lookups (face, HB_OT_TAG_GSUB, - feature_index, - 0, // Start - &lookup_count, - lookup_indexes ); - // std::cout << " Lookup count: " << count << " total: " << lookup_count << std::endl; - - if (count > 0) { - hb_set_t* glyphs_before = NULL; // hb_set_create(); - hb_set_t* glyphs_input = hb_set_create(); - hb_set_t* glyphs_after = NULL; // hb_set_create(); - hb_set_t* glyphs_output = NULL; // hb_set_create(); - - // For now, just look at first index - hb_ot_layout_lookup_collect_glyphs (face, HB_OT_TAG_GSUB, - lookup_indexes[0], - glyphs_before, - glyphs_input, - glyphs_after, - glyphs_output ); - - hb_font_t *font = hb_font_create (face); - - // Without this, all functions return 0, etc. - hb_ft_font_set_funcs (font); - - hb_codepoint_t codepoint = -1; - while (hb_set_next (glyphs_input, &codepoint)) { - - // There is a unicode to glyph mapping function but not the inverse! - for (hb_codepoint_t unicode_i = 0; unicode_i < 0xffff; ++unicode_i) { - hb_codepoint_t glyph = 0; - hb_font_get_nominal_glyph (font, unicode_i, &glyph); - if ( glyph == codepoint) { - unicode_characters += (gunichar)unicode_i; - continue; - } - } - } - res->openTypeSubstitutions[table.first] = unicode_characters; - - hb_set_destroy (glyphs_input); - hb_font_destroy (font); - } - } else { - // std::cout << " Did not find '" << table.first << "'!" << std::endl; - } - } + if (res) { + extract_openTypeTables(res); } - // for (auto table: res->openTypeSubstitutions) { - // std::cout << table.first << ": " << table.second << std::endl; - // } -#else - std::cerr << "Requires Harfbuzz 1.2.3 for visualizing alternative glyph OpenType tables. " - << "Compiled with: " << HB_VERSION_STRING << "." << std::endl; -#endif - - hb_face_destroy (face); - g_free(scripts_hb); } else { // already here res = loadedFaces[descr]; @@ -911,6 +917,70 @@ void font_factory::AddInCache(font_instance *who) nbEnt++; } +void font_factory::AddFontsDir(char const *utf8dir) +{ +#ifdef USE_PANGO_WIN32 + g_info("Adding additional font directories only supported for fontconfig backend."); +#else + if (!Inkscape::IO::file_test(utf8dir, G_FILE_TEST_IS_DIR)) { + g_warning("Fonts dir '%s' does not exist and will be ignored.", utf8dir); + return; + } + + gchar *dir; +# ifdef WIN32 + dir = g_win32_locale_filename_from_utf8(utf8dir); +# else + dir = g_filename_from_utf8(utf8dir, -1, NULL, NULL, NULL); +# endif + + FcConfig *conf = NULL; +# if PANGO_VERSION_CHECK(1,38,0) + conf = pango_fc_font_map_get_config(PANGO_FC_FONT_MAP(fontServer)); +# endif + FcBool res = FcConfigAppFontAddDir(conf, (FcChar8 const *)dir); + if (res == FcTrue) { + g_info("Fonts dir '%s' added successfully.", utf8dir); + } else { + g_warning("Could not add fonts dir '%s'.", utf8dir); + } + + g_free(dir); +#endif +} + +void font_factory::AddFontFile(char const *utf8file) +{ +#ifdef USE_PANGO_WIN32 + g_info("Adding additional font only supported for fontconfig backend."); +#else + if (!Inkscape::IO::file_test(utf8file, G_FILE_TEST_IS_REGULAR)) { + g_warning("Font file '%s' does not exist and will be ignored.", utf8file); + return; + } + + gchar *file; +# ifdef WIN32 + file = g_win32_locale_filename_from_utf8(utf8file); +# else + file = g_filename_from_utf8(utf8file, -1, NULL, NULL, NULL); +# endif + + FcConfig *conf = NULL; +# if PANGO_VERSION_CHECK(1,38,0) + conf = pango_fc_font_map_get_config(PANGO_FC_FONT_MAP(fontServer)); +# endif + FcBool res = FcConfigAppFontAddFile(conf, (FcChar8 const *)file); + if (res == FcTrue) { + g_info("Font file '%s' added successfully.", utf8file); + } else { + g_warning("Could not add font file '%s'.", utf8file); + } + + g_free(file); +#endif +} + /* Local Variables: mode:c++ diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 41c4cb6eb..12260f99a 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -136,6 +136,12 @@ public: // internal void AddInCache(font_instance *who); + /// Add a directory from which to include additional fonts + void AddFontsDir(char const *utf8dir); + + /// Add a an additional font. + void AddFontFile(char const *utf8file); + private: void* loadedPtr; diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index 7a16fc0c3..57aa22d48 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -556,10 +556,10 @@ bool font_instance::FontDecoration( double &underline_position, double &underl return false; } double scale=1.0/parent->fontSize; - underline_position = fabs(otm.otmUnderscorePosition *scale); - underline_thickness = fabs(otm.otmUnderscoreSize *scale); - linethrough_position = fabs(otm.otmStrikeoutPosition *scale); - linethrough_thickness = fabs(otm.otmStrikeoutSize *scale); + underline_position = fabs(otm.otmsUnderscorePosition *scale); + underline_thickness = fabs(otm.otmsUnderscoreSize *scale); + linethrough_position = fabs(otm.otmsStrikeoutPosition *scale); + linethrough_thickness = fabs(otm.otmsStrikeoutSize *scale); #else if ( theFace->units_per_EM == 0 ) { return false; // bitmap font @@ -683,12 +683,12 @@ void font_instance::FindFontMetrics() { if ( theFace ) { #ifdef USE_PANGO_WIN32 - + OUTLINETEXTMETRIC otm; if ( GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) { double scale=1.0/parent->fontSize; _ascent = fabs(otm.otmMacAscent * scale); _descent = fabs(otm.otmMacDescent * scale); - _xheight = fabs(otm.otmXHeight * scale); + _xheight = fabs(otm.otmsXHeight * scale); _ascent_max = fabs(otm.otmAscent * scale); _descent_max = fabs(otm.otmDescent * scale); diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 63364ab34..eca795994 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -253,8 +253,7 @@ class Layout::Calculator UnbrokenSpanPosition const &start_span_pos, ScanlineMaker::ScanRun const &scan_run, std::vector<ChunkInfo> *chunk_info, - FontMetrics *line_height, - FontMetrics const *strut_height) const; + FontMetrics *line_height) const; bool _measureUnbrokenSpan(ParagraphInfo const ¶, BrokenSpan *span, @@ -399,24 +398,46 @@ bool Layout::Calculator::_measureUnbrokenSpan(ParagraphInfo const ¶, double char_width = 0.0; while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) { + + PangoGlyphInfo *info = &(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index]); + // double glyph_width = font_size_multiplier * info->geometry.width; + // double glyph_x_offset = font_size_multiplier * info->geometry.x_offset; + // double glyph_y_offset = font_size_multiplier * info->geometry.y_offset; + // std::cout << " glyph: " << info->glyph << " width: " << glyph_width << " x_offset: " << glyph_x_offset << " y_offset: " << glyph_y_offset << std::endl; + + font_instance *font = para.pango_items[span->end.iter_span->pango_item_index].font; + double font_size = span->start.iter_span->font_size; + double glyph_h_advance = font_size * font->Advance(info->glyph, false); + double glyph_v_advance = font_size * font->Advance(info->glyph, true ); + // std::cout << " h_advance: " << glyph_h_advance << " v_advance: " << glyph_v_advance << std::endl; + // Geom::OptRect bbox = font->BBox(info->glyph); + // *bbox *= Geom::Scale(font_size); + // std::cout << " bbox: " << *bbox << std::endl; + // std::cout << " h_extent: " << bbox->width() << " v_extent: " << bbox->height() << std::endl; + if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { // Vertical text if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED && - para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) { + para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == PANGO_GRAVITY_SOUTH) ) { // Sideways orientation - char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false); + char_width += glyph_h_advance; } else { // Upright orientation - char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true); + guint32 c = *Glib::ustring::const_iterator(span->end.iter_span->input_stream_first_character.base() + span->end.char_byte); + if (g_unichar_type (c) != G_UNICODE_NON_SPACING_MARK) { + // Non-spacing marks should not contribute to width. Fonts may not report the correct advance, especially if the 'vmtx' table is missing. + char_width += glyph_v_advance; + } } } else { // Horizontal text - char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width; + char_width += font_size_multiplier * info->geometry.width; } span->end_glyph_index++; } + if (char_attributes.is_cursor_position) char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue(); if (char_attributes.is_white) @@ -435,6 +456,7 @@ bool Layout::Calculator::_measureUnbrokenSpan(ParagraphInfo const ¶, if (is_soft_hyphen) soft_hyphen_glyph_width = char_width; + // Go to next character (resets end.char_byte to zero if at end) span->end.increment(); // Width should not include letter_spacing (or word_spacing) after last letter at end of line. @@ -663,8 +685,8 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, new_span.block_progression = _block_progression; new_span.text_orientation = unbroken_span.text_orientation; if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) - { - new_span.font->Ref(); + { + new_span.font->Ref(); new_span.font_size = unbroken_span.font_size; new_span.direction = para.pango_items[unbroken_span.pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT; new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte); @@ -698,6 +720,7 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) { // the span is set up, push the glyphs and chars + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]); Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ; unsigned char_index_in_unbroken_span = it_span->start.char_index; @@ -708,9 +731,9 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) { unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); - int newcluster = 0; - if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){ - newcluster = 1; + bool newcluster = false; + if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start) { + newcluster = true; x_in_span = x_in_span_last; } @@ -750,10 +773,12 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, new_glyph.vertical_scale = 1.0; // Position glyph -------------------- - new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier; + new_glyph.x = current_x; new_glyph.y =_y_offset; - // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign + // y-coordinate is flipped between vertical and horizontal text... + // delta_y is common offset but applied with opposite sign + double delta_x = unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier; double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift; SPCSSBaseline dominant_baseline = _flow._blockBaseline(); @@ -767,41 +792,53 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; } - new_glyph.y += delta_y; - // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset... if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED && - para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) { + para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == PANGO_GRAVITY_SOUTH) ) { - // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz + // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. new_glyph.orientation = ORIENTATION_SIDEWAYS; - new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; + new_glyph.x += delta_x; + new_glyph.y -= delta_y; + + new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); } else { // Upright orientation - new_glyph.x += new_span.line_height.ascent; + new_glyph.x += delta_x; + new_glyph.y -= delta_y; - // Glyph reference point is center (shift: left edge to center glyph) - new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier; + // Adjust for alignment point (top of em box, horizontal center). + new_glyph.x += new_span.line_height.ascent; new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] - para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] ); - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true); - if( new_glyph.width == 0 ) { - new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; + static double shift_y = 0; // Save to use with non_spacing marks (should be shifted the same amount as previous glyph). + static double shift_x = 0; // Subtract incorrect Pango inclusion of horizontal advance (https://bugzilla.gnome.org 787526) + if (g_unichar_type (*iter_source_text) == G_UNICODE_NON_SPACING_MARK) { + new_glyph.width = 0; + new_glyph.x += shift_x; // Hack + shift_x = 0; + } else { + // Glyph reference point is center (shift: left edge to center glyph) + shift_y = unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier; + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true); + shift_x = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); } - + new_glyph.y -= shift_y; } } else { // Horizontal text if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; - new_glyph.y -= delta_y; + new_glyph.x += delta_x; + new_glyph.y += delta_y; + new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; @@ -810,7 +847,6 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info } - if (new_span.direction == RIGHT_TO_LEFT) { // pango wanted to give us glyphs in visual order but we refused, so we need to work // out where the cluster start is ourselves @@ -818,12 +854,7 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, for (unsigned rtl_index = glyph_index; rtl_index < it_span->end_glyph_index ; rtl_index++) { if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index) break; - if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) - // Vertical text - cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true); - else - // Horizontal text - cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width; + cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width; } new_glyph.x -= cluster_width; } @@ -831,8 +862,9 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, // create the Layout::Character(s) double advance_width = new_glyph.width; - if (newcluster){ - newcluster = 0; + if (newcluster) { + newcluster = false; + // find where the text ends for this log_cluster end_byte = it_span->start.iter_span->text_bytes; // Upper limit for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){ @@ -841,6 +873,7 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, break; } } + // Figure out how many glyphs and characters are in the log_cluster. log_cluster_size_glyphs = 0; log_cluster_size_chars = 0; @@ -848,6 +881,7 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, if(unbroken_span.glyph_string->log_clusters[glyph_index ] != unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break; } + Glib::ustring::const_iterator lclist = iter_source_text; unsigned lcb = char_byte; while(lcb < end_byte){ @@ -856,6 +890,7 @@ void Layout::Calculator::_outputLine(ParagraphInfo const ¶, lcb = lclist.base() - unbroken_span.input_stream_first_character.base(); } } + while (char_byte < end_byte) { /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining. When there are no ligatures these two sizes are always the same. @@ -1472,8 +1507,8 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, TRACE((" initial line_box_height (em size): %f\n", line_box_height->emSize() )); UnbrokenSpanPosition span_pos; + static int trys = 0; for( ; ; ) { - // Get regions where one can place one line of text (can be more than one, if filling a // donut for example). std::vector<ScanlineMaker::ScanRun> scan_runs; @@ -1495,10 +1530,9 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, unsigned scan_run_index; span_pos = *start_span_pos; for (scan_run_index = 0 ; scan_run_index < scan_runs.size() ; scan_run_index++) { - // Returns false if some text in line requires a taller line_box_height. // (We try again with a larger line_box_height.) - if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) { + if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height)) { break; } @@ -1534,11 +1568,12 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, UnbrokenSpanPosition const &start_span_pos, ScanlineMaker::ScanRun const &scan_run, std::vector<ChunkInfo> *chunk_info, - FontMetrics *line_height, - FontMetrics const *strut_height) const + FontMetrics *line_height) const { TRACE((" begin _buildChunksInScanRun: chunks: %lu, em size: %f\n", chunk_info->size(), line_height->emSize() )); + FontMetrics line_height_saved = *line_height; // Store for recalculating line height if chunks are backed out + ChunkInfo new_chunk; new_chunk.text_width = 0.0; new_chunk.whitespace_count = 0; @@ -1647,7 +1682,7 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, } // Recalculate line_box_height after backing out chunks - *line_height = *strut_height; + *line_height = line_height_saved; for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info->begin() ; it_chunk != chunk_info->end() ; it_chunk++) { for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { FontMetrics span_height = it_span->start.iter_span->line_height; @@ -1751,6 +1786,10 @@ bool Layout::Calculator::calculate() if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) { // Vertical text, CJK pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_EAST); + + if( _flow._blockTextOrientation() == SP_CSS_TEXT_ORIENTATION_UPRIGHT ) { + pango_context_set_gravity_hint(_pango_context, PANGO_GRAVITY_HINT_STRONG); + } } else { // Horizontal text pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_AUTO); diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index 7f20dee95..9a73b9eca 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -800,7 +800,7 @@ void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path) SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const { - GSList *cc = NULL; + std::list<SPCurve *> cc; for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) { Geom::Affine glyph_matrix; @@ -811,22 +811,14 @@ SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_ if (pathv) { Geom::PathVector pathv_trans = (*pathv) * glyph_matrix; SPCurve *c = new SPCurve(pathv_trans); - if (c) cc = g_slist_prepend(cc, c); + if (c) cc.push_back(c); } } - cc = g_slist_reverse(cc); + SPCurve *curve = new SPCurve(cc); - SPCurve *curve; - if ( cc ) { - curve = SPCurve::concat(cc); - } else { - curve = new SPCurve(); - } - - while (cc) { + for (auto i:cc) { /* fixme: This is dangerous, as we are mixing art_alloc and g_new */ - reinterpret_cast<SPCurve *>(cc->data)->unref(); - cc = g_slist_remove(cc, cc->data); + i->unref(); } return curve; |
