diff --git a/include/llmr/style/bucket_description.hpp b/include/llmr/style/bucket_description.hpp index e306b799f..3b6fc4313 100644 --- a/include/llmr/style/bucket_description.hpp +++ b/include/llmr/style/bucket_description.hpp @@ -96,6 +96,7 @@ public: float vertical_alignment = 0.5; float line_height = 1.2 * 24; float max_width = 15.0f * 24; + float letter_spacing = 0; vec2 translate {0, 0}; float padding = 2.0f; float textMinDistance = 250.0f; diff --git a/include/llmr/text/glyph_store.hpp b/include/llmr/text/glyph_store.hpp index f8b14683d..49427a276 100644 --- a/include/llmr/text/glyph_store.hpp +++ b/include/llmr/text/glyph_store.hpp @@ -34,7 +34,8 @@ public: const float &maxWidth, const float &lineHeight, const float &alignment, - const float &verticalAlignment) const; + const float &verticalAlignment, + const float &letterSpacing) const; Shaping lineWrap(Shaping shaped, const float &lineHeight, const float &maxWidth, diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index f2b3ac05c..2d47542d9 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -227,7 +227,7 @@ std::unique_ptr TileParser::createTextBucket(const VectorTileLayer& laye // Shape labels. const Shaping shaped = fontStack.getShaping(string, bucket_desc.geometry.max_width, bucket_desc.geometry.line_height, bucket_desc.geometry.alignment, - bucket_desc.geometry.vertical_alignment); + bucket_desc.geometry.vertical_alignment, bucket_desc.geometry.letter_spacing); shaping.emplace(toString(it_prop->second), shaped); // Place labels. diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index 1a1d8ee1e..61f3c444c 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -133,6 +133,12 @@ BucketDescription StyleParser::parseBucket(JSVal value) { } else { throw Style::exception("max width must be a number"); } + } else if (name == "letterSpacing") { + if (value.IsNumber()) { + bucket.geometry.letter_spacing = value.GetDouble() * 24; + } else { + throw Style::exception("letter spacing must be a number"); + } } else if (name == "miterLimit") { if (value.IsNumber()) { bucket.geometry.miter_limit = value.GetDouble(); diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index ac647eb84..4d7ff57ec 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -30,7 +30,9 @@ const std::map &FontStack::getSDFs() const { } const Shaping FontStack::getShaping(const std::u32string &string, const float &maxWidth, - const float &lineHeight, const float &alignment, const float &verticalAlignment) const { + const float &lineHeight, const float &alignment, const float &verticalAlignment, + const float &letterSpacing) const { + std::lock_guard lock(mtx); uint32_t i = 0; uint32_t x = 0; @@ -40,7 +42,7 @@ const Shaping FontStack::getShaping(const std::u32string &string, const float &m GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0); shaped.push_back(glyph); i++; - x += metrics.find(chr)->second.advance; + x += metrics.find(chr)->second.advance + letterSpacing; } shaped = lineWrap(shaped, lineHeight, maxWidth, alignment, verticalAlignment);