From 65ef6f8e9648e97acf3f98df9dc7d5d4c36de03a Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 21 Apr 2022 20:24:49 -0300 Subject: [PATCH] Replace utf8 iterators with utf8_decode (fix #3260) This should fix some problems decoding invalid UTF-8 strings. --- laf | 2 +- src/app/file/split_filename.cpp | 46 ++++++++-------- src/app/file/split_filename.h | 6 ++- src/app/file/split_filename_tests.cpp | 11 ++++ src/app/ui/skin/skin_theme.cpp | 14 ++--- src/app/util/freetype_utils.cpp | 76 +++++++++++++-------------- src/ui/entry.cpp | 4 +- src/ui/graphics.cpp | 26 +++------ src/ui/graphics.h | 9 ++-- src/ui/widget.cpp | 18 +++---- 10 files changed, 106 insertions(+), 106 deletions(-) diff --git a/laf b/laf index 4f4693a8e..df53f4ac0 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit 4f4693a8e63b7bc63e3c0825112f7f3f190ebee9 +Subproject commit df53f4ac0cecada789bf84b4283947d2591833bd diff --git a/src/app/file/split_filename.cpp b/src/app/file/split_filename.cpp index 7df66301e..feb2fde8c 100644 --- a/src/app/file/split_filename.cpp +++ b/src/app/file/split_filename.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -9,9 +10,7 @@ #endif #include "app/file/split_filename.h" -#include "base/convert_to.h" #include "base/fs.h" -#include "base/string.h" #include @@ -20,41 +19,44 @@ namespace app { // Splits a file-name like "my_ani0000.pcx" to "my_ani" and ".pcx", // returning the number of the center; returns "-1" if the function // can't split anything -int split_filename(const std::string& filename, std::string& left, std::string& right, int& width) +int split_filename(const std::string& filename, + std::string& left, + std::string& right, + int& width) { left = base::get_file_title_with_path(filename); right = base::get_file_extension(filename); if (!right.empty()) right.insert(right.begin(), '.'); - // Remove all trailing numbers in the "left" side, and pass they to "result_str". + // Remove all trailing numbers in the "left" side. std::string result_str; width = 0; - for (;;) { - // Get the last UTF-8 character (as we don't have a - // reverse_iterator, we iterate from the beginning) - int chr = 0; - base::utf8_const_iterator begin(left.begin()), end(left.end()); - base::utf8_const_iterator it(begin), prev(begin); - for (; it != end; prev=it, ++it) - chr = *it; + int num = -1; + int order = 1; - if ((chr >= '0') && (chr <= '9')) { - result_str.insert(result_str.begin(), chr); - width++; + auto it = left.rbegin(); + auto end = left.rend(); - left.erase(prev - begin); + while (it != end) { + const int chr = *it; + if (chr >= '0' && chr <= '9') { + if (num < 0) + num = 0; + + num += order*(chr-'0'); + order *= 10; + ++width; + ++it; } else break; } - // Convert the "buf" to integer and return it. - if (!result_str.empty()) { - return base::convert_to(result_str); - } - else - return -1; + if (width > 0) + left.erase(left.end()-width, left.end()); + + return num; } } // namespace app diff --git a/src/app/file/split_filename.h b/src/app/file/split_filename.h index da0b74e75..31993aece 100644 --- a/src/app/file/split_filename.h +++ b/src/app/file/split_filename.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -12,7 +13,10 @@ namespace app { - int split_filename(const std::string& filename, std::string& left, std::string& right, int& width); + int split_filename(const std::string& filename, + std::string& left, + std::string& right, + int& width); } // namespace app diff --git a/src/app/file/split_filename_tests.cpp b/src/app/file/split_filename_tests.cpp index 4be32614f..37682eae2 100644 --- a/src/app/file/split_filename_tests.cpp +++ b/src/app/file/split_filename_tests.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This program is distributed under the terms of @@ -17,11 +18,21 @@ TEST(SplitFilename, Common) std::string left, right; int width; + EXPECT_EQ(-1, split_filename("sprite.png", left, right, width)); + EXPECT_EQ("sprite", left); + EXPECT_EQ(".png", right); + EXPECT_EQ(0, width); + EXPECT_EQ(1, split_filename("C:\\test\\a1.png", left, right, width)); EXPECT_EQ("C:\\test\\a", left); EXPECT_EQ(".png", right); EXPECT_EQ(1, width); + EXPECT_EQ(2001, split_filename("/hi/bye2001.png", left, right, width)); + EXPECT_EQ("/hi/bye", left); + EXPECT_EQ(".png", right); + EXPECT_EQ(4, width); + EXPECT_EQ(1, split_filename("C:/test/a1.png", left, right, width)); EXPECT_EQ("C:/test/a", left); EXPECT_EQ(".png", right); diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index b22a27d0d..af2f7d095 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -29,6 +29,7 @@ #include "base/fs.h" #include "base/log.h" #include "base/string.h" +#include "base/utf8_decode.h" #include "gfx/border.h" #include "gfx/point.h" #include "gfx/rect.h" @@ -1131,14 +1132,13 @@ void SkinTheme::drawEntryText(ui::Graphics* g, ui::Entry* widget) int scroll = delegate.index(); const std::string& textString = widget->text(); - base::utf8_const_iterator utf8_it((textString.begin())); - int textlen = base::utf8_length(textString); - scroll = std::min(scroll, textlen); - if (scroll) - utf8_it += scroll; + base::utf8_decode dec(textString); + auto pos = dec.pos(); + for (int i=0; idrawText(utf8_it, - base::utf8_const_iterator(textString.end()), + // TODO use a string_view() + g->drawText(std::string(pos, textString.end()), colors.text(), ColorNone, bounds.origin(), &delegate); diff --git a/src/app/util/freetype_utils.cpp b/src/app/util/freetype_utils.cpp index a0de9d974..b8802626d 100644 --- a/src/app/util/freetype_utils.cpp +++ b/src/app/util/freetype_utils.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -47,52 +48,49 @@ doc::Image* render_text(const std::string& fontfile, int fontsize, image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h)); doc::clear_image(image.get(), 0); - ft::ForEachGlyph feg(face); - if (feg.initialize(base::utf8_const_iterator(text.begin()), - base::utf8_const_iterator(text.end()))) { - do { - auto glyph = feg.glyph(); - if (!glyph) - continue; + ft::ForEachGlyph feg(face, text); + while (feg.next()) { + auto glyph = feg.glyph(); + if (!glyph) + continue; - int t, yimg = - bounds.y + int(glyph->y); + int t, yimg = - bounds.y + int(glyph->y); - for (int v=0; vbitmap->rows); ++v, ++yimg) { - const uint8_t* p = glyph->bitmap->buffer + v*glyph->bitmap->pitch; - int ximg = - bounds.x + int(glyph->x); - int bit = 0; + for (int v=0; vbitmap->rows); ++v, ++yimg) { + const uint8_t* p = glyph->bitmap->buffer + v*glyph->bitmap->pitch; + int ximg = - bounds.x + int(glyph->x); + int bit = 0; - for (int u=0; ubitmap->width); ++u, ++ximg) { - int alpha; + for (int u=0; ubitmap->width); ++u, ++ximg) { + int alpha; - if (antialias) { - alpha = *(p++); - } - else { - alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); - if (bit == 8) { - bit = 0; - ++p; - } - } - - int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); - if (output_alpha) { - doc::color_t output_color = - doc::rgba(doc::rgba_getr(color), - doc::rgba_getg(color), - doc::rgba_getb(color), - output_alpha); - - doc::put_pixel( - image.get(), ximg, yimg, - doc::rgba_blender_normal( - doc::get_pixel(image.get(), ximg, yimg), - output_color)); + if (antialias) { + alpha = *(p++); + } + else { + alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); + if (bit == 8) { + bit = 0; + ++p; } } + + int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); + if (output_alpha) { + doc::color_t output_color = + doc::rgba(doc::rgba_getr(color), + doc::rgba_getg(color), + doc::rgba_getb(color), + output_alpha); + + doc::put_pixel( + image.get(), ximg, yimg, + doc::rgba_blender_normal( + doc::get_pixel(image.get(), ximg, yimg), + output_color)); + } } - } while (feg.nextChar()); + } } } else { diff --git a/src/ui/entry.cpp b/src/ui/entry.cpp index 4c4faf4a0..d331ce470 100644 --- a/src/ui/entry.cpp +++ b/src/ui/entry.cpp @@ -922,9 +922,7 @@ void Entry::recalcCharBoxes(const std::string& text) { int lastTextIndex = int(text.size()); CalcBoxesTextDelegate delegate(lastTextIndex); - os::draw_text(nullptr, font(), - base::utf8_const_iterator(text.begin()), - base::utf8_const_iterator(text.end()), + os::draw_text(nullptr, font(), text, gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate); m_boxes = delegate.boxes(); diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 3efd2deee..7775de8c2 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -362,8 +362,7 @@ void Graphics::setFont(const os::FontRef& font) m_font = font; } -void Graphics::drawText(base::utf8_const_iterator it, - const base::utf8_const_iterator& end, +void Graphics::drawText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& origPt, os::DrawTextDelegate* delegate) @@ -372,18 +371,11 @@ void Graphics::drawText(base::utf8_const_iterator it, os::SurfaceLock lock(m_surface.get()); gfx::Rect textBounds = - os::draw_text(m_surface.get(), m_font.get(), it, end, fg, bg, pt.x, pt.y, delegate); + os::draw_text(m_surface.get(), m_font.get(), str, fg, bg, pt.x, pt.y, delegate); dirty(gfx::Rect(pt.x, pt.y, textBounds.w, textBounds.h)); } -void Graphics::drawText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt) -{ - drawText(base::utf8_const_iterator(str.begin()), - base::utf8_const_iterator(str.end()), - fg, bg, pt, nullptr); -} - namespace { class DrawUITextDelegate : public os::DrawTextDelegate { @@ -457,10 +449,8 @@ void Graphics::drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, int y = m_dy+pt.y; DrawUITextDelegate delegate(m_surface.get(), m_font.get(), mnemonic); - os::draw_text(m_surface.get(), m_font.get(), - base::utf8_const_iterator(str.begin()), - base::utf8_const_iterator(str.end()), - fg, bg, x, y, &delegate); + os::draw_text(m_surface.get(), m_font.get(), str, + fg, bg, x, y, &delegate); dirty(delegate.bounds()); } @@ -482,11 +472,9 @@ gfx::Size Graphics::measureUIText(const std::string& str) int Graphics::measureUITextLength(const std::string& str, os::Font* font) { DrawUITextDelegate delegate(nullptr, font, 0); - os::draw_text(nullptr, font, - base::utf8_const_iterator(str.begin()), - base::utf8_const_iterator(str.end()), - gfx::ColorNone, gfx::ColorNone, 0, 0, - &delegate); + os::draw_text(nullptr, font, str, + gfx::ColorNone, gfx::ColorNone, 0, 0, + &delegate); return delegate.bounds().w; } diff --git a/src/ui/graphics.h b/src/ui/graphics.h index 7c250b2a6..6ef87d690 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -117,11 +117,10 @@ namespace ui { os::Font* font() { return m_font.get(); } void setFont(const os::FontRef& font); - void drawText(base::utf8_const_iterator it, - const base::utf8_const_iterator& end, - gfx::Color fg, gfx::Color bg, const gfx::Point& pt, - os::DrawTextDelegate* delegate); - void drawText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt); + void drawText(const std::string& str, + gfx::Color fg, gfx::Color bg, + const gfx::Point& pt, + os::DrawTextDelegate* delegate = nullptr); void drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, const int mnemonic); void drawAlignedUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, const int align); diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp index a9d8c55f5..0c232732c 100644 --- a/src/ui/widget.cpp +++ b/src/ui/widget.cpp @@ -16,6 +16,7 @@ #include "base/clamp.h" #include "base/memory.h" #include "base/string.h" +#include "base/utf8_decode.h" #include "os/font.h" #include "os/surface.h" #include "os/system.h" @@ -1400,19 +1401,18 @@ void Widget::processMnemonicFromText(int escapeChar) if (!m_text.empty()) newText.reserve(m_text.size()); - for (base::utf8_const_iterator - it(m_text.begin()), - end(m_text.end()); it != end; ++it) { - if (*it == escapeChar) { - ++it; - if (it == end) { + base::utf8_decode decode(m_text); + while (int chr = decode.next()) { + if (chr == escapeChar) { + chr = decode.next(); + if (!chr) { break; // Ill-formed string (it ends with escape character) } - else if (*it != escapeChar) { - setMnemonic(*it); + else if (chr != escapeChar) { + setMnemonic(chr); } } - newText.push_back(*it); + newText.push_back(chr); } setText(base::to_utf8(newText));