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
|
//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_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue