Convert text drawn on the map to ISO8859-2 (which is what libgd uses)
Using non-ISO8859-2 encoded text (e.g. utf-8), or anything else, may result in garbage characters.axinite
parent
e47e3bcd3a
commit
570ed84fd2
|
@ -1,4 +1,4 @@
|
|||
project(minetestmapper CXX)
|
||||
project(minetestmapper CXX C)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
|
@ -139,6 +139,21 @@ if(NOT ZLIB_LIBRARY OR NOT ZLIB_INCLUDE_DIR)
|
|||
message(SEND_ERROR "zlib not found!")
|
||||
endif(NOT ZLIB_LIBRARY OR NOT ZLIB_INCLUDE_DIR)
|
||||
|
||||
# Find iconv
|
||||
OPTION(ENABLE_ICONV "Enable character encoding conversion of text written on the map" True)
|
||||
if(ENABLE_ICONV)
|
||||
find_package(Iconv)
|
||||
if(ICONV_FOUND)
|
||||
set(USE_ICONV 1)
|
||||
set(MAPPER_SRCS_ICONV CharEncodingConverterIConv.cpp)
|
||||
message (STATUS "iconv libraries: ${ICONV_LIBRARIES}")
|
||||
else(ICONV_FOUND)
|
||||
message (SEND_ERROR "iconv libraries not found")
|
||||
endif(ICONV_FOUND)
|
||||
else(ENABLE_ICONV)
|
||||
set(USE_ICONV 0)
|
||||
endif(ENABLE_ICONV)
|
||||
|
||||
find_package(PkgConfig)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
|
@ -245,6 +260,7 @@ include_directories(
|
|||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
${LIBGD_INCLUDE_DIR}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${ICONV_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(mapper_SRCS
|
||||
|
@ -256,12 +272,15 @@ set(mapper_SRCS
|
|||
Settings.cpp
|
||||
BlockPos.cpp
|
||||
mapper.cpp
|
||||
CharEncodingConverter.cpp
|
||||
${MAPPER_SRCS_ICONV}
|
||||
)
|
||||
|
||||
set(LINK_LIBRARIES
|
||||
minetestmapper
|
||||
${LIBGD_LIBRARY}
|
||||
${ZLIB_LIBRARY}
|
||||
${ICONV_LIBRARIES}
|
||||
)
|
||||
|
||||
if(USE_SQLITE3)
|
||||
|
|
|
@ -17,9 +17,14 @@
|
|||
- Command-line options are now all case-agnostic wrt their parameters.
|
||||
- Allow nodes to be defined as air-type or ignore-type in colors.txt
|
||||
- Added an option to draw 'ignore'-nodes.
|
||||
- Text drawn on the map is now converted to the ISO8859-2 encoding. Due
|
||||
to limited font support in the drawing library, characters not
|
||||
in the ISO8859-2 set are not supported.
|
||||
Minetestmapper will attempt to render unsupported characters
|
||||
sensibly if it can.
|
||||
Bugfixes:
|
||||
- Fixed possible compilation failure caused by stdint.h
|
||||
- Fixed compilation failure when some database libraries are not installed
|
||||
- Fixed compilation failure when some database libraries are not installed.
|
||||
Even when support for some of the databases was not enabled,
|
||||
the libraries for those databases still needed to be installed on
|
||||
the system for compilation to succeed. Now the libraries no
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#include <iostream>
|
||||
#include "cmake_config.h"
|
||||
|
||||
#include "CharEncodingConverter.h"
|
||||
#ifdef _WIN32
|
||||
#warning No standard charset converter defined for WIN32 - disabling conversion
|
||||
#else
|
||||
#include "CharEncodingConverterIConv.h"
|
||||
#endif
|
||||
|
||||
CharEncodingConverter *CharEncodingConverter::createStandardConverter(const std::string to, const std::string from)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return new CharEncodingConverterDummy(to, from);
|
||||
#else
|
||||
#if USE_ICONV
|
||||
return new CharEncodingConverterIConv(to, from);
|
||||
#else
|
||||
return new CharEncodingConverterDummy(to, from);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string CharEncodingConverter::getCurrentCharEncoding(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return CharEncodingConverterDummy::getCurrentCharEncoding();
|
||||
#else
|
||||
#if USE_ICONV
|
||||
return CharEncodingConverterIConv::getCurrentCharEncoding();
|
||||
#else
|
||||
return CharEncodingConverterDummy::getCurrentCharEncoding();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
#ifndef _CHARENCODINGCONVERTER_H_INCLUDED_
|
||||
#define _CHARENCODINGCONVERTER_H_INCLUDED_
|
||||
|
||||
#include <string>
|
||||
|
||||
class CharEncodingConverter
|
||||
{
|
||||
public:
|
||||
CharEncodingConverter(const std::string to, const std::string from)
|
||||
: m_toFormat(to), m_fromFormat(from) {
|
||||
if (m_toFormat == "") m_toFormat = getCurrentCharEncoding();
|
||||
if (m_fromFormat == "") m_fromFormat = getCurrentCharEncoding();
|
||||
}
|
||||
virtual ~CharEncodingConverter(void) {}
|
||||
|
||||
// Create a converter with an unspecified but suitable backend.
|
||||
// (for usage convenience)
|
||||
static CharEncodingConverter *createStandardConverter(const std::string to, const std::string from = "");
|
||||
static std::string getCurrentCharEncoding(void);
|
||||
|
||||
virtual std::string convert(const std::string &src) = 0;
|
||||
virtual std::string fromFormat(void) { return m_fromFormat; }
|
||||
virtual std::string toFormat(void) { return m_toFormat; }
|
||||
protected:
|
||||
std::string m_toFormat;
|
||||
std::string m_fromFormat;
|
||||
};
|
||||
|
||||
class CharEncodingConverterDummy : public CharEncodingConverter
|
||||
{
|
||||
public:
|
||||
CharEncodingConverterDummy(const std::string to, const std::string from = "")
|
||||
: CharEncodingConverter(to, from) {}
|
||||
virtual ~CharEncodingConverterDummy(void) {}
|
||||
|
||||
static std::string getCurrentCharEncoding(void) { return "UTF-8"; }
|
||||
std::string convert(const std::string &src) override { return src; }
|
||||
};
|
||||
|
||||
#endif // _CHARENCODINGCONVERTER_H_INCLUDED_
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
#include "CharEncodingConverterIConv.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <langinfo.h>
|
||||
|
||||
#define ICONV_BUFSIZE 16
|
||||
|
||||
std::string CharEncodingConverterIConv::getCurrentCharEncoding(void)
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
char *enc = nl_langinfo(CODESET);
|
||||
std::string encoding;
|
||||
if (*enc) {
|
||||
if (std::string(enc) == "ANSI_X3.4-1968")
|
||||
encoding = "US-ASCII";
|
||||
else
|
||||
encoding = enc;
|
||||
}
|
||||
else {
|
||||
std::cerr << "WARNING: could not determine current character encoding. Assuming UTF-8" << std::endl;
|
||||
encoding = "UTF-8";
|
||||
}
|
||||
// Reset locale to 'C'.
|
||||
// Advantage: unknown characters are converted to '?' instead of causing a failure...
|
||||
// Disadvantage: transliteration support may be more limited.
|
||||
// Alternative: modify conversion to replace unknown chars with '?' manually.
|
||||
setlocale(LC_CTYPE, "C");
|
||||
return encoding;
|
||||
}
|
||||
|
||||
|
||||
CharEncodingConverterIConv::CharEncodingConverterIConv(std::string to, std::string from)
|
||||
: CharEncodingConverter(to, from)
|
||||
{
|
||||
to = m_toFormat + "//TRANSLIT";
|
||||
from = m_fromFormat;
|
||||
m_iconv = iconv_open(to.c_str(), from.c_str());
|
||||
if (m_iconv == (iconv_t) -1) {
|
||||
int rno = errno;
|
||||
std::string msg = std::string("Error initializing iconv charset converter (")
|
||||
+ (from=="" ? std::string("(default)") : from) + " --> "
|
||||
+ (to=="" ? std::string("(default)") : to) + "): " + strerror(rno);
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
CharEncodingConverterIConv::~CharEncodingConverterIConv(void)
|
||||
{
|
||||
if (m_iconv != (iconv_t) -1)
|
||||
iconv_close(m_iconv);
|
||||
}
|
||||
|
||||
std::string CharEncodingConverterIConv::convert(const std::string &src)
|
||||
{
|
||||
std::string dst;
|
||||
|
||||
char toBuffer[ICONV_BUFSIZE + 1];
|
||||
char *fromBufP, *toBufP;
|
||||
size_t fromBufLen, toBufLen;
|
||||
|
||||
fromBufLen = src.length();
|
||||
// Assume that iconv() does not write to the source array...
|
||||
fromBufP = const_cast<char *>(src.c_str());
|
||||
toBufLen = ICONV_BUFSIZE;
|
||||
toBufP = toBuffer;
|
||||
|
||||
size_t rv;
|
||||
do {
|
||||
rv = iconv(m_iconv, &fromBufP, &fromBufLen, &toBufP, &toBufLen);
|
||||
if (rv == (size_t) -1) {
|
||||
int rno = errno;
|
||||
if (rno != E2BIG) {
|
||||
std::ostringstream oss;
|
||||
oss << "Failure converting character from "
|
||||
<< fromFormat() << " to " << toFormat()
|
||||
<< " (text: '[" << std::string(src.c_str(), 0, fromBufP - src.c_str()) << "]" << std::string(fromBufP) << "'): "
|
||||
<< strerror(rno);
|
||||
// Note: strerror() can be misleading, e.g. complaining about invalid input
|
||||
// when really the character cannot be represented in the output...
|
||||
// (but //TRANSLIT avoids (most of?) those kinds of errors...)
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
dst += std::string(toBuffer, ICONV_BUFSIZE - toBufLen);
|
||||
toBufLen = ICONV_BUFSIZE;
|
||||
toBufP = toBuffer;
|
||||
} while (rv == (size_t) -1);
|
||||
|
||||
iconv(m_iconv, NULL, NULL, NULL, NULL);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
#ifndef _CHARENCODINGCONVERTERICONV_H_INCLUDED_
|
||||
#define _CHARENCODINGCONVERTERICONV_H_INCLUDED_
|
||||
|
||||
#include <iconv.h>
|
||||
#include "CharEncodingConverter.h"
|
||||
|
||||
class CharEncodingConverterIConv : public CharEncodingConverter
|
||||
{
|
||||
public:
|
||||
CharEncodingConverterIConv(std::string to, std::string from = "");
|
||||
virtual ~CharEncodingConverterIConv(void);
|
||||
|
||||
static std::string getCurrentCharEncoding(void);
|
||||
std::string convert(const std::string &src) override;
|
||||
private:
|
||||
iconv_t m_iconv;
|
||||
};
|
||||
|
||||
#endif // _CHARENCODINGCONVERTERICONV_H_INCLUDED_
|
||||
|
|
@ -186,8 +186,13 @@ TileGenerator::TileGenerator():
|
|||
m_tileMapXOffset(0),
|
||||
m_tileMapYOffset(0),
|
||||
m_surfaceHeight(INT_MIN),
|
||||
m_surfaceDepth(INT_MAX)
|
||||
m_surfaceDepth(INT_MAX),
|
||||
m_gdStringConv(NULL)
|
||||
{
|
||||
// Libgd requires ISO8859-2 :-(
|
||||
// Internally, we use UTF-8. Assume minetest does the same... (if not, it's probably broken)
|
||||
m_gdStringConv = CharEncodingConverter::createStandardConverter("ISO8859-2", "UTF-8");
|
||||
|
||||
memset(&m_databaseFormatFound, 0, sizeof(m_databaseFormatFound));
|
||||
// Load default grey colors.
|
||||
m_heightMapColors.push_back(HeightMapColor(INT_MIN, Color(0,0,0), -129, Color(0,0,0)));
|
||||
|
@ -2239,9 +2244,10 @@ void TileGenerator::renderPlayers(const std::string &inputPath)
|
|||
for (PlayerAttributes::Players::iterator player = players.begin(); player != players.end(); ++player) {
|
||||
int imageX = worldX2ImageX(player->x / 10);
|
||||
int imageY = worldZ2ImageY(player->z / 10);
|
||||
std::string displayName = m_gdStringConv->convert(player->name);
|
||||
|
||||
gdImageArc(m_image, imageX, imageY, 5, 5, 0, 360, color);
|
||||
gdImageString(m_image, gdFontGetMediumBold(), imageX + 2, imageY + 2, reinterpret_cast<unsigned char *>(const_cast<char *>(player->name.c_str())), color);
|
||||
gdImageString(m_image, gdFontGetMediumBold(), imageX + 2, imageY + 2, reinterpret_cast<unsigned char *>(const_cast<char *>(displayName.c_str())), color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2376,8 +2382,11 @@ void TileGenerator::renderDrawObjects(void)
|
|||
case DrawObject::Rectangle:
|
||||
gdImageRectangle(m_image, o->corner1.x(), o->corner1.y(), o->corner2.x(), o->corner2.y(), o->color.to_libgd());
|
||||
break;
|
||||
case DrawObject::Text:
|
||||
gdImageString(m_image, gdFontGetMediumBold(), o->center.x(), o->center.y(), reinterpret_cast<unsigned char *>(const_cast<char *>(o->text.c_str())), o->color.to_libgd());
|
||||
case DrawObject::Text: {
|
||||
std::string displayText = m_gdStringConv->convert(o->text.c_str());
|
||||
gdImageString(m_image, gdFontGetMediumBold(), o->center.x(), o->center.y(),
|
||||
reinterpret_cast<unsigned char *>(const_cast<char *>(displayText.c_str())), o->color.to_libgd());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "CharEncodingConverter.h"
|
||||
#include "types.h"
|
||||
#include "PixelAttributes.h"
|
||||
#include "BlockPos.h"
|
||||
|
@ -317,6 +318,8 @@ private:
|
|||
uint16_t m_readedPixels[16];
|
||||
std::set<std::string> m_unknownNodes;
|
||||
std::vector<DrawObject> m_drawObjects;
|
||||
|
||||
CharEncodingConverter *m_gdStringConv;
|
||||
}; /* ----- end of class TileGenerator ----- */
|
||||
|
||||
#endif /* end of include guard: TILEGENERATOR_H_JJNUCARH */
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#define USE_LEVELDB @USE_LEVELDB@
|
||||
#define USE_REDIS @USE_REDIS@
|
||||
|
||||
#define USE_ICONV @USE_ICONV@
|
||||
|
||||
#define VERSION_MAJOR "@VERSION_MAJOR@"
|
||||
#define VERSION_MINOR "@VERSION_MINOR@"
|
||||
|
||||
|
|
|
@ -110,3 +110,7 @@ Known Problems
|
|||
``#4x91a1`` on another system)
|
||||
|
||||
The cause of this difference has not been determined yet.
|
||||
|
||||
* The drawing library supports ISO8859-2 fonts only. Characters in text and
|
||||
player names that cannot be converted to the ISO8859-2 character set will
|
||||
not be rendered correctly.
|
||||
|
|
|
@ -654,6 +654,11 @@ Detailed Description of Options
|
|||
'``$``', etc. On unix-like systems, use single quotes to avoid
|
||||
interpretation of most characters (except for ``'`` itself).
|
||||
|
||||
Due to a limitation of the drawing library, currently only text that
|
||||
can be represented in (i.e. converted to) the ISO8859-2 character set is
|
||||
supported. Text that uses non-compatible characters will not be rendered
|
||||
correctly.
|
||||
|
||||
Note that the combination of geometry, color and text should be a
|
||||
single argument. This means that they must be enclosed in quotes
|
||||
together on the command-line, else they will be misinterpreted as three
|
||||
|
@ -801,6 +806,9 @@ Detailed Description of Options
|
|||
|
||||
The color can be set with `--origincolor`_.
|
||||
|
||||
Just like for text drawn with `--draw[map]text`_, characters that cannot be converted
|
||||
to the ISO8859-2 character set will not be rendered correctly.
|
||||
|
||||
An image with a few players:
|
||||
|
||||
.. image:: images/players.png
|
||||
|
|
|
@ -758,6 +758,8 @@ int main(int argc, char *argv[])
|
|||
string heightMapNodesFile;
|
||||
bool foundGeometrySpec = false;
|
||||
bool setFixedOrShrinkGeometry = false;
|
||||
CharEncodingConverter *charConvUTF8;
|
||||
charConvUTF8 = CharEncodingConverter::createStandardConverter("UTF-8");
|
||||
|
||||
TileGenerator generator;
|
||||
try {
|
||||
|
@ -1408,14 +1410,16 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (object == 't') {
|
||||
iss >> std::ws;
|
||||
std::getline(iss, drawObject.text);
|
||||
if (drawObject.text.empty() || iss.fail()) {
|
||||
std::string localizedText;
|
||||
std::getline(iss, localizedText);
|
||||
if (localizedText.empty() || iss.fail()) {
|
||||
std::cerr << "Invalid or missing text for "
|
||||
<< long_options[option_index].name
|
||||
<< " '" << optarg << "'" << std::endl;
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
drawObject.text = charConvUTF8->convert(localizedText);
|
||||
}
|
||||
|
||||
generator.drawObject(drawObject);
|
||||
|
|
Loading…
Reference in New Issue