227 lines
8.2 KiB
C++
227 lines
8.2 KiB
C++
/*****************************************************************************
|
|
*
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
*
|
|
* Copyright (C) 2014 Artem Pavlenko
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*****************************************************************************/
|
|
#ifndef MAPNIK_TEXT_LAYOUT_HPP
|
|
#define MAPNIK_TEXT_LAYOUT_HPP
|
|
|
|
//mapnik
|
|
#include <mapnik/value_types.hpp>
|
|
#include <mapnik/pixel_position.hpp>
|
|
#include <mapnik/box2d.hpp>
|
|
#include <mapnik/attribute.hpp>
|
|
#include <mapnik/symbolizer_enumerations.hpp>
|
|
#include <mapnik/text/text_properties.hpp>
|
|
#include <mapnik/text/text_line.hpp>
|
|
#include <mapnik/text/itemizer.hpp>
|
|
#include <mapnik/font_engine_freetype.hpp>
|
|
#include <mapnik/text/evaluated_format_properties_ptr.hpp>
|
|
#include <mapnik/text/rotation.hpp>
|
|
|
|
//stl
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
namespace mapnik
|
|
{
|
|
|
|
class feature_impl;
|
|
class text_layout;
|
|
|
|
using text_layout_ptr = std::shared_ptr<text_layout>;
|
|
using text_layout_vector = std::vector<text_layout_ptr>;
|
|
// this is a std::deque to ensure pointers stay valid as a deque
|
|
// "never invalidates pointers or references to the rest of the elements"
|
|
// http://en.cppreference.com/w/cpp/container/deque
|
|
// If this were a vector this test would crash:
|
|
// python tests/visual_tests/test.py text-expressionformat-color
|
|
using child_format_ptrs = std::deque<evaluated_format_properties_ptr>;
|
|
|
|
class text_layout
|
|
{
|
|
public:
|
|
using line_vector = std::vector<text_line>;
|
|
using const_iterator = line_vector::const_iterator;
|
|
using child_iterator = text_layout_vector::const_iterator;
|
|
|
|
text_layout(face_manager_freetype & font_manager,
|
|
feature_impl const& feature,
|
|
attributes const& attrs,
|
|
double scale_factor,
|
|
text_symbolizer_properties const& properties,
|
|
text_layout_properties const& layout_defaults,
|
|
formatting::node_ptr tree);
|
|
|
|
// Adds a new text part. Call this function repeatedly to build the complete text.
|
|
void add_text(mapnik::value_unicode_string const& str, evaluated_format_properties_ptr const& format);
|
|
|
|
// Returns the complete text stored in this layout.
|
|
mapnik::value_unicode_string const& text() const;
|
|
|
|
// Processes the text into a list of glyphs, performing RTL/LTR handling, shaping and line breaking.
|
|
void layout();
|
|
|
|
// Clear all data stored in this object. The object's state is the same as directly after construction.
|
|
void clear();
|
|
|
|
// Height of all lines together (in pixels).
|
|
inline double height() const { return height_; }
|
|
// Width of the longest line (in pixels).
|
|
inline double width() const { return width_ ; }
|
|
|
|
// Line iterator.
|
|
inline const_iterator begin() const { return lines_.begin(); }
|
|
inline const_iterator end() const { return lines_.end(); }
|
|
|
|
// Number of lines.
|
|
inline std::size_t num_lines() const { return lines_.size(); }
|
|
|
|
// Width of a certain glyph cluster (in pixels).
|
|
inline double cluster_width(unsigned cluster) const
|
|
{
|
|
std::map<unsigned, double>::const_iterator width_itr = width_map_.find(cluster);
|
|
if (width_itr != width_map_.end()) return width_itr->second;
|
|
return 0;
|
|
}
|
|
|
|
// Returns the number of glyphs so memory can be preallocated.
|
|
inline unsigned glyphs_count() const { return glyphs_count_;}
|
|
|
|
void add_child(text_layout_ptr const& child_layout);
|
|
|
|
inline text_layout_vector const& get_child_layouts() const { return child_layout_list_; }
|
|
inline face_manager_freetype & get_font_manager() const { return font_manager_; }
|
|
inline double get_scale_factor() const { return scale_factor_; }
|
|
inline text_symbolizer_properties const& get_default_text_properties() const { return properties_; }
|
|
inline text_layout_properties const& get_layout_properties() const { return layout_properties_; }
|
|
|
|
inline rotation const& orientation() const { return orientation_; }
|
|
inline pixel_position const& displacement() const { return displacement_; }
|
|
inline box2d<double> const& bounds() const { return bounds_; }
|
|
inline horizontal_alignment_e horizontal_alignment() const { return halign_; }
|
|
pixel_position alignment_offset() const;
|
|
double jalign_offset(double line_width) const;
|
|
evaluated_format_properties_ptr & new_child_format_ptr(evaluated_format_properties_ptr const& p);
|
|
|
|
const_iterator longest_line() const;
|
|
|
|
private:
|
|
void break_line(std::pair<unsigned, unsigned> && line_limits);
|
|
void break_line_icu(std::pair<unsigned, unsigned> && line_limits);
|
|
void shape_text(text_line & line);
|
|
void add_line(text_line && line);
|
|
void clear_cluster_widths(unsigned first, unsigned last);
|
|
void init_auto_alignment();
|
|
|
|
// input
|
|
face_manager_freetype & font_manager_;
|
|
double scale_factor_;
|
|
|
|
// processing
|
|
text_itemizer itemizer_;
|
|
// Maps char index (UTF-16) to width. If multiple glyphs map to the same char the sum of all widths is used
|
|
// note: this probably isn't the best solution. it would be better to have an object for each cluster, but
|
|
// it needs to be implemented with no overhead.
|
|
std::map<unsigned, double> width_map_;
|
|
double width_;
|
|
double height_;
|
|
unsigned glyphs_count_;
|
|
|
|
// output
|
|
line_vector lines_;
|
|
|
|
// layout properties (owned by text_layout)
|
|
text_layout_properties layout_properties_;
|
|
|
|
// text symbolizer properties (owned by placement_finder's 'text_placement_info' (info_) which is owned by symbolizer_helper
|
|
text_symbolizer_properties const& properties_;
|
|
|
|
// format properties (owned by text_layout)
|
|
evaluated_format_properties_ptr format_;
|
|
|
|
// alignments
|
|
vertical_alignment_e valign_;
|
|
horizontal_alignment_e halign_;
|
|
justify_alignment_e jalign_;
|
|
|
|
// Precalculated values for maximum performance
|
|
rotation orientation_ = {0,1.0};
|
|
char wrap_char_ = ' ';
|
|
double wrap_width_ = 0.0;
|
|
bool wrap_before_ = false;
|
|
bool repeat_wrap_char_ = false;
|
|
bool rotate_displacement_ = false;
|
|
double text_ratio_ = 0.0;
|
|
pixel_position displacement_ = {0,0};
|
|
box2d<double> bounds_;
|
|
|
|
// children
|
|
text_layout_vector child_layout_list_;
|
|
|
|
// take ownership of evaluated_format_properties_ptr of any format children
|
|
// in order to keep them in scope
|
|
// NOTE: this must not be a std::vector - see note above about child_format_ptrs
|
|
child_format_ptrs format_ptrs_;
|
|
};
|
|
|
|
class layout_container
|
|
{
|
|
public:
|
|
layout_container()
|
|
: glyphs_count_(0), line_count_(0) {}
|
|
|
|
void add(text_layout_ptr layout);
|
|
void clear();
|
|
|
|
void layout();
|
|
|
|
inline size_t size() const { return layouts_.size(); }
|
|
inline bool empty() const { return layouts_.empty(); }
|
|
|
|
inline text_layout_vector::const_iterator begin() const { return layouts_.begin(); }
|
|
inline text_layout_vector::const_iterator end() const { return layouts_.end(); }
|
|
inline text_layout_ptr const& back() const { return layouts_.back(); }
|
|
inline mapnik::value_unicode_string const& text() const { return text_; }
|
|
|
|
inline unsigned glyphs_count() const { return glyphs_count_; }
|
|
inline unsigned line_count() const { return line_count_; }
|
|
|
|
inline box2d<double> const& bounds() const { return bounds_; }
|
|
|
|
inline double width() const { return bounds_.width(); }
|
|
inline double height() const { return bounds_.height(); }
|
|
|
|
private:
|
|
text_layout_vector layouts_;
|
|
|
|
mapnik::value_unicode_string text_;
|
|
|
|
unsigned glyphs_count_;
|
|
unsigned line_count_;
|
|
|
|
box2d<double> bounds_;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // TEXT_LAYOUT_HPP
|