bool font_face::glyph_dimensions(glyph_info & glyph) const { FT_Vector pen; pen.x = 0; pen.y = 0; FT_Set_Transform(face_, 0, &pen); if (FT_Load_Glyph(face_, glyph.glyph_index, FT_LOAD_NO_HINTING)) { MAPNIK_LOG_ERROR(font_face) << "FT_Load_Glyph failed"; return false; } FT_Glyph image; if (FT_Get_Glyph(face_->glyph, &image)) { MAPNIK_LOG_ERROR(font_face) << "FT_Get_Glyph failed"; return false; } FT_BBox glyph_bbox; FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox); FT_Done_Glyph(image); glyph.unscaled_ymin = glyph_bbox.yMin; glyph.unscaled_ymax = glyph_bbox.yMax; glyph.unscaled_advance = face_->glyph->advance.x; glyph.unscaled_line_height = face_->size->metrics.height; glyph.unscaled_ascender = unscaled_ascender_; return true; }
text_placements_ptr text_placements_simple::from_xml(xml_node const& xml, fontset_map const& fontsets, bool is_shield) { // TODO - handle X cleaner std::string placements_string = xml.get_attr<std::string>("placements", "X"); // like set_property_from_xml in properties_util.hpp if (!placements_string.empty()) { if (placements_string == "X") { text_placements_ptr ptr = std::make_shared<text_placements_simple>(placements_string); ptr->defaults.from_xml(xml, fontsets, is_shield); return ptr; } else { try { // we don't use parse_expression(placements_string) directly here to benefit from the cache in the xml_node boost::optional<expression_ptr> val = xml.get_opt_attr<expression_ptr>("placements"); if (val) { text_placements_ptr ptr = std::make_shared<text_placements_simple>(*val); ptr->defaults.from_xml(xml, fontsets, is_shield); return ptr; } } catch (std::exception const& ex) { // otherwise ensure it is valid std::vector<directions_e> direction; std::vector<int> text_sizes; if (!parse_positions(placements_string,direction,text_sizes)) { MAPNIK_LOG_ERROR(text_placements) << "Could not parse text_placement_simple placement string ('" << placements_string << "')"; if (direction.size() == 0) { MAPNIK_LOG_ERROR(text_placements) << "text_placements_simple with no valid placements! ('"<< placements_string <<"')"; } return text_placements_ptr(); } else { text_placements_ptr ptr = std::make_shared<text_placements_simple>(placements_string,std::move(direction),std::move(text_sizes)); ptr->defaults.from_xml(xml, fontsets, is_shield); return ptr; } } text_placements_ptr ptr = std::make_shared<text_placements_simple>(placements_string); ptr->defaults.from_xml(xml, fontsets, is_shield); return ptr; } } return text_placements_ptr(); }
bool datasource_cache::register_datasource(std::string const& filename) { #ifdef MAPNIK_THREADSAFE std::lock_guard<std::recursive_mutex> lock(instance_mutex_); #endif try { if (!mapnik::util::exists(filename)) { MAPNIK_LOG_ERROR(datasource_cache) << "Cannot load '" << filename << "' (plugin does not exist)"; return false; } std::shared_ptr<PluginInfo> plugin = std::make_shared<PluginInfo>(filename,"datasource_name"); if (plugin->valid()) { if (plugin->name().empty()) { MAPNIK_LOG_ERROR(datasource_cache) << "Problem loading plugin library '" << filename << "' (plugin is lacking compatible interface)"; } else { if (plugins_.emplace(plugin->name(),plugin).second) { MAPNIK_LOG_DEBUG(datasource_cache) << "datasource_cache: Registered=" << plugin->name(); return true; } } } else { MAPNIK_LOG_ERROR(datasource_cache) << "Problem loading plugin library: " << filename << " (dlopen failed - plugin likely has an unsatisfied dependency or incompatible ABI)"; } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(datasource_cache) << "Exception caught while loading plugin library: " << filename << " (" << ex.what() << ")"; } return false; }
bool freetype_engine::register_font(std::string const& file_name) { #ifdef MAPNIK_THREADSAFE mapnik::scoped_lock lock(mutex_); #endif FT_Library library = 0; FT_Error error = FT_Init_FreeType(&library); if (error) { throw std::runtime_error("Failed to initialize FreeType2 library"); } FT_Face face = 0; int num_faces = 0; bool success = false; // some font files have multiple fonts in a file // the count is in the 'root' face library[0] // see the FT_FaceRec in freetype.h for ( int i = 0; face == 0 || i < num_faces; i++ ) { // if face is null then this is the first face error = FT_New_Face (library,file_name.c_str(),i,&face); if (error) { break; } // store num_faces locally, after FT_Done_Face it can not be accessed any more if (!num_faces) num_faces = face->num_faces; // some fonts can lack names, skip them // http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec if (face->family_name && face->style_name) { std::string name = std::string(face->family_name) + " " + std::string(face->style_name); // skip fonts with leading . in the name if (!boost::algorithm::starts_with(name,".")) { name2file_.insert(std::make_pair(name, std::make_pair(i,file_name))); success = true; } } else { std::ostringstream s; s << "Warning: unable to load font file '" << file_name << "' "; if (!face->family_name && !face->style_name) s << "which lacks both a family name and style name"; else if (face->family_name) s << "which reports a family name of '" << std::string(face->family_name) << "' and lacks a style name"; else if (face->style_name) s << "which reports a style name of '" << std::string(face->style_name) << "' and lacks a family name"; MAPNIK_LOG_ERROR(font_engine_freetype) << "register_font: " << s.str(); } } if (face) FT_Done_Face(face); if (library) FT_Done_FreeType(library); return success; }
void xml_node::add_attribute(const char * name, const char * value) { auto result = attributes_.emplace(name,xml_attribute(value)); if (!result.second) { MAPNIK_LOG_ERROR(xml_tree) << "ignoring duplicate attribute '" << name << "'"; } }
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points() { label_placement_enum how_placed = placement_->properties.label_placement; if (how_placed == LINE_PLACEMENT) { point_placement_ = false; return; } else { point_placement_ = true; } double label_x=0.0; double label_y=0.0; double z=0.0; std::list<geometry_type*>::const_iterator itr = geometries_to_process_.begin(); std::list<geometry_type*>::const_iterator end = geometries_to_process_.end(); for (; itr != end; itr++) { geometry_type const& geom = **itr; if (how_placed == VERTEX_PLACEMENT) { geom.rewind(0); for(unsigned i = 0; i < geom.size(); i++) { geom.vertex(&label_x, &label_y); prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(std::make_pair(label_x, label_y)); } } else { if (geom.type() == LineString) { label::middle_point(geom, label_x,label_y); } else if (how_placed == POINT_PLACEMENT) { label::centroid(geom, label_x, label_y); } else if (how_placed == INTERIOR_PLACEMENT) { label::interior_position(geom, label_x, label_y); } else { MAPNIK_LOG_ERROR(symbolizer_helpers) << "ERROR: Unknown placement type in initialize_points()"; } prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(std::make_pair(label_x, label_y)); } } point_itr_ = points_.begin(); }
bool vertex_cache::backward(double length) { if (length < 0) { MAPNIK_LOG_ERROR(vertex_cache) << "vertex_cache::backward() called with negative argument!\n"; return false; } return move(-length); }
bool datasource_cache::register_datasources(std::string const& dir, bool recurse) { #ifdef MAPNIK_THREADSAFE mapnik::scoped_lock lock(mutex_); #endif if (!mapnik::util::exists(dir)) { return false; } plugin_directories_.insert(dir); if (!mapnik::util::is_directory(dir)) { return register_datasource(dir); } bool success = false; try { boost::filesystem::directory_iterator end_itr; #ifdef _WINDOWS std::wstring wide_dir(mapnik::utf8_to_utf16(dir)); for (boost::filesystem::directory_iterator itr(wide_dir); itr != end_itr; ++itr) { std::string file_name = mapnik::utf16_to_utf8(itr->path().wstring()); #else for (boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { std::string file_name = itr->path().string(); #endif if (boost::filesystem::is_directory(*itr) && recurse) { if (register_datasources(file_name, true)) { success = true; } } else { std::string base_name = itr->path().filename().string(); if (!boost::algorithm::starts_with(base_name,".") && mapnik::util::is_regular_file(file_name) && is_input_plugin(file_name)) { if (register_datasource(file_name)) { success = true; } } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(datasource_cache) << "register_datasources: " << ex.what(); } return success; }
text_placement_info_simple::text_placement_info_simple(text_placements_simple const* parent, std::string const& evaluated_positions, double scale_factor) : text_placement_info(parent, scale_factor), state(0), position_state(0), direction_(parent->direction_), text_sizes_(parent->text_sizes_), parent_(parent) { if (direction_.empty() && !parse_positions(evaluated_positions,direction_,text_sizes_)) { MAPNIK_LOG_ERROR(text_placements) << "Could not parse text_placement_simple placement string ('" << evaluated_positions << "')"; if (direction_.size() == 0) { MAPNIK_LOG_ERROR(text_placements) << "text_placements_simple with no valid placements! ('"<< evaluated_positions <<"')"; } } }
bool freetype_engine::register_fonts_impl(std::string const& dir, font_library & library, freetype_engine::font_file_mapping_type & font_file_mapping, bool recurse) { if (!mapnik::util::exists(dir)) { return false; } if (!mapnik::util::is_directory(dir)) { return register_font_impl(dir, library, font_file_mapping); } bool success = false; try { boost::filesystem::directory_iterator end_itr; #ifdef _WINDOWS std::wstring wide_dir(mapnik::utf8_to_utf16(dir)); for (boost::filesystem::directory_iterator itr(wide_dir); itr != end_itr; ++itr) { std::string file_name = mapnik::utf16_to_utf8(itr->path().wstring()); #else for (boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { std::string file_name = itr->path().string(); #endif if (boost::filesystem::is_directory(*itr) && recurse) { if (register_fonts_impl(file_name, library, font_file_mapping, true)) { success = true; } } else { std::string base_name = itr->path().filename().string(); if (!boost::algorithm::starts_with(base_name,".") && mapnik::util::is_regular_file(file_name) && is_font_file(file_name)) { if (register_font_impl(file_name, library, font_file_mapping)) { success = true; } } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(font_engine_freetype) << "register_fonts: " << ex.what(); } return success; }
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<count; ++i) { int32_t vis_length; int32_t run_start; direction = ubidi_getVisualRun(bidi, i, &run_start, &vis_length); run_start += start; //Add offset to compensate offset in setPara direction_runs_.emplace_back(direction, run_start, run_start+vis_length); } } } } else { MAPNIK_LOG_ERROR(text_itemizer) << "ICU error: " << u_errorName(error) << "\n"; //TODO: Exception } ubidi_close(bidi); }
~prepared_index_statement() { if (stmt_) { int res = sqlite3_finalize(stmt_); if (res != SQLITE_OK) { if (*(*ds_)) { MAPNIK_LOG_ERROR(sqlite) << "~prepared_index_statement:" << sqlite3_errmsg(*(*ds_)); } else { MAPNIK_LOG_ERROR(sqlite) << "~prepared_index_statement:" << res; } } } }
Detector & detector(std::string const & key) { auto it = cache_.find(key); if (it == cache_.end()) { MAPNIK_LOG_ERROR(detector) << "Collision cache '" << key << "' does not exist. Default cache is used instead."; return default_; } return it->second; }
bool freetype_engine::register_fonts(std::string const& dir, bool recurse) { if (!mapnik::util::exists(dir)) { return false; } if (!mapnik::util::is_directory(dir)) { return mapnik::freetype_engine::register_font(dir); } bool success = false; try { boost::filesystem::directory_iterator end_itr; for (boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { #if (BOOST_FILESYSTEM_VERSION == 3) std::string file_name = itr->path().string(); #else // v2 std::string file_name = itr->string(); #endif if (boost::filesystem::is_directory(*itr) && recurse) { if (register_fonts(file_name, true)) { success = true; } } else { #if (BOOST_FILESYSTEM_VERSION == 3) std::string base_name = itr->path().filename().string(); #else // v2 std::string base_name = itr->filename(); #endif if (!boost::algorithm::starts_with(base_name,".") && boost::filesystem::is_regular_file(file_name) && is_font_file(file_name)) { if (mapnik::freetype_engine::register_font(file_name)) { success = true; } } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(font_engine_freetype) << "register_fonts: " << ex.what(); } return success; }
bool datasource_cache::register_datasource(std::string const& str) { bool success = false; try { lt_dlhandle module = lt_dlopen(str.c_str()); if (module) { // http://www.mr-edd.co.uk/blog/supressing_gcc_warnings #ifdef __GNUC__ __extension__ #endif datasource_name* ds_name = reinterpret_cast<datasource_name*>(lt_dlsym(module, "datasource_name")); if (ds_name && insert(ds_name(),module)) { MAPNIK_LOG_DEBUG(datasource_cache) << "datasource_cache: Registered=" << ds_name(); success = true; } else if (!ds_name) { MAPNIK_LOG_ERROR(datasource_cache) << "Problem loading plugin library '" << str << "' (plugin is lacking compatible interface)"; } } else { MAPNIK_LOG_ERROR(datasource_cache) << "Problem loading plugin library: " << str << " (dlopen failed - plugin likely has an unsatisfied dependency or incompatible ABI)"; } } catch (...) { MAPNIK_LOG_ERROR(datasource_cache) << "Exception caught while loading plugin library: " << str; } return success; }
static inline boost::optional<mapnik::enumeration<T,MAX> > xml_attribute_cast_impl(xml_tree const& /*tree*/, std::string const& source) { using result_type = typename boost::optional<mapnik::enumeration<T,MAX> >; try { mapnik::enumeration<T,MAX> e; e.from_string(source); return result_type(e); } catch (illegal_enum_value const& ex) { MAPNIK_LOG_ERROR(do_xml_attribute_cast) << ex.what(); return result_type(); } }
void raster_symbolizer::set_mode(std::string const& mode) { MAPNIK_LOG_ERROR(raster_symbolizer) << "setting 'mode' is deprecated and will be removed in Mapnik 3.x, use 'comp-op' with Mapnik >= 2.1.x"; mode_ = mode; if (mode == "normal") { MAPNIK_LOG_ERROR(raster_symbolizer) << "converting 'mode=normal' to 'comp-op:src_over'"; this->set_comp_op(src_over); } else { std::string mode2 = boost::algorithm::replace_last_copy(mode,"2",""); boost::optional<composite_mode_e> comp_op = comp_op_from_string(mode2); if (comp_op) { MAPNIK_LOG_ERROR(raster_symbolizer) << "converting 'mode:" << mode << "' to 'comp-op:" + *comp_op_to_string(*comp_op) + "'"; this->set_comp_op(*comp_op); } else { MAPNIK_LOG_ERROR(raster_symbolizer) << "could not convert mode '" << mode << "' into comp-op, defaulting to 'comp-op:src-over'"; } } }
bool datasource_cache::register_datasources(std::string const& dir, bool recurse) { #ifdef MAPNIK_THREADSAFE std::lock_guard<std::recursive_mutex> lock(instance_mutex_); #endif if (!mapnik::util::exists(dir)) { return false; } plugin_directories_.insert(dir); if (!mapnik::util::is_directory(dir)) { return register_datasource(dir); } bool success = false; try { for (std::string const& file_name : mapnik::util::list_directory(dir)) { if (mapnik::util::is_directory(file_name) && recurse) { if (register_datasources(file_name, true)) { success = true; } } else { std::string base_name = mapnik::util::basename(file_name); if (!boost::algorithm::starts_with(base_name,".") && mapnik::util::is_regular_file(file_name) && is_input_plugin(file_name)) { if (register_datasource(file_name)) { success = true; } } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(datasource_cache) << "register_datasources: " << ex.what(); } return success; }
bool freetype_engine::register_fonts_impl(std::string const& dir, font_library & library, freetype_engine::font_file_mapping_type & font_file_mapping, bool recurse) { if (!mapnik::util::exists(dir)) { return false; } if (!mapnik::util::is_directory(dir)) { return register_font_impl(dir, library, font_file_mapping); } bool success = false; try { for (std::string const& file_name : mapnik::util::list_directory(dir)) { if (mapnik::util::is_directory(file_name) && recurse) { if (register_fonts_impl(file_name, library, font_file_mapping, true)) { success = true; } } else { std::string base_name = mapnik::util::basename(file_name); if (!boost::algorithm::starts_with(base_name,".") && mapnik::util::is_regular_file(file_name) && is_font_file(file_name)) { if (register_font_impl(file_name, library, font_file_mapping)) { success = true; } } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(font_engine_freetype) << "register_fonts: " << ex.what(); } return success; }
result_type operator() ( Iterator, Iterator end, Iterator err_pos, boost::spirit::info const& what) const { #ifdef MAPNIK_LOG std::stringstream s; using difference_type = typename std::iterator_traits<Iterator>::difference_type; auto start_err = err_pos; std::advance(err_pos, std::min(std::distance(err_pos, end), difference_type(16))); auto end_err = err_pos; assert(end_err <= end); s << "Mapnik GeoJSON parsing error:" << what << " expected but got: " << std::string(start_err, end_err); MAPNIK_LOG_ERROR(error_handler) << s.str(); #endif return boost::spirit::qi::fail; }
boost::optional<mapped_region_ptr> mapped_memory_cache::find(std::string const& uri, bool update_cache) { #ifdef MAPNIK_THREADSAFE std::lock_guard<std::mutex> lock(mutex_); #endif using iterator_type = std::unordered_map<std::string, mapped_region_ptr>::const_iterator; boost::optional<mapped_region_ptr> result; iterator_type itr = cache_.find(uri); if (itr != cache_.end()) { result.reset(itr->second); return result; } if (mapnik::util::exists(uri)) { try { boost::interprocess::file_mapping mapping(uri.c_str(),boost::interprocess::read_only); mapped_region_ptr region(std::make_shared<boost::interprocess::mapped_region>(mapping,boost::interprocess::read_only)); result.reset(region); if (update_cache) { cache_.emplace(uri, *result); } return result; } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(mapped_memory_cache) << "Error loading mapped memory file: '" << uri << "' (" << ex.what() << ")"; } } /* else { MAPNIK_LOG_WARN(mapped_memory_cache) << "Memory region does not exist file: " << uri; } */ return result; }
image_gray8 create_bitmap(box2d<T> box) { if (!box.valid()) { return image_gray8(0, 0); } try { return image_gray8(box.width(), box.height()); } catch (std::runtime_error const & e) { // TODO: Scale down in this case. MAPNIK_LOG_ERROR(grid_vertex_adapter) << "grid vertex adapter: Cannot allocate underlaying bitmap. Bbox: " << box.to_string() << "; " << e.what(); return image_gray8(0, 0); } }
void text_placement_info_combined::apply_placement(text_placement_info_ptr placement_info) const { text_placement_info_simple* simple = dynamic_cast<text_placement_info_simple*>(placement_info.get()); if (simple) { apply_simple_placement(); } else { text_placement_info_list* list = dynamic_cast<text_placement_info_list*>(placement_info.get()); if (list) { apply_list_placement(); } else { MAPNIK_LOG_ERROR(text_placement_info_combined) << "Could not apply unknown placement info ptr"; } } }
/** Converts @p str to an enum. * @throw illegal_enum_value @p str is not a legal identifier. * */ void from_string(std::string const& str) { // TODO: Enum value strings with underscore are deprecated in Mapnik 3.x // and support will be removed in Mapnik 4.x. bool deprecated = false; std::string str_copy(str); if (str_copy.find('_') != std::string::npos) { std::replace(str_copy.begin(), str_copy.end(), '_', '-'); deprecated = true; } for (unsigned i = 0; i < THE_MAX; ++i) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" // clang+gcc #pragma GCC diagnostic ignored "-Wpragmas" // gcc #pragma GCC diagnostic ignored "-Wundefined-var-template" if (str_copy == our_strings_[i]) #pragma GCC diagnostic pop { value_ = static_cast<ENUM>(i); if (deprecated) { MAPNIK_LOG_ERROR(enumerations) << "enumeration value (" << str << ") using \"_\" is deprecated and will be removed in Mapnik 4.x, use '" << str_copy << "' instead"; } return; } } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" // clang+gcc #pragma GCC diagnostic ignored "-Wpragmas" // gcc #pragma GCC diagnostic ignored "-Wundefined-var-template" throw illegal_enum_value(std::string("Illegal enumeration value '") + str + "' for enum " + our_name_); #pragma GCC diagnostic pop }
bool freetype_engine::register_font_impl(std::string const& file_name, FT_LibraryRec_ * library) { #ifdef _WINDOWS FILE * file = _wfopen(mapnik::utf8_to_utf16(file_name).c_str(), L"rb"); #else FILE * file = std::fopen(file_name.c_str(),"rb"); #endif if (file == nullptr) return false; FT_Face face = 0; FT_Open_Args args; FT_StreamRec streamRec; memset(&args, 0, sizeof(args)); memset(&streamRec, 0, sizeof(streamRec)); fseek(file, 0, SEEK_END); std::size_t file_size = std::ftell(file); fseek(file, 0, SEEK_SET); streamRec.base = 0; streamRec.pos = 0; streamRec.size = file_size; streamRec.descriptor.pointer = file; streamRec.read = ft_read_cb; streamRec.close = ft_close_cb; args.flags = FT_OPEN_STREAM; args.stream = &streamRec; int num_faces = 0; bool success = false; // some font files have multiple fonts in a file // the count is in the 'root' face library[0] // see the FT_FaceRec in freetype.h for ( int i = 0; face == 0 || i < num_faces; ++i ) { // if face is null then this is the first face FT_Error error = FT_Open_Face(library, &args, i, &face); if (error) break; // store num_faces locally, after FT_Done_Face it can not be accessed any more if (num_faces == 0) num_faces = face->num_faces; // some fonts can lack names, skip them // http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec if (face->family_name && face->style_name) { std::string name = std::string(face->family_name) + " " + std::string(face->style_name); // skip fonts with leading . in the name if (!boost::algorithm::starts_with(name,".")) { name2file_.insert(std::make_pair(name, std::make_pair(i,file_name))); success = true; } } else { std::ostringstream s; s << "Warning: unable to load font file '" << file_name << "' "; if (!face->family_name && !face->style_name) s << "which lacks both a family name and style name"; else if (face->family_name) s << "which reports a family name of '" << std::string(face->family_name) << "' and lacks a style name"; else if (face->style_name) s << "which reports a style name of '" << std::string(face->style_name) << "' and lacks a family name"; MAPNIK_LOG_ERROR(font_engine_freetype) << "register_font: " << s.str(); } if (face) FT_Done_Face(face); } return success; }
// In the Unicode string characters are always stored in logical order. // This makes line breaking easy. One word is added to the current line at a time. Once the line is too long // we either go back one step or insert the line break at the current position (depending on "wrap_before" setting). // At the end everything that is left over is added as the final line. void text_layout::break_line_icu(std::pair<unsigned, unsigned> && line_limits) { text_line line(line_limits.first, line_limits.second); shape_text(line); double scaled_wrap_width = wrap_width_ * scale_factor_; if (scaled_wrap_width <= 0 || line.width() < scaled_wrap_width) { add_line(std::move(line)); return; } if (text_ratio_ > 0) { double wrap_at; double string_width = line.width(); double string_height = line.line_height(); for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > text_ratio_ && (string_width/i) > scaled_wrap_width; i += 1.0) ; scaled_wrap_width = wrap_at; } mapnik::value_unicode_string const& text = itemizer_.text(); Locale locale; // TODO: Is the default constructor correct? UErrorCode status = U_ZERO_ERROR; std::unique_ptr<BreakIterator> breakitr(BreakIterator::createLineInstance(locale, status)); // Not breaking the text if an error occurs is probably the best thing we can do. // https://github.com/mapnik/mapnik/issues/2072 if (!U_SUCCESS(status)) { add_line(std::move(line)); MAPNIK_LOG_ERROR(text_layout) << " could not create BreakIterator: " << u_errorName(status); return; } breakitr->setText(text); double current_line_length = 0; int last_break_position = static_cast<int>(line.first_char()); for (unsigned i = line.first_char(); i < line.last_char(); ++i) { // TODO: character_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 <= scaled_wrap_width) continue; int break_position = wrap_before_ ? breakitr->preceding(i + 1) : breakitr->following(i); // following() returns a break position after the last word. So DONE should only be returned // when calling preceding. if (break_position <= last_break_position || break_position == static_cast<int>(BreakIterator::DONE)) { // A single word is longer than the maximum line width. // Violate line width requirement and choose next break position break_position = breakitr->following(i); if (break_position == static_cast<int>(BreakIterator::DONE)) { break_position = line.last_char(); MAPNIK_LOG_ERROR(text_layout) << "Unexpected result in break_line. Trying to recover...\n"; } } // Break iterator operates on the whole string, while we only look at one line. So we need to // clamp break values. if (break_position < static_cast<int>(line.first_char())) { break_position = line.first_char(); } if (break_position > static_cast<int>(line.last_char())) { break_position = line.last_char(); } bool adjust_for_space_character = break_position > 0 && text[break_position - 1] == 0x0020; text_line new_line(last_break_position, adjust_for_space_character ? break_position - 1 : break_position); clear_cluster_widths(last_break_position, adjust_for_space_character ? break_position - 1 : break_position); shape_text(new_line); add_line(std::move(new_line)); last_break_position = break_position; i = break_position - 1; current_line_length = 0; } if (last_break_position == static_cast<int>(line.first_char())) { // No line breaks => no reshaping required add_line(std::move(line)); } else if (last_break_position != static_cast<int>(line.last_char())) { text_line new_line(last_break_position, line.last_char()); clear_cluster_widths(last_break_position, line.last_char()); shape_text(new_line); add_line(std::move(new_line)); } }
static void shape_text(text_line & line, text_itemizer & itemizer, std::map<unsigned,double> & width_map, face_manager_freetype & font_manager, double scale_factor ) { unsigned start = line.first_char(); unsigned end = line.last_char(); mapnik::value_unicode_string const& text = itemizer.text(); size_t length = end - start; if (!length) return; line.reserve(length); std::list<text_item> const& list = itemizer.itemize(start, end); UErrorCode err = U_ZERO_ERROR; mapnik::value_unicode_string shaped; mapnik::value_unicode_string reordered; for (auto const& text_item : list) { face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset); double size = text_item.format->text_size * scale_factor; face_set->set_unscaled_character_sizes(); for (auto const& face : *face_set) { UBiDi *bidi = ubidi_openSized(length, 0, &err); ubidi_setPara(bidi, text.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err); ubidi_writeReordered(bidi, reordered.getBuffer(length), length, UBIDI_DO_MIRRORING, &err); ubidi_close(bidi); reordered.releaseBuffer(length); int32_t num_char = u_shapeArabic(reordered.getBuffer(), length, shaped.getBuffer(length), length, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err); if (num_char < 0) { MAPNIK_LOG_ERROR(icu_shaper) << " u_shapeArabic returned negative num_char " << num_char; } std::size_t num_chars = static_cast<std::size_t>(num_char); shaped.releaseBuffer(length); bool shaped_status = true; if (U_SUCCESS(err) && (num_chars == length)) { U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); unsigned i = 0; for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); glyph_info tmp; tmp.offset.clear(); tmp.char_index = i; tmp.glyph_index = FT_Get_Char_Index(face->get_face(), ch); if (tmp.glyph_index == 0) { shaped_status = false; break; } tmp.face = face; tmp.format = text_item.format; face->glyph_dimensions(tmp); tmp.scale_multiplier = size / face->get_face()->units_per_EM; width_map[i] += tmp.advance(); line.add_glyph(std::move(tmp), scale_factor); ++i; } } if (!shaped_status) continue; line.update_max_char_height(face->get_char_height(size)); return; } } }
void base_symbolizer_helper::initialize_points() { label_placement_enum how_placed = placement_->properties.label_placement; if (how_placed == LINE_PLACEMENT) { point_placement_ = false; return; } else { point_placement_ = true; } double label_x=0.0; double label_y=0.0; double z=0.0; for (auto * geom_ptr : geometries_to_process_) { geometry_type const& geom = *geom_ptr; if (how_placed == VERTEX_PLACEMENT) { geom.rewind(0); for(unsigned i = 0; i < geom.size(); ++i) { geom.vertex(&label_x, &label_y); prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(pixel_position(label_x, label_y)); } } else { // https://github.com/mapnik/mapnik/issues/1423 bool success = false; // https://github.com/mapnik/mapnik/issues/1350 if (geom.type() == geometry_type::types::LineString) { success = label::middle_point(geom, label_x,label_y); } else if (how_placed == POINT_PLACEMENT) { success = label::centroid(geom, label_x, label_y); } else if (how_placed == INTERIOR_PLACEMENT) { success = label::interior_position(geom, label_x, label_y); } else { MAPNIK_LOG_ERROR(symbolizer_helpers) << "ERROR: Unknown placement type in initialize_points()"; } if (success) { prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(pixel_position(label_x, label_y)); } } } point_itr_ = points_.begin(); }
void Map::zoom_all() { try { if (layers_.empty()) { return; } projection proj0(srs_); box2d<double> ext; bool success = false; bool first = true; std::vector<layer>::const_iterator itr = layers_.begin(); std::vector<layer>::const_iterator end = layers_.end(); while (itr != end) { if (itr->active()) { std::string const& layer_srs = itr->srs(); projection proj1(layer_srs); proj_transform prj_trans(proj0,proj1); box2d<double> layer_ext = itr->envelope(); if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS)) { success = true; MAPNIK_LOG_DEBUG(map) << "map: Layer " << itr->name() << " original ext=" << itr->envelope(); MAPNIK_LOG_DEBUG(map) << "map: Layer " << itr->name() << " transformed to map srs=" << layer_ext; if (first) { ext = layer_ext; first = false; } else { ext.expand_to_include(layer_ext); } } } ++itr; } if (success) { if (maximum_extent_) { ext.clip(*maximum_extent_); } zoom_to_box(ext); } else { if (maximum_extent_) { MAPNIK_LOG_ERROR(map) << "could not zoom to combined layer extents" << " so falling back to maximum-extent for zoom_all result"; zoom_to_box(*maximum_extent_); } else { std::ostringstream s; s << "could not zoom to combined layer extents " << "using zoom_all because proj4 could not " << "back project any layer extents into the map srs " << "(set map 'maximum-extent' to override layer extents)"; throw std::runtime_error(s.str()); } } } catch (proj_init_error const& ex) { throw mapnik::config_error(std::string("Projection error during map.zoom_all: ") + ex.what()); } }
bool freetype_engine::register_font_impl(std::string const& file_name, font_library & library, freetype_engine::font_file_mapping_type & font_file_mapping) { MAPNIK_LOG_DEBUG(font_engine_freetype) << "registering: " << file_name; mapnik::util::file file(file_name); if (!file) return false; FT_Face face = 0; FT_Open_Args args; FT_StreamRec streamRec; memset(&args, 0, sizeof(args)); memset(&streamRec, 0, sizeof(streamRec)); streamRec.base = 0; streamRec.pos = 0; streamRec.size = file.size(); streamRec.descriptor.pointer = file.get(); streamRec.read = ft_read_cb; streamRec.close = nullptr; args.flags = FT_OPEN_STREAM; args.stream = &streamRec; int num_faces = 0; bool success = false; // some font files have multiple fonts in a file // the count is in the 'root' face library[0] // see the FT_FaceRec in freetype.h for ( int i = 0; face == 0 || i < num_faces; ++i ) { // if face is null then this is the first face FT_Error error = FT_Open_Face(library.get(), &args, i, &face); if (error) break; // store num_faces locally, after FT_Done_Face it can not be accessed any more if (num_faces == 0) num_faces = face->num_faces; // some fonts can lack names, skip them // http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec if (face->family_name && face->style_name) { std::string name = std::string(face->family_name) + " " + std::string(face->style_name); // skip fonts with leading . in the name if (!boost::algorithm::starts_with(name,".")) { // http://stackoverflow.com/a/24795559/2333354 auto range = font_file_mapping.equal_range(name); if (range.first == range.second) // the key was previously absent; insert a pair { font_file_mapping.emplace_hint(range.first, name, std::make_pair(i,file_name)); } else // the key was present, replace the associated value { /* some action with value range.first->second about to be overwritten here */ MAPNIK_LOG_WARN(font_engine_freetype) << "registering new " << name << " at '" << file_name << "'"; range.first->second = std::make_pair(i,file_name); // replace value } success = true; } } else { std::ostringstream s; s << "Warning: unable to load font file '" << file_name << "' "; if (!face->family_name && !face->style_name) s << "which lacks both a family name and style name"; else if (face->family_name) s << "which reports a family name of '" << std::string(face->family_name) << "' and lacks a style name"; else if (face->style_name) s << "which reports a style name of '" << std::string(face->style_name) << "' and lacks a family name"; MAPNIK_LOG_ERROR(font_engine_freetype) << "register_font: " << s.str(); } if (face) FT_Done_Face(face); } return success; }