/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2013 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 * *****************************************************************************/ //mapnik #include #include #include // stl #include namespace mapnik { text_itemizer::text_itemizer() : text_(), format_runs_(), direction_runs_(), script_runs_() { forced_line_breaks_.push_back(0); } void text_itemizer::add_text(mapnik::value_unicode_string str, char_properties_ptr format) { unsigned start = text_.length(); text_ += str; format_runs_.emplace_back(format, start, text_.length()); while ((start = text_.indexOf('\n', start)+1) > 0) { forced_line_breaks_.push_back(start); } } std::list const& text_itemizer::itemize(unsigned start, unsigned end) { if (end == 0) { end = text_.length(); } // format itemiziation is done by add_text() itemize_direction(start, end); itemize_script(); create_item_list(); return output_; } void text_itemizer::clear() { output_.clear(); text_.remove(); format_runs_.clear(); forced_line_breaks_.clear(); forced_line_breaks_.push_back(0); } std::pair text_itemizer::line(unsigned i) const { #ifdef MAPNIK_DEBUG if (i >= forced_line_breaks_.size()) return std::make_pair(0, 0); #endif if (i == forced_line_breaks_.size()-1) { return std::make_pair(forced_line_breaks_[i], text_.length()); } //Note -1 offset to exclude the \n char return std::make_pair(forced_line_breaks_[i], forced_line_breaks_[i+1]-1); } unsigned text_itemizer::num_lines() const { return forced_line_breaks_.size(); } void text_itemizer::itemize_direction(unsigned start, unsigned end) { direction_runs_.clear(); UErrorCode error = U_ZERO_ERROR; int32_t length = end - start; UBiDi *bidi = ubidi_openSized(length, 0, &error); if (!bidi || U_FAILURE(error)) { MAPNIK_LOG_ERROR(text_itemizer) << "Failed to create bidi object: " << u_errorName(error) << "\n"; return; } ubidi_setPara(bidi, text_.getBuffer() + start, length, UBIDI_DEFAULT_LTR, 0, &error); if (U_SUCCESS(error)) { UBiDiDirection direction = ubidi_getDirection(bidi); if (direction != UBIDI_MIXED) { direction_runs_.emplace_back(direction, start, end); } else { // mixed-directional int32_t count = ubidi_countRuns(bidi, &error); if(U_SUCCESS(error)) { for(int i=0; i typename T::const_iterator text_itemizer::find_run(T const& list, unsigned position) { typename T::const_iterator itr = list.begin(), end = list.end(); for ( ;itr!=end; ++itr) { // end is the first character not included in text range! if (itr->start <= position && itr->end > position) return itr; } return itr; } void text_itemizer::create_item_list() { /* This function iterates over direction runs in visual order and splits them if neccessary. * Split RTL runs are processed in reverse order to keep glyphs in correct order. * * logical 123 | 456789 * LTR visual 123 | 456789 * RTL visual 987654 | 321 * Glyphs within a single run are reversed by the shaper. */ output_.clear(); for (auto const& dir_run : direction_runs_) { unsigned position = dir_run.start; unsigned end = dir_run.end; std::list::iterator rtl_insertion_point = output_.end(); // Find first script and format run format_run_list::const_iterator format_itr = find_run(format_runs_, position); script_run_list::const_iterator script_itr = find_run(script_runs_, position); while (position < end) { assert(script_itr != script_runs_.end()); assert(format_itr != format_runs_.end()); text_item item; item.start = position; position = std::min(script_itr->end, std::min(format_itr->end, end)); item.end = position; item.format = format_itr->data; item.script = script_itr->data; item.rtl = dir_run.data; if (dir_run.data == UBIDI_LTR) { output_.push_back(item); } else { rtl_insertion_point = output_.insert(rtl_insertion_point, item); } if (script_itr->end == position) ++script_itr; if (format_itr->end == position) ++format_itr; } } } } //ns mapnik