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
// 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 <cstring>
@ -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<int>(result_str);
}
else
return -1;
if (width > 0)
left.erase(left.end()-width, left.end());
return num;
}
} // namespace app

View File

@ -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

View File

@ -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);

View File

@ -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; i<scroll && dec.next(); ++i)
pos = dec.pos();
g->drawText(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);

View File

@ -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<ft::Face> 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<ft::Face> 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; v<int(glyph->bitmap->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; v<int(glyph->bitmap->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; u<int(glyph->bitmap->width); ++u, ++ximg) {
int alpha;
for (int u=0; u<int(glyph->bitmap->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 {

View File

@ -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();

View File

@ -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;
}

View File

@ -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);

View File

@ -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));