Add line breaking algorithm.

This commit is contained in:
Hermann Kraus 2012-07-19 02:30:27 +02:00
parent 27c5e50dcb
commit 525dad7623
4 changed files with 78 additions and 39 deletions

View file

@ -31,34 +31,14 @@
//stl //stl
#include <vector> #include <vector>
#include <list> #include <list>
#include <map>
//boost //boost
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
namespace mapnik namespace mapnik
{ {
#if 0
/** 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<glyph_info> 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<glyph_info> glyphs_;
double width_;
double text_height_;
double line_height_;
};
typedef boost::shared_ptr<format_run> format_run_ptr;
/** This class stores all format_runs in a line in left to right order. /** 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) * It can be used for rendering but no text processing (like line breaking)
@ -78,7 +58,7 @@ private:
}; };
typedef boost::shared_ptr<text_line> text_line_ptr; typedef boost::shared_ptr<text_line> text_line_ptr;
#endif
class text_layout class text_layout
{ {
@ -89,19 +69,27 @@ public:
itemizer.add_text(str, format); itemizer.add_text(str, format);
} }
void break_lines(); void break_lines(double break_width);
void shape_text(); void shape_text();
void clear(); void clear();
unsigned size() const { return glyphs_.size(); } unsigned size() const { return glyphs_.size(); }
typedef std::vector<glyph_info> glyph_vector; typedef std::vector<glyph_info> glyph_vector;
glyph_vector const& get_glyphs() const { return glyphs_; } 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: private:
text_itemizer itemizer; text_itemizer itemizer;
std::vector<text_line_ptr> lines_; // std::vector<text_line_ptr> 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<unsigned, double> width_map;
glyph_vector glyphs_; glyph_vector glyphs_;
face_manager_freetype &font_manager_; face_manager_freetype &font_manager_;
double total_width_;
double width_;
}; };
} }

View file

@ -263,6 +263,8 @@ bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement()
} }
placement_->properties.process(*layout_, feature_); placement_->properties.process(*layout_, feature_);
layout_->shape_text(); layout_->shape_text();
layout_->break_lines(placement_->properties.wrap_width);
//TODO //TODO
// info_ = &(text_.get_string_info()); // info_ = &(text_.get_string_info());
if (placement_->properties.orientation) if (placement_->properties.orientation)

View file

@ -46,6 +46,7 @@ std::list<text_item> const& text_itemizer::itemize()
// format itemiziation is done by add_text() // format itemiziation is done by add_text()
itemize_direction(); itemize_direction();
itemize_script(); itemize_script();
std::cout << "Itemizer: direction: "<< direction_runs.size() << " script: " << script_runs.size() << "\n";
create_item_list(); create_item_list();
return output; return output;
} }
@ -81,6 +82,7 @@ void text_itemizer::itemize_direction()
{ {
int32_t length; int32_t length;
direction = ubidi_getVisualRun(bidi, i, 0, &length); direction = ubidi_getVisualRun(bidi, i, 0, &length);
std::cout << "visual run" << direction << " length:" << length << "\n";
position += length; position += length;
direction_runs.push_back(direction_run_t(direction, position)); direction_runs.push_back(direction_run_t(direction, position));
} }

View file

@ -29,6 +29,9 @@
// harf-buzz // harf-buzz
#include <harfbuzz/hb.h> #include <harfbuzz/hb.h>
// ICU
#include <unicode/brkiter.h>
/* TODO: Remove unused classes: /* TODO: Remove unused classes:
* processed_text * processed_text
* string_info * 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<text.length(); i++)
{
std::cout << "i=" << i << "\n";
//TODO: Char spacing
std::map<unsigned, double>::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() void text_layout::shape_text()
@ -53,6 +94,8 @@ void text_layout::shape_text()
glyphs_.reserve(text.length()); //Preallocate memory glyphs_.reserve(text.length()); //Preallocate memory
total_width_ = 0.0;
std::list<text_item> const& list = itemizer.itemize(); std::list<text_item> const& list = itemizer.itemize();
std::list<text_item>::const_iterator itr = list.begin(), end = list.end(); std::list<text_item>::const_iterator itr = list.begin(), end = list.end();
for (;itr!=end; itr++) 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); hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL);
std::string s; 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; i<num_glyphs; i++) for (unsigned i=0; i<num_glyphs; i++)
{ {
@ -84,21 +127,25 @@ void text_layout::shape_text()
tmp.face = face; tmp.face = face;
tmp.format = itr->format; tmp.format = itr->format;
face->glyph_dimensions(tmp); face->glyph_dimensions(tmp);
width_map[glyphs[i].cluster] += tmp.width;
total_width_ += tmp.width;
glyphs_.push_back(tmp); 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::cout << "text_length: unicode chars: " << itemizer.get_text().length() << " glyphs: " << glyphs_.size() << "\n";
std::vector<glyph_info>::const_iterator itr2 = glyphs_.begin(), end2 = glyphs_.end(); // std::vector<glyph_info>::const_iterator itr2 = glyphs_.begin(), end2 = glyphs_.end();
for (;itr2 != end2; itr2++) // for (;itr2 != end2; itr2++)
{ // {
std::cout << "'" << (char) itemizer.get_text().charAt(itr2->char_index) << // std::cout << "'" << (char) itemizer.get_text().charAt(itr2->char_index) <<
"' glyph codepoint:" << itr2->glyph_index << // "' glyph codepoint:" << itr2->glyph_index <<
" cluster: " << itr2->char_index << // " cluster: " << itr2->char_index <<
" width: "<< itr2->width << // " width: "<< itr2->width <<
" height: " << itr2->height() << // " height: " << itr2->height() <<
"\n"; // "\n";
} // }
} }
void text_layout::clear() void text_layout::clear()