Replace utf8 iterators with utf8_decode (fix #3260)

This should fix some problems decoding invalid UTF-8 strings.
This commit is contained in:
David Capello 2022-04-21 20:24:49 -03:00
parent 75a99360a0
commit 65ef6f8e96
10 changed files with 106 additions and 106 deletions

2
laf

@ -1 +1 @@
Subproject commit 4f4693a8e63b7bc63e3c0825112f7f3f190ebee9 Subproject commit df53f4ac0cecada789bf84b4283947d2591833bd

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,9 +10,7 @@
#endif #endif
#include "app/file/split_filename.h" #include "app/file/split_filename.h"
#include "base/convert_to.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/string.h"
#include <cstring> #include <cstring>
@ -20,41 +19,44 @@ namespace app {
// Splits a file-name like "my_ani0000.pcx" to "my_ani" and ".pcx", // Splits a file-name like "my_ani0000.pcx" to "my_ani" and ".pcx",
// returning the number of the center; returns "-1" if the function // returning the number of the center; returns "-1" if the function
// can't split anything // 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); left = base::get_file_title_with_path(filename);
right = base::get_file_extension(filename); right = base::get_file_extension(filename);
if (!right.empty()) if (!right.empty())
right.insert(right.begin(), '.'); 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; std::string result_str;
width = 0; width = 0;
for (;;) { int num = -1;
// Get the last UTF-8 character (as we don't have a int order = 1;
// 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;
if ((chr >= '0') && (chr <= '9')) { auto it = left.rbegin();
result_str.insert(result_str.begin(), chr); auto end = left.rend();
width++;
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 else
break; break;
} }
// Convert the "buf" to integer and return it. if (width > 0)
if (!result_str.empty()) { left.erase(left.end()-width, left.end());
return base::convert_to<int>(result_str);
} return num;
else
return -1;
} }
} // namespace app } // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -12,7 +13,10 @@
namespace app { 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 } // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -17,11 +18,21 @@ TEST(SplitFilename, Common)
std::string left, right; std::string left, right;
int width; 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(1, split_filename("C:\\test\\a1.png", left, right, width));
EXPECT_EQ("C:\\test\\a", left); EXPECT_EQ("C:\\test\\a", left);
EXPECT_EQ(".png", right); EXPECT_EQ(".png", right);
EXPECT_EQ(1, width); 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(1, split_filename("C:/test/a1.png", left, right, width));
EXPECT_EQ("C:/test/a", left); EXPECT_EQ("C:/test/a", left);
EXPECT_EQ(".png", right); EXPECT_EQ(".png", right);

View File

@ -29,6 +29,7 @@
#include "base/fs.h" #include "base/fs.h"
#include "base/log.h" #include "base/log.h"
#include "base/string.h" #include "base/string.h"
#include "base/utf8_decode.h"
#include "gfx/border.h" #include "gfx/border.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "gfx/rect.h" #include "gfx/rect.h"
@ -1131,14 +1132,13 @@ void SkinTheme::drawEntryText(ui::Graphics* g, ui::Entry* widget)
int scroll = delegate.index(); int scroll = delegate.index();
const std::string& textString = widget->text(); const std::string& textString = widget->text();
base::utf8_const_iterator utf8_it((textString.begin())); base::utf8_decode dec(textString);
int textlen = base::utf8_length(textString); auto pos = dec.pos();
scroll = std::min(scroll, textlen); for (int i=0; i<scroll && dec.next(); ++i)
if (scroll) pos = dec.pos();
utf8_it += scroll;
g->drawText(utf8_it, // TODO use a string_view()
base::utf8_const_iterator(textString.end()), g->drawText(std::string(pos, textString.end()),
colors.text(), ColorNone, colors.text(), ColorNone,
bounds.origin(), &delegate); bounds.origin(), &delegate);

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -47,10 +48,8 @@ doc::Image* render_text(const std::string& fontfile, int fontsize,
image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h)); image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h));
doc::clear_image(image.get(), 0); doc::clear_image(image.get(), 0);
ft::ForEachGlyph<ft::Face> feg(face); ft::ForEachGlyph<ft::Face> feg(face, text);
if (feg.initialize(base::utf8_const_iterator(text.begin()), while (feg.next()) {
base::utf8_const_iterator(text.end()))) {
do {
auto glyph = feg.glyph(); auto glyph = feg.glyph();
if (!glyph) if (!glyph)
continue; continue;
@ -92,7 +91,6 @@ doc::Image* render_text(const std::string& fontfile, int fontsize,
} }
} }
} }
} while (feg.nextChar());
} }
} }
else { else {

View File

@ -922,9 +922,7 @@ void Entry::recalcCharBoxes(const std::string& text)
{ {
int lastTextIndex = int(text.size()); int lastTextIndex = int(text.size());
CalcBoxesTextDelegate delegate(lastTextIndex); CalcBoxesTextDelegate delegate(lastTextIndex);
os::draw_text(nullptr, font(), os::draw_text(nullptr, font(), text,
base::utf8_const_iterator(text.begin()),
base::utf8_const_iterator(text.end()),
gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate); gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate);
m_boxes = delegate.boxes(); m_boxes = delegate.boxes();

View File

@ -362,8 +362,7 @@ void Graphics::setFont(const os::FontRef& font)
m_font = font; m_font = font;
} }
void Graphics::drawText(base::utf8_const_iterator it, void Graphics::drawText(const std::string& str,
const base::utf8_const_iterator& end,
gfx::Color fg, gfx::Color bg, gfx::Color fg, gfx::Color bg,
const gfx::Point& origPt, const gfx::Point& origPt,
os::DrawTextDelegate* delegate) os::DrawTextDelegate* delegate)
@ -372,18 +371,11 @@ void Graphics::drawText(base::utf8_const_iterator it,
os::SurfaceLock lock(m_surface.get()); os::SurfaceLock lock(m_surface.get());
gfx::Rect textBounds = 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)); 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 { namespace {
class DrawUITextDelegate : public os::DrawTextDelegate { class DrawUITextDelegate : public os::DrawTextDelegate {
@ -457,9 +449,7 @@ void Graphics::drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg,
int y = m_dy+pt.y; int y = m_dy+pt.y;
DrawUITextDelegate delegate(m_surface.get(), m_font.get(), mnemonic); DrawUITextDelegate delegate(m_surface.get(), m_font.get(), mnemonic);
os::draw_text(m_surface.get(), m_font.get(), os::draw_text(m_surface.get(), m_font.get(), str,
base::utf8_const_iterator(str.begin()),
base::utf8_const_iterator(str.end()),
fg, bg, x, y, &delegate); fg, bg, x, y, &delegate);
dirty(delegate.bounds()); dirty(delegate.bounds());
@ -482,9 +472,7 @@ gfx::Size Graphics::measureUIText(const std::string& str)
int Graphics::measureUITextLength(const std::string& str, os::Font* font) int Graphics::measureUITextLength(const std::string& str, os::Font* font)
{ {
DrawUITextDelegate delegate(nullptr, font, 0); DrawUITextDelegate delegate(nullptr, font, 0);
os::draw_text(nullptr, font, os::draw_text(nullptr, font, str,
base::utf8_const_iterator(str.begin()),
base::utf8_const_iterator(str.end()),
gfx::ColorNone, gfx::ColorNone, 0, 0, gfx::ColorNone, gfx::ColorNone, 0, 0,
&delegate); &delegate);
return delegate.bounds().w; return delegate.bounds().w;

View File

@ -117,11 +117,10 @@ namespace ui {
os::Font* font() { return m_font.get(); } os::Font* font() { return m_font.get(); }
void setFont(const os::FontRef& font); void setFont(const os::FontRef& font);
void drawText(base::utf8_const_iterator it, void drawText(const std::string& str,
const base::utf8_const_iterator& end, gfx::Color fg, gfx::Color bg,
gfx::Color fg, gfx::Color bg, const gfx::Point& pt, const gfx::Point& pt,
os::DrawTextDelegate* delegate); os::DrawTextDelegate* delegate = nullptr);
void drawText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt);
void drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, const int mnemonic); 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); void drawAlignedUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, const int align);

View File

@ -16,6 +16,7 @@
#include "base/clamp.h" #include "base/clamp.h"
#include "base/memory.h" #include "base/memory.h"
#include "base/string.h" #include "base/string.h"
#include "base/utf8_decode.h"
#include "os/font.h" #include "os/font.h"
#include "os/surface.h" #include "os/surface.h"
#include "os/system.h" #include "os/system.h"
@ -1400,19 +1401,18 @@ void Widget::processMnemonicFromText(int escapeChar)
if (!m_text.empty()) if (!m_text.empty())
newText.reserve(m_text.size()); newText.reserve(m_text.size());
for (base::utf8_const_iterator base::utf8_decode decode(m_text);
it(m_text.begin()), while (int chr = decode.next()) {
end(m_text.end()); it != end; ++it) { if (chr == escapeChar) {
if (*it == escapeChar) { chr = decode.next();
++it; if (!chr) {
if (it == end) {
break; // Ill-formed string (it ends with escape character) break; // Ill-formed string (it ends with escape character)
} }
else if (*it != escapeChar) { else if (chr != escapeChar) {
setMnemonic(*it); setMnemonic(chr);
} }
} }
newText.push_back(*it); newText.push_back(chr);
} }
setText(base::to_utf8(newText)); setText(base::to_utf8(newText));