Qt: Safe parsing of Windows format data under Linux - c++

I have a Server-Client application in which JSON data is send between those. The Client has a Linux and a Windows version, while the Server application runs under Linux.
The Linux Client communicates just find, but I have problems with the Windows Client.
The problematic JSON data contains a text field with an apostrophe. Let's say the content is "a dog`s name", then the Windows client sends this as "a dog\x92s name", while the Linux client goes for "a dog\xE2\x80\x99s name", at least that is what qDebug() shows me.
I parse the JSON data with the lines
QJsonDocument document = QJsonDocument::fromJson(body);
if(document.isArray()) json_data = document.array();
if(document.isObject()) json_data.append(document.object());
where body is a QByteArray and json_data is a QJsonArray.
If the Windows data is fed into this, it seems that the Qt JSON parser does not recognize it as valid JSON and thus json_data end up being empty.
I really don't want to do anything manually with that text specific to those very characters, as I want it not only to work with that apostrophe but with all kinds of special characters that a user might enter in general. Is there some way to handle this in general? I assume the Windows is in something like the Windows-1252 encoding?

I think windows client sends strings encoded in CP1251 or CP1252. And json decoder expects utf-8.
Maybe source code is not in utf-8 and has string literals. Qt4 has QTextCodec::setCodecForCStrings. Qt5 assume string literals encoded in utf-8.
$ echo -n "’" | iconv -f utf-8 -t cp1251 | xxd
00000000: 92
$ echo -n "’" | xxd
00000000: e280 99
If you don't want to fix windows client the proper way (fixing it's output encoding) you can deal with this situation by converting all input from windows client to unicode before building QJsonDocument on server.
QByteArray bodycp1252;
QTextCodec* cp1252 = QTextCodec::codecForName("CP1252");
QTextCodec* utf8 = QTextCodec::codecForName("UTF-8");
QByteArray body = utf8->fromUnicode(cp1252->toUnicode(bodycp1252));
QJsonDocument document = QJsonDocument::fromJson(body);
It's possible to check if QByteArray contains valid utf-8 data with QUtf8::isValidUtf8(const char *chars, qsizetype len) function. It is defined in private headers, so you need to add QT += core-private. Unfortunately implementation is not visible by linker (not exported from QtCore.lib) so you need to add qutfcodec.cpp from qt sources to your project to resolve linker errors.
////////////////// is-valid-utf8.pro
QT -= gui
QT += core core-private
CONFIG += c++11 console
CONFIG -= app_bundle
qt_src = "C:/Qt/5.15.1/Src"
SOURCES += \
main.cpp \
$$qt_src/qtbase/src/corelib/codecs/qutfcodec.cpp
////////////////// main.cpp
#include <QCoreApplication>
#include <private/qutfcodec_p.h>
#include <QTextCodec>
#include <QDebug>
bool isValidUtf8(const QByteArray& data) {
return QUtf8::isValidUtf8(data.data(), data.size()).isValidUtf8;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextCodec* utf8 = QTextCodec::codecForName("UTF-8");
QTextCodec* cp1251 = QTextCodec::codecForName("CP1251");
QByteArray utf8data1 = utf8->fromUnicode("Привет мир");
QByteArray cp1251data1 = cp1251->fromUnicode("Привет мир");
QByteArray utf8data2 = utf8->fromUnicode("Hello world");
QByteArray cp1251data2 = cp1251->fromUnicode("Hello world");
Q_ASSERT(isValidUtf8(utf8data1));
Q_ASSERT(isValidUtf8(cp1251data1) == false);
Q_ASSERT(isValidUtf8(utf8data2));
Q_ASSERT(isValidUtf8(cp1251data2));
qDebug() << "test passed";
return 0;
}
source

Related

C++ MySQL Connector no utf8

I have a problem getting UTF-8 strings from MySQL database. I use C++ connector 1.1 and connect with following code:
sql::ConnectOptionsMap connection_properties;
connection_properties["hostName"] = server;
connection_properties["userName"] = user;
connection_properties["password"] = password;
connection_properties["schema"] = database;
connection_properties["port"] = 3306;
connection_properties["OPT_CHARSET_NAME"] = "utf8";
connection_properties["characterSetResults"] = "utf8";
connection_properties["preInit"] = "SET NAMES utf8";
driver = get_driver_instance();
con = driver->connect(connection_properties);
con->setSchema(database);
I already tried different utf8 options as you see....
If a statement should return database strings like "アフガニスタン" I only see chars like this "アフガニスタン" when I use Visual Studio debugger. The observed code:
std::string name = res->getString(2);
After Json encode it prints "ÒéóÒâòÒé¼ÒâïÒé╣Òé┐Òâ│" into command line.
Other utf8 columns with normal latin characters are returned as expected. It only affects translation columns with non latin chars.
Same database call from PHP with same logic (db connection and json encode) on same PC prints out following chars "\u30a2\u30d5\u30ac\u30cb\u30b9\u30bf\u30f3".
Any ideas about that?
Actually there is no problem. I wrote returned data into a file and all UTF-8 characters are correct. Debugger and CMD are not able to display UTF-8 data as expected...

Printing a PDF file from Qt

I have found following code snippet which works as my expectation, but the problem is that, when a PDF file is open and user print the PDF file with some other printer from the PDF reader and not close the PDF reader and again print the PDF file from my application, it will print with printer which is configured on PDF reader not the printer which I set as to print the PDF file.
Example:
On application I set Printer_1 as to print PDF file.
Print the file (it will open the file in PDF reader and print with Printer_1 printer).
On PDF reader I go to CTRL + P to print and there I select Printer_2 and click on print.
I don't close the PDF reader and again on application (printer is selected to Printer_1), I print the PDF file. Now the print command is sent to Printer_2 instead of Printer_1.
Please what is the problem in the following code snippet?
#include <QSettings>
#include <QProcess>
#include <QDebug>
int main(int argc, char *argv[])
{
const QString classesRoot = "HKEY_CLASSES_ROOT";
// get ID of .pdf extension
QSettings pdfSettings(classesRoot + "\\.pdf", QSettings::NativeFormat);
QString pdfId = pdfSettings.value("Default").toString();
// get path to default program that associated with PDF files
QString printPath = QSettings(classesRoot + "\\" + pdfId + "\\shell\\print\\command", QSettings::NativeFormat).value("Default").toString();
QString openPath = QSettings(classesRoot + "\\" + pdfId + "\\shell\\open\\command", QSettings::NativeFormat).value("Default").toString();
qDebug() << "print path" << printPath;
qDebug() << "open path" << openPath;
// open .pdf file
QProcess::startDetached(openPath.arg("full path to pdf file.pdf") );
// print .pdf file
QProcess printProcess;
printProcess.start(printPath.arg("full path to pdf file.pdf") );
printProcess.waitForFinished(-1);
return 0;
}
instead of line
QString printPath = QSettings(classesRoot + "\\" + pdfId + "\\shell\\print\\command", QSettings::NativeFormat).value("Default").toString();
use this and all works as expected:
QString printPath = QSettings(classesRoot + "\\" + pdfId + "\\shell\\printto\\command", QSettings::NativeFormat).value("Default").toString();
And pass "filename", "printer name" ,"printer driver" and "port" [driver name and port are not mandatory]
I'm sorry but I'd said that this seems reasonably to be the expected behaviour: if the application is already open and it is designed not to open separate instances for the same file (as many viewers do and as yours does), then is highly probable that current settings (in your case, the current printer) are preserved too, since, basically, nothing has changed. If you reproduce your steps but replacing your program by your own manual opening of the PDF file, I'm quite confident that you'll see the same results.
An option would be to avoid opening an external viewer and directly load the PDF file (currently using a third-party library, such as Poppler) and print it using Qt's modules. You can check this answer for more information about it.

Qt toBase64 and Linux base64 differs

Subj. Why can it be?
I've tried all the flags for Qt toBase64 but nothing changed.
Bash:
cat Invader_1.png | base64
Output:
iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAALEwAACxMBAJqcGAAAA0JJREFUeJzt3MFt20AURdHnQIWoFLoSq5S4E7uSsBR24mxiIIsgSDIT
cex3DjBLjT5J4ULcTAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAACcY0vyNmFt9x27yhbPaKovZw8AnEcAoJgAQDEBgGICAMUEAIoJABQTACgm
AFBMAKCYAEAxAYBiAgDFBACKCQAUEwAodjl7gIUcSZ4n7fV18PNHkpfhKdZyS3Id3GPPnGd0TNgD
fmnP+Ik1+51nvoc97styvAJAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUEAIoJABQT
ACgmAFBMAACSbBk/sML6GGsLSfwDgGoCAMUEAIoJABQTACgmAFBMAKCYAEAxAYBiAgDFBACKCQAU
EwAoJgBQTACgmABAscvZA0xyS3Id3GP08+9ekxyDe1yTPA1PspaV7sst44eCHEleBvdgkj3nnzIz
87SZbYHrcF9+v/YJ13M6rwBQTACgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUE
AIoJABQTACj2cPL3b0m+nTwDnOkxJx4u4h8AFBMAKCYAUEwAoJgAQDEBgGICAMUEAIoJABQTACgm
AFBMAKCYAEAxAYBiAgDFBACKXc4eYJLXJMfgHtckT8OT8BH4vSxiS/I2YW0LzWKtv7aM2xaa5Z95
BYBiAgDFBACKCQAUEwAoJgBQTACgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBmO8xyYP1
X9bjXzwH/oAAQDEBgGICAMUEAIoJABQTACgmAFBMAKCYAEAxAYBiAgDFBACKCQAUEwAoJgBQTACg
2OXk7z+SPE/aZ8YeM2bZfqwVvGT83lyT3Ab3mGXP5/u9HBP2YCF7krdF1jbherYFruN97ROuh594 BYBiAgDFBACKCQAUEwAoJgBQTACgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAn3wG6mDu8
W58tNgAAAABJRU5ErkJggg==
Qt:
QImage image;
image.load("/home/undead/Pictures/1489180309_Invader_1.png");
image.save(&buffer, "PNG");
buffer.close();
bytes_array = bytes_array.toBase64();
Output:
iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAADQklEQVR4nO3cwW3bQBRF0edAhagUuhKrlLgTu5KwFHbibGIgiyBIMhNx7HcOMEuNPknhQtxMAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJxjS_I2YW33HbvKFs9oqi9nDwCcRwCgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUEAIoJABQTACh2OXuAhRxJnift9XXw80eSl-Ep1nJLch3cY8-cZ3RM2AN-ac_4iTX7nWe-hz3uy3K8AkAxAYBiAgDFBACKCQAUEwAoJgBQTACgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAAJJsGT-wwvoYawtJ_AOAagIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUEAIoJABQTACgmAFBMAKCYAECxy9kDTHJLch3cY_Tz716THIN7XJM8DU-ylpXuyy3jh4IcSV4G92CSPeefMjPztJltgetwX36_9gnXczqvAFBMAKCYAEAxAYBiAgDFBACKCQAUEwAoJgBQTACgmABAMQGAYgIAxQQAigkAFBMAKPZw8vdvSb6dPAOc6TEnHi7iHwAUEwAoJgBQTACgmABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUEAIpdzh5gktckx-Ae1yRPw5PwEfi9LGJL8jZhbQvNYq2_tozbFprln3kFgGICAMUEAIoJABQTACgmAFBMAKCYAEAxAYBiAgDFBACKCQAUEwAoJgBQTACgmABAMQGY7zHJg_Vf1uNfPAf-gABAMQGAYgIAxQQAigkAFBMAKCYAUEwAoJgAQDEBgGICAMUEAIoJABQTACgmAFBMAKDY5eTvP5I8T9pnxh4zZtl-rBW8ZPzeXJPcBveYZc_n-70cE_ZgIXuSt0XWNuF6tgWu433tE66Hn3gFgGICAMUEAIoJABQTACgmAFBMAKCYAEAxAYBiAgDFBACKCQAUEwAoJgBQTACgmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCffAbqYO7xbny02AAAAAElFTkSuQmCC
You are comparing 2 different things.
cat Invader_1.png | base64
Outputs the base64 binary content of Invader_1.png.
While
QImage image;
image.load("/home/undead/Pictures/1489180309_Invader_1.png");
image.save(&buffer, "PNG");
buffer.close();
bytes_array = bytes_array.toBase64();
Converts 1489180309_Invader_1.png into a pixel image and back to a PNG and then outputs the base64 binary content of the new PNG.
And as stated by #JustRufus and #TobySpeight, a PNG of the same image can be created with different settings (e.g compression levels) and/or metadata leading to different binary contents.
If you want to compare things you should fix your Qt code to just read the file:
QFile file("/home/undead/Pictures/1489180309_Invader_1.png");
file.open(QFile::ReadOnly);
QByteArray data = file.readAll();
file.close();
data = data.toBase64();

Search for UTF encoded characters using QRegExp

I'm trying to check for characters §£¤ using QRegExp.
QString string = "§¤£";
int res = string.count(QRegExp("[§¤£]"));
And the res returns 0.
Edit your .pro file and set the following:
CODECFORSRC = UTF-8
CODECFORTR = UTF-8
Then add to your .cpp file:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
That will give you the UTF-8 support for your soruce and internationalization if you need it.

How to read a XML file using fstream in IOS

I am using XCode, and here is a c++ code in XCode
std::fstream stream("templates.Xml", std::ios::binary | std::ios::in);
if(!stream) return false;
I put the Xml file in the folder that contain the ".xcodeproj", and I put it in the folder that contains ".app" but the stream always return false, why?
It looks like you are trying to open the file from the wrong directory.
"templates.Xml" is saved in the bundle- is not saved in the documents directory. By default, if you open "./filename", this actually points to:
/Users/arinmorf/Library/Application Support/iPhone Simulator/7.0.3/Applications/246E91F9-FAB2-4A46-B1F1-855B5363F24D/Documents/
Where arinmorf would be your username and the long hex string is randomly generated every time you install the app on the simulator.
The templates.xml file would be found in:
/Users/arinmorf/Library/Application Support/iPhone Simulator/7.0.3/Applications/246E91F9-FAB2-4A46-B1F1-855B5363F24D/iFly.app/templates.xml
iFly.app is the name of my app, yours would be "T". BUT you can't use the absolute path in your project, because of the randomly generated string, you need to use the NSBundle or CFBundleRef.
In objective-C, you would use:
filePath = [[NSBundle mainBundle] pathForResource:#"templates" ofType:#"xml"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
In C++ it looks like:
CFURLRef fileURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("templates"), CFSTR("xml"), NULL);
CFStringRef filePath = CFURLCopyFileSystemPath(fileURL, kCFURLPOSIXPathStyle);
CFStringEncoding encodingMethod = CFStringGetSystemEncoding();
const char *path = CFStringGetCStringPtr(filePath, encodingMethod);
FILE* f = fopen(path, "r");
(Credit to Chris Frederick at https://stackoverflow.com/a/8768366/2070758 for C++ version)
Yes, I solved the problem in two ways either depending on Main Bundle, or create custom bundle and use it.