1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- support for Chinese fonts (GBK 2-byte encoding)

- fixes #1446
This commit is contained in:
Ivan Savenko 2013-09-08 16:49:23 +00:00
parent 42879225f0
commit e13dc872e0
7 changed files with 195 additions and 55 deletions

View File

@ -688,7 +688,7 @@ void processCommand(const std::string &message)
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/")));
std::ofstream outFile(outPath + outName);
std::ofstream outFile(outPath + outName, std::ofstream::binary);
outFile.write((char*)data.first.get(), data.second);
}
else

View File

@ -138,49 +138,45 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
while (text.length())
{
ui32 lineLength = 0; //in characters or given char metric
ui32 z = 0; //our position in text
ui32 wordBreak = -1;
ui32 currPos = 0; //our position in text
bool opened = false;//if we have an unclosed brace in current line
bool lineManuallyBroken = false;
while(z < text.length() && text[z] != 0x0a && lineLength < maxLineSize)
while(currPos < text.length() && text[currPos] != 0x0a && lineLength < maxLineSize)
{
if (ui8(text[currPos]) <= ui8(' ')) // candidate for line break
wordBreak = currPos;
/* We don't count braces in string length. */
if (text[z] == '{')
if (text[currPos] == '{')
opened=true;
else if (text[z]=='}')
else if (text[currPos]=='}')
opened=false;
else
lineLength += graphics->fonts[font]->getSymbolWidth(text[z]);
z++;
lineLength += graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
currPos += graphics->fonts[font]->getCharacterSize(text[currPos]);
}
if (z < text.length() && (text[z] != 0x0a))
if (currPos < text.length() && (text[currPos] != 0x0a))
{
/* We have a long line. Try to do a nice line break, if
* possible. We backtrack on the line until we find a
* suitable character.
* Note: Cyrillic symbols have indexes 220-255 so we need
* to use ui8 for comparison
*/
int pos = z-1;
while(pos > 0 && ((ui8)text[pos]) > ' ' )
pos --;
if (pos > 0)
z = pos+1;
// We have a long line. Try to do a nice line break, if possible
if (wordBreak != ui32(-1))
currPos = wordBreak;
else
currPos--;
}
if(z) //non-blank line
if(currPos) //non-blank line
{
ret.push_back(text.substr(0, z));
ret.push_back(text.substr(0, currPos));
if (opened)
/* Close the brace for the current line. */
ret.back() += '}';
text.erase(0, z);
text.erase(0, currPos);
}
else if(text[z] == 0x0a) //blank line
else if(text[currPos] == 0x0a) //blank line
{
ret.push_back(""); //add empty string, no extra actions needed
}

View File

@ -316,6 +316,7 @@ void Graphics::loadFonts()
const JsonVector & bmpConf = config["bitmap"].Vector();
const JsonNode & ttfConf = config["trueType"];
const JsonNode & hanConf = config["bitmapHan"];
assert(bmpConf.size() == FONTS_NUMBER);
@ -323,11 +324,12 @@ void Graphics::loadFonts()
{
std::string filename = bmpConf[i].String();
if (!ttfConf[filename].isNull()) // no ttf override
if (!hanConf[filename].isNull())
fonts[i] = new CBitmapHanFont(hanConf[filename]);
else if (!ttfConf[filename].isNull()) // no ttf override
fonts[i] = new CTrueTypeFont(ttfConf[filename]);
else
fonts[i] = new CBitmapFont(filename);
}
}

View File

@ -18,6 +18,16 @@
*
*/
size_t IFont::getStringWidth(const std::string & data) const
{
size_t width = 0;
for(size_t i=0; i<data.size(); i += getCharacterSize(data[i]))
{
width += getGlyphWidth(data.data() + i);
}
return width;
}
void IFont::renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
@ -105,23 +115,12 @@ size_t CBitmapFont::getLineHeight() const
return height;
}
size_t CBitmapFont::getSymbolWidth(char data) const
size_t CBitmapFont::getGlyphWidth(const char * data) const
{
const Char & ch = chars[ui8(data)];
const Char & ch = chars[ui8(*data)];
return ch.leftOffset + ch.width + ch.rightOffset;
}
size_t CBitmapFont::getStringWidth(const std::string & data) const
{
size_t width = 0;
for(auto & ch : data)
{
width += getSymbolWidth(ch);
}
return width;
}
void CBitmapFont::renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const
{
Rect clipRect;
@ -195,6 +194,11 @@ void CBitmapFont::renderText(SDL_Surface * surface, const std::string & data, co
SDL_UnlockSurface(surface);
}
size_t CBitmapFont::getCharacterSize(char data) const
{
return 1;
}
std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config)
{
std::string filename = "Data/" + config["file"].String();
@ -240,10 +244,10 @@ size_t CTrueTypeFont::getLineHeight() const
return TTF_FontHeight(font.get());
}
size_t CTrueTypeFont::getSymbolWidth(char data) const
size_t CTrueTypeFont::getGlyphWidth(const char *data) const
{
int advance;
TTF_GlyphMetrics(font.get(), data, nullptr, nullptr, nullptr, nullptr, &advance);
TTF_GlyphMetrics(font.get(), *data, nullptr, nullptr, nullptr, nullptr, &advance);
return advance;
}
@ -277,3 +281,109 @@ void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data,
SDL_FreeSurface(rendered);
}
}
size_t CTrueTypeFont::getCharacterSize(char data) const
{
return 1;
}
size_t CBitmapHanFont::getCharacterDataOffset(size_t index) const
{
size_t rowSize = (size + 7) / 8; // 1 bit per pixel, rounded up
size_t charSize = rowSize * size; // glyph contains "size" rows
return index * charSize;
}
size_t CBitmapHanFont::getCharacterIndex(ui8 first, ui8 second) const
{
if (second > 0x7f )
second--;
return (first - 0x81) * (12*16 - 2) + (second - 0x40);
}
void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const
{
//TODO: somewhat duplicated with CBitmapFont::renderCharacter();
Rect clipRect;
SDL_GetClipRect(surface, &clipRect);
TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
// start of line, may differ from 0 due to end of surface or clipped surface
int lineBegin = std::max<int>(0, clipRect.y - posY);
int lineEnd = std::min<int>(size, clipRect.y + clipRect.h - posY - 1);
// start end end of each row, may differ from 0
int rowBegin = std::max<int>(0, clipRect.x - posX);
int rowEnd = std::min<int>(size, clipRect.x + clipRect.w - posX - 1);
//for each line in symbol
for(int dy = lineBegin; dy <lineEnd; dy++)
{
Uint8 *dstLine = (Uint8*)surface->pixels;
Uint8 *source = data.first.get() + getCharacterDataOffset(characterIndex);
// shift source\destination pixels to current position
dstLine += (posY+dy) * surface->pitch + posX * bpp;
source += ((size + 7) / 8) * dy;
//for each column in line
for(int dx = rowBegin; dx < rowEnd; dx++)
{
// select current bit in bitmap
int bit = (source[dx / 8] << (dx % 8)) & 0x80;
Uint8* dstPixel = dstLine + dx*bpp;
if (bit != 0)
colorPutter(dstPixel, color.r, color.g, color.b);
}
}
posX += size;
}
void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
int posX = pos.x;
int posY = pos.y;
SDL_LockSurface(surface);
for(size_t i=0; i<data.size(); i += getCharacterSize(data[i]))
{
if (ui8(data[i]) < 0x80)
renderCharacter(surface, getCharacterIndex(0xa3, data[i] + 0x80), color, posX, posY);
else
renderCharacter(surface, getCharacterIndex(data[i], data[i+1]), color, posX, posY);
}
SDL_UnlockSurface(surface);
}
CBitmapHanFont::CBitmapHanFont(const JsonNode &config):
data(CResourceHandler::get()->load(ResourceID("data/" + config["name"].String(), EResType::OTHER))->readAll()),
size(config["size"].Float())
{
// basic tests to make sure that fonts are OK
// 1) fonts must contain 190 "sections", 126 symbols each.
assert(getCharacterIndex(0xfe, 0xff) == 190*126);
// ensure that font size is correct - enough to fit all possible symbols
assert(getCharacterDataOffset(getCharacterIndex(0xfe, 0xff)) == data.second);
}
size_t CBitmapHanFont::getLineHeight() const
{
return size;
}
size_t CBitmapHanFont::getGlyphWidth(const char * data) const
{
return size;
}
size_t CBitmapHanFont::getCharacterSize(char data) const
{
if (ui8(data) < 0x80)
return 1;
return 2;
}

View File

@ -18,6 +18,9 @@ struct SDL_Color;
typedef struct _TTF_Font TTF_Font;
class CBitmapFont;
class CBitmapHanFont;
class IFont
{
protected:
@ -30,10 +33,13 @@ public:
/// Returns height of font
virtual size_t getLineHeight() const = 0;
/// Returns width of a single symbol
virtual size_t getSymbolWidth(char data) const = 0;
/// Returns width, in pixels of a character glyph. Pointer must contain at least characterSize valid bytes
virtual size_t getGlyphWidth(const char * data) const = 0;
/// Returns size (in bytes) of one char in current encoding, may be bigger than one for non-ascii
/// TODO: move it out of this class. Separate entity for handling localization/different encodings?
virtual size_t getCharacterSize(char data) const = 0;
/// Return width of the string
virtual size_t getStringWidth(const std::string & data) const = 0;
virtual size_t getStringWidth(const std::string & data) const;
/**
* @param surface - destination to print text on
@ -77,13 +83,37 @@ class CBitmapFont : public IFont
void renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CBitmapFont(const std::string & filename);
size_t getLineHeight() const;
size_t getSymbolWidth(char data) const;
size_t getStringWidth(const std::string & data) const;
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
size_t getCharacterSize(char data) const override;
friend class CBitmapHanFont;
};
/// supports multi-byte characters for such languages like Chinese
class CBitmapHanFont : public IFont
{
// data, directly copied from file
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
// size of the font. Not available in file but needed for proper rendering
const size_t size;
size_t getCharacterDataOffset(size_t index) const;
size_t getCharacterIndex(ui8 first, ui8 second) const;
void renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CBitmapHanFont(const JsonNode & config);
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
size_t getCharacterSize(char data) const override;
};
class CTrueTypeFont : public IFont
@ -97,11 +127,12 @@ class CTrueTypeFont : public IFont
TTF_Font * loadFont(const JsonNode & config);
int getFontStyle(const JsonNode & config);
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CTrueTypeFont(const JsonNode & fontConfig);
size_t getLineHeight() const;
size_t getSymbolWidth(char data) const;
size_t getStringWidth(const std::string & data) const;
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
size_t getCharacterSize(char data) const override;
size_t getStringWidth(const std::string & data) const override;
};

View File

@ -66,8 +66,6 @@ void CDownloadManager::downloadFinished(QNetworkReply *reply)
file.status = FileEntry::FINISHED;
}
file.reply->deleteLater();
bool downloadComplete = true;
for (auto & entry : currentDownloads)
{
@ -91,6 +89,9 @@ void CDownloadManager::downloadFinished(QNetworkReply *reply)
if (downloadComplete)
emit finished(successful, failed, encounteredErrors);
file.reply->deleteLater();
file.reply = nullptr;
}
void CDownloadManager::downloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal)

View File

@ -191,7 +191,7 @@ bool ZipArchive::extract(std::string from, std::string where, std::vector<std::s
if (boost::algorithm::ends_with(file, "/"))
continue;
std::ofstream destFile(fullName);
std::ofstream destFile(fullName, std::ofstream::binary);
if (!destFile.good())
return false;