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
#include <vector>
#include <list>
#include <map>
//boost
#include <boost/shared_ptr.hpp>
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<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;
#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> 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_info> 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<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_;
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_);
layout_->shape_text();
layout_->break_lines(placement_->properties.wrap_width);
//TODO
// info_ = &(text_.get_string_info());
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()
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));
}

View file

@ -29,6 +29,9 @@
// harf-buzz
#include <harfbuzz/hb.h>
// ICU
#include <unicode/brkiter.h>
/* 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<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()
@ -53,6 +94,8 @@ void text_layout::shape_text()
glyphs_.reserve(text.length()); //Preallocate memory
total_width_ = 0.0;
std::list<text_item> const& list = itemizer.itemize();
std::list<text_item>::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; i<num_glyphs; i++)
{
@ -84,21 +127,25 @@ void text_layout::shape_text()
tmp.face = face;
tmp.format = itr->format;
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<glyph_info>::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<glyph_info>::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()