From d26346f3351f0a45583370366ad5de6d777e6e7e Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 25 Apr 2022 15:44:32 +0100 Subject: [PATCH] Fix label bounding box calculation to include glyph descenders [WIP] (ref #4308) --- include/mapnik/text/harfbuzz_shaper.hpp | 12 ++++++------ include/mapnik/text/icu_shaper.hpp | 10 +++++----- include/mapnik/text/text_layout.hpp | 2 ++ include/mapnik/text/text_line.hpp | 7 ++++++- src/text/placement_finder.cpp | 6 +++--- src/text/text_layout.cpp | 2 ++ src/text/text_line.cpp | 7 +++++-- 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/include/mapnik/text/harfbuzz_shaper.hpp b/include/mapnik/text/harfbuzz_shaper.hpp index bf60cf04a..916b6fa22 100644 --- a/include/mapnik/text/harfbuzz_shaper.hpp +++ b/include/mapnik/text/harfbuzz_shaper.hpp @@ -360,7 +360,8 @@ struct harfbuzz_shaper // Try next font in fontset continue; } - double max_glyph_height = 0; + double ymin = 0; + double ymax = 0; for (auto const& c_id : clusters) { auto const& c = glyphinfos[c_id]; @@ -382,19 +383,18 @@ struct harfbuzz_shaper // Overwrite default advance with better value provided by HarfBuzz g.unscaled_advance = gpos.x_advance; g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier); - double tmp_height = g.height(); + ymin = std::min(ymin, g.ymin()); + ymax = std::max(ymax, g.ymax()); if (g.face->is_color()) { - tmp_height = g.ymax(); + ymin = 0.0; } - if (tmp_height > max_glyph_height) - max_glyph_height = tmp_height; width_map[char_index] += g.advance(); line.add_glyph(std::move(g), scale_factor); } } } - line.update_max_char_height(max_glyph_height); + line.update_max_char_height(ymin, ymax); break; // When we reach this point the current font had all glyphs. } } diff --git a/include/mapnik/text/icu_shaper.hpp b/include/mapnik/text/icu_shaper.hpp index bf3a24436..702d9deee 100644 --- a/include/mapnik/text/icu_shaper.hpp +++ b/include/mapnik/text/icu_shaper.hpp @@ -93,7 +93,8 @@ struct icu_shaper std::size_t num_chars = static_cast(num_char); shaped.releaseBuffer(length); bool shaped_status = true; - double max_glyph_height = 0; + double ymin = 0; + double ymax = 0; if (U_SUCCESS(err) && (num_chars == length)) { unsigned char_index = 0; @@ -113,9 +114,8 @@ struct icu_shaper { g.face = face; g.scale_multiplier = size / face->get_face()->units_per_EM; - double tmp_height = g.height(); - if (tmp_height > max_glyph_height) - max_glyph_height = tmp_height; + ymin = std::min(ymin, g.ymin()); + ymax = std::max(ymax, g.ymax()); width_map[char_index++] += g.advance(); line.add_glyph(std::move(g), scale_factor); } @@ -123,7 +123,7 @@ struct icu_shaper } if (!shaped_status) continue; - line.update_max_char_height(max_glyph_height); + line.update_max_char_height(ymin, ymax); return; } } diff --git a/include/mapnik/text/text_layout.hpp b/include/mapnik/text/text_layout.hpp index 06e01d447..a4dbcab57 100644 --- a/include/mapnik/text/text_layout.hpp +++ b/include/mapnik/text/text_layout.hpp @@ -117,6 +117,7 @@ class text_layout inline rotation const& orientation() const { return orientation_; } inline pixel_position const& displacement() const { return displacement_; } + inline double base_ajustment() const { return baseline_adjustment_; } inline box2d const& bounds() const { return bounds_; } inline horizontal_alignment_e horizontal_alignment() const { return halign_; } pixel_position alignment_offset() const; @@ -173,6 +174,7 @@ class text_layout bool repeat_wrap_char_ = false; bool rotate_displacement_ = false; double text_ratio_ = 0.0; + double baseline_adjustment_ = 0.0; pixel_position displacement_ = {0, 0}; box2d bounds_ = {0, 0, 0, 0}; diff --git a/include/mapnik/text/text_line.hpp b/include/mapnik/text/text_line.hpp index a3997e461..9ec9f5d05 100644 --- a/include/mapnik/text/text_line.hpp +++ b/include/mapnik/text/text_line.hpp @@ -65,7 +65,11 @@ class MAPNIK_DECL text_line : util::noncopyable double max_char_height() const { return max_char_height_; } // Called for each font/style to update the maximum height of this line. - void update_max_char_height(double max_char_height); + void update_max_char_height(double ymin, double ymax); + + // Verticall adjustment + + double baseline_adjustment() const { return baseline_adjustment_;} // Line height including line spacing. double line_height() const { return line_height_; } @@ -88,6 +92,7 @@ class MAPNIK_DECL text_line : util::noncopyable glyph_vector glyphs_; double line_height_; // Includes line spacing (returned by freetype) double max_char_height_; // Max height of any glyphs in line - calculated by shaper + double baseline_adjustment_; // Adjustment to (0,0) origin double width_; double glyphs_width_; unsigned first_char_; diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index dd127b6b4..6098e60a2 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -172,7 +172,6 @@ bool placement_finder::find_point_placement(pixel_position const& pos) // Find text origin. pixel_position layout_center = pos + layout.displacement(); - if (!base_point_set) { glyphs->set_base_point(layout_center); @@ -180,9 +179,10 @@ bool placement_finder::find_point_placement(pixel_position const& pos) } box2d bbox = layout.bounds(); - bbox.re_center(layout_center.x, layout_center.y); - /* For point placements it is faster to just check the bounding box. */ + bbox.re_center(layout_center.x, layout_center.y - layout.base_ajustment()); + + // For point placements it is faster to just check the bounding box. if (collision(bbox, layouts_.text(), false)) return false; diff --git a/src/text/text_layout.cpp b/src/text/text_layout.cpp index e091e1429..99acfd07f 100644 --- a/src/text/text_layout.cpp +++ b/src/text/text_layout.cpp @@ -210,6 +210,7 @@ void text_layout::layout() displacement_ = scale_factor_ * displacement_ + alignment_offset(); if (rotate_displacement_) displacement_ = displacement_.rotate(!orientation_); + // Find layout bounds, expanded for rotation rotated_box2d(bounds_, orientation_, displacement_, width_, height_); } @@ -436,6 +437,7 @@ void text_layout::add_line(text_line&& line) line.set_first_line(true); } height_ += line.height(); + baseline_adjustment_ = line.baseline_adjustment(); glyphs_count_ += line.size(); width_ = std::max(width_, line.width()); lines_.emplace_back(std::move(line)); diff --git a/src/text/text_line.cpp b/src/text/text_line.cpp index 844fb9ed1..b72fa819f 100644 --- a/src/text/text_line.cpp +++ b/src/text/text_line.cpp @@ -30,6 +30,7 @@ text_line::text_line(unsigned first_char, unsigned last_char) : glyphs_() , line_height_(0.0) , max_char_height_(0.0) + , baseline_adjustment_(0.0) , width_(0.0) , glyphs_width_(0.0) , first_char_(first_char) @@ -42,6 +43,7 @@ text_line::text_line(text_line&& rhs) : glyphs_(std::move(rhs.glyphs_)) , line_height_(std::move(rhs.line_height_)) , max_char_height_(std::move(rhs.max_char_height_)) + , baseline_adjustment_(std::move(baseline_adjustment_)) , width_(std::move(rhs.width_)) , glyphs_width_(std::move(rhs.glyphs_width_)) , first_char_(std::move(rhs.first_char_)) @@ -92,9 +94,10 @@ double text_line::height() const return line_height_; } -void text_line::update_max_char_height(double max_char_height) +void text_line::update_max_char_height(double ymin, double ymax) { - max_char_height_ = std::max(max_char_height_, max_char_height); + max_char_height_ = std::max(max_char_height_, ymax - ymin); + baseline_adjustment_ = ymin; } void text_line::set_first_line(bool first_line)