summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
authorJabier Arraiza <jabier.arraiza@marker.es>2017-11-03 00:10:02 +0000
committerJabier Arraiza <jabier.arraiza@marker.es>2017-11-03 00:10:02 +0000
commitd2df0412f728dd5bb54537dfdfe7c35b34d40e0e (patch)
treee2703384779e83312c456399999997fcc289c5cf /src/libnrtype
parentMerge branch 'master' into powerpencil (diff)
parentchange assignment to equality (diff)
downloadinkscape-d2df0412f728dd5bb54537dfdfe7c35b34d40e0e.tar.gz
inkscape-d2df0412f728dd5bb54537dfdfe7c35b34d40e0e.zip
Merge branch 'master' into powerpencil
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontFactory.cpp380
-rw-r--r--src/libnrtype/FontFactory.h6
-rw-r--r--src/libnrtype/FontInstance.cpp12
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp121
-rw-r--r--src/libnrtype/Layout-TNG-Output.cpp18
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 &para,
BrokenSpan *span,
@@ -399,24 +398,46 @@ bool Layout::Calculator::_measureUnbrokenSpan(ParagraphInfo const &para,
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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
// 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 &para,
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 &para,
// 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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
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 &para,
}
// 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;