mirror of https://github.com/aseprite/aseprite.git
Replace utf8 iterators with utf8_decode (fix #3260)
This should fix some problems decoding invalid UTF-8 strings.
This commit is contained in:
parent
75a99360a0
commit
65ef6f8e96
2
laf
2
laf
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4f4693a8e63b7bc63e3c0825112f7f3f190ebee9
|
Subproject commit df53f4ac0cecada789bf84b4283947d2591833bd
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue