Add line breaking algorithm.
This commit is contained in:
parent
27c5e50dcb
commit
525dad7623
4 changed files with 78 additions and 39 deletions
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue