From 525dad76236bba1f4f6f8e77149cf02e49fb9819 Mon Sep 17 00:00:00 2001 From: Hermann Kraus Date: Thu, 19 Jul 2012 02:30:27 +0200 Subject: [PATCH] Add line breaking algorithm. --- include/mapnik/text/layout.hpp | 38 ++++++----------- src/symbolizer_helpers.cpp | 2 + src/text/itemizer.cpp | 2 + src/text/layout.cpp | 75 +++++++++++++++++++++++++++------- 4 files changed, 78 insertions(+), 39 deletions(-) diff --git a/include/mapnik/text/layout.hpp b/include/mapnik/text/layout.hpp index 99300a998..0975b8a0a 100644 --- a/include/mapnik/text/layout.hpp +++ b/include/mapnik/text/layout.hpp @@ -31,34 +31,14 @@ //stl #include #include +#include //boost #include namespace mapnik { - -/** This class stores all glyphs in a format run (i.e. conscutive glyphs with the same format). */ -class format_run -{ -public: - format_run(char_properties_ptr properties, double text_height); - std::vector const& glyphs() const { return glyphs_; } - void add_glyph(glyph_info const& info); - double line_height() const { return line_height_; } - double text_height() const { return text_height_; } -private: - char_properties_ptr properties_; - std::vector glyphs_; - double width_; - double text_height_; - double line_height_; -}; - -typedef boost::shared_ptr format_run_ptr; - - - +#if 0 /** This class stores all format_runs in a line in left to right order. * * It can be used for rendering but no text processing (like line breaking) @@ -78,7 +58,7 @@ private: }; typedef boost::shared_ptr text_line_ptr; - +#endif class text_layout { @@ -89,19 +69,27 @@ public: itemizer.add_text(str, format); } - void break_lines(); + void break_lines(double break_width); void shape_text(); void clear(); unsigned size() const { return glyphs_.size(); } typedef std::vector glyph_vector; glyph_vector const& get_glyphs() const { return glyphs_; } + /** Get the text width. Returns 0 if shape_text() wasn't called before. + * If break_lines was already called the width of the longest line is returned. + **/ + double get_width() const { return width_; } private: text_itemizer itemizer; - std::vector lines_; +// std::vector lines_; + /// Maps char index (UTF-16) to width. If multiple glyphs map to the same char the sum of all widths is used + std::map width_map; glyph_vector glyphs_; face_manager_freetype &font_manager_; + double total_width_; + double width_; }; } diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp index 7eab59b8c..1516c2939 100644 --- a/src/symbolizer_helpers.cpp +++ b/src/symbolizer_helpers.cpp @@ -263,6 +263,8 @@ bool text_symbolizer_helper::next_placement() } placement_->properties.process(*layout_, feature_); layout_->shape_text(); + layout_->break_lines(placement_->properties.wrap_width); + //TODO // info_ = &(text_.get_string_info()); if (placement_->properties.orientation) diff --git a/src/text/itemizer.cpp b/src/text/itemizer.cpp index 0ed200fda..8c0b42b6e 100644 --- a/src/text/itemizer.cpp +++ b/src/text/itemizer.cpp @@ -46,6 +46,7 @@ std::list const& text_itemizer::itemize() // format itemiziation is done by add_text() itemize_direction(); itemize_script(); + std::cout << "Itemizer: direction: "<< direction_runs.size() << " script: " << script_runs.size() << "\n"; create_item_list(); return output; } @@ -81,6 +82,7 @@ void text_itemizer::itemize_direction() { int32_t length; direction = ubidi_getVisualRun(bidi, i, 0, &length); + std::cout << "visual run" << direction << " length:" << length << "\n"; position += length; direction_runs.push_back(direction_run_t(direction, position)); } diff --git a/src/text/layout.cpp b/src/text/layout.cpp index 2833aa885..2d32f98d7 100644 --- a/src/text/layout.cpp +++ b/src/text/layout.cpp @@ -29,6 +29,9 @@ // harf-buzz #include +// ICU +#include + /* TODO: Remove unused classes: * processed_text * string_info @@ -43,8 +46,46 @@ text_layout::text_layout(face_manager_freetype &font_manager) { } -void text_layout::break_lines() +void text_layout::break_lines(double break_width) { + if (total_width_ < break_width || !break_width) return; + UnicodeString const& text = itemizer.get_text(); + Locale locale; //TODO: Is the default constructor correct? + UErrorCode status = U_ZERO_ERROR; + BreakIterator *breakitr = BreakIterator::createLineInstance(locale, status); + //Not breaking the text if an error occurs is probably the best thing we can do. + if (!U_SUCCESS(status)) return; + breakitr->setText(text); + unsigned current_line_length = 0; + unsigned last_break_position = 0; + for (unsigned i=0; i::const_iterator width_itr = width_map.find(i); + if (width_itr != width_map.end()) + { + current_line_length += width_itr->second; + } + if (current_line_length > break_width) + { + unsigned break_position = breakitr->preceding(i); + if (break_position <= last_break_position) + { + //A single word is longer than the maximum line width. + //Violate line width requirement and choose next break position + break_position = breakitr->following(i); + std::cout << "Line overflow!\n"; + } + //TODO: Add line + std::cout << "Line to long ("<< current_line_length << ") at "<< i << " going to " << break_position << ". Last break was at " << last_break_position << "\n"; + last_break_position = break_position; + i = break_position; + + current_line_length = 0; + } + } + } void text_layout::shape_text() @@ -53,6 +94,8 @@ void text_layout::shape_text() glyphs_.reserve(text.length()); //Preallocate memory + total_width_ = 0.0; + std::list const& list = itemizer.itemize(); std::list::const_iterator itr = list.begin(), end = list.end(); for (;itr!=end; itr++) @@ -71,7 +114,7 @@ void text_layout::shape_text() hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL); std::string s; - std::cout << "Processing item '" << text.tempSubStringBetween(itr->start, itr->end).toUTF8String(s) << "' (" << uscript_getName(itr->script) << "," << itr->end - itr->start << "," << num_glyphs << "," << itr->rtl << ")\n"; +// std::cout << "Processing item '" << text.tempSubStringBetween(itr->start, itr->end).toUTF8String(s) << "' (" << uscript_getName(itr->script) << "," << itr->end - itr->start << "," << num_glyphs << "," << itr->rtl << ")\n"; for (unsigned i=0; iformat; face->glyph_dimensions(tmp); + + width_map[glyphs[i].cluster] += tmp.width; + total_width_ += tmp.width; + glyphs_.push_back(tmp); - std::cout << "glyph:" << glyphs[i].mask << " xa:" << positions[i].x_advance << " ya:" << positions[i].y_advance << " xo:" << positions[i].x_offset << " yo:" << positions[i].y_offset << "\n"; +// std::cout << "glyph:" << glyphs[i].mask << " xa:" << positions[i].x_advance << " ya:" << positions[i].y_advance << " xo:" << positions[i].x_offset << " yo:" << positions[i].y_offset << "\n"; } } - std::cout << "text_length: unicode chars: " << itemizer.get_text().length() << " glyphs: " << glyphs_.size() << "\n"; - std::vector::const_iterator itr2 = glyphs_.begin(), end2 = glyphs_.end(); - for (;itr2 != end2; itr2++) - { - std::cout << "'" << (char) itemizer.get_text().charAt(itr2->char_index) << - "' glyph codepoint:" << itr2->glyph_index << - " cluster: " << itr2->char_index << - " width: "<< itr2->width << - " height: " << itr2->height() << - "\n"; - } +// std::cout << "text_length: unicode chars: " << itemizer.get_text().length() << " glyphs: " << glyphs_.size() << "\n"; +// std::vector::const_iterator itr2 = glyphs_.begin(), end2 = glyphs_.end(); +// for (;itr2 != end2; itr2++) +// { +// std::cout << "'" << (char) itemizer.get_text().charAt(itr2->char_index) << +// "' glyph codepoint:" << itr2->glyph_index << +// " cluster: " << itr2->char_index << +// " width: "<< itr2->width << +// " height: " << itr2->height() << +// "\n"; +// } } void text_layout::clear()