Text codec used when redirecting output

This commit is contained in:
Jakub Melka 2020-10-01 16:10:08 +02:00
parent 7bded071ea
commit 03f7f01c7d
6 changed files with 84 additions and 28 deletions

View File

@ -17,6 +17,7 @@
#include "pdfoutputformatter.h" #include "pdfoutputformatter.h"
#include <QTextCodec>
#include <QTextStream> #include <QTextStream>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include <QCoreApplication> #include <QCoreApplication>
@ -91,7 +92,7 @@ private:
class PDFXmlOutputFormatterImpl : public PDFOutputFormatterImpl class PDFXmlOutputFormatterImpl : public PDFOutputFormatterImpl
{ {
public: public:
PDFXmlOutputFormatterImpl(); PDFXmlOutputFormatterImpl(QString codec);
virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override; virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override;
virtual void endElement() override; virtual void endElement() override;
@ -109,7 +110,7 @@ private:
class PDFHtmlOutputFormatterImpl : public PDFOutputFormatterImpl class PDFHtmlOutputFormatterImpl : public PDFOutputFormatterImpl
{ {
public: public:
PDFHtmlOutputFormatterImpl(); PDFHtmlOutputFormatterImpl(QString codecName);
virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override; virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override;
virtual void endElement() override; virtual void endElement() override;
@ -308,14 +309,17 @@ const PDFTextOutputFormatterImpl::TableCell& PDFTextOutputFormatterImpl::getTabl
return dummy; return dummy;
} }
PDFHtmlOutputFormatterImpl::PDFHtmlOutputFormatterImpl() : PDFHtmlOutputFormatterImpl::PDFHtmlOutputFormatterImpl(QString codecName) :
m_string(), m_string(),
m_streamWriter(&m_string), m_streamWriter(&m_string),
m_depth(0), m_depth(0),
m_headerDepth(1), m_headerDepth(1),
m_elementStack() m_elementStack()
{ {
if (QTextCodec* codec = QTextCodec::codecForName(codecName.toLatin1()))
{
m_streamWriter.setCodec(codec);
}
} }
void PDFHtmlOutputFormatterImpl::beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) void PDFHtmlOutputFormatterImpl::beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference)
@ -483,7 +487,7 @@ void PDFHtmlOutputFormatterImpl::endl()
m_streamWriter.writeEndElement(); m_streamWriter.writeEndElement();
} }
PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl() : PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl(QString codecName) :
m_string(), m_string(),
m_streamWriter(&m_string), m_streamWriter(&m_string),
m_depth(0) m_depth(0)
@ -491,6 +495,11 @@ PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl() :
m_streamWriter.setAutoFormatting(true); m_streamWriter.setAutoFormatting(true);
m_streamWriter.setAutoFormattingIndent(2); m_streamWriter.setAutoFormattingIndent(2);
if (QTextCodec* codec = QTextCodec::codecForName(codecName.toLatin1()))
{
m_streamWriter.setCodec(codec);
}
m_namespace = "https://github.com/JakubMelka/PdfForQt"; m_namespace = "https://github.com/JakubMelka/PdfForQt";
m_prefix = "pdftool"; m_prefix = "pdftool";
} }
@ -575,7 +584,7 @@ QString PDFXmlOutputFormatterImpl::getString() const
return m_string; return m_string;
} }
PDFOutputFormatter::PDFOutputFormatter(Style style) : PDFOutputFormatter::PDFOutputFormatter(Style style, QString codecName) :
m_impl(nullptr) m_impl(nullptr)
{ {
switch (style) switch (style)
@ -585,11 +594,11 @@ PDFOutputFormatter::PDFOutputFormatter(Style style) :
break; break;
case Style::Xml: case Style::Xml:
m_impl = new PDFXmlOutputFormatterImpl(); m_impl = new PDFXmlOutputFormatterImpl(codecName);
break; break;
case Style::Html: case Style::Html:
m_impl = new PDFHtmlOutputFormatterImpl(); m_impl = new PDFHtmlOutputFormatterImpl(codecName);
break; break;
} }
@ -621,19 +630,39 @@ QString PDFOutputFormatter::getString() const
return m_impl->getString(); return m_impl->getString();
} }
void PDFConsole::writeText(QString text) void PDFConsole::writeText(QString text, QString codecName)
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), text.utf16(), text.size(), nullptr, nullptr); HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (!WriteConsoleW(outputHandle, text.utf16(), text.size(), nullptr, nullptr))
{
// Write console failed. This can happen only, if outputHandle is not handle
// to console screen buffer, but, for example a file or a pipe.
if (QTextCodec* codec = QTextCodec::codecForName(codecName.toLatin1()))
{
QByteArray encodedData = codec->fromUnicode(text);
WriteFile(outputHandle, encodedData.constData(), encodedData.size(), nullptr, nullptr);
}
}
#else #else
QTextStream(stdout) << text; QTextStream(stdout) << text;
#endif #endif
} }
void PDFConsole::writeError(QString text) void PDFConsole::writeError(QString text, QString codecName)
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), text.utf16(), text.size(), nullptr, nullptr); HANDLE outputHandle = GetStdHandle(STD_ERROR_HANDLE);
if (!WriteConsoleW(outputHandle, text.utf16(), text.size(), nullptr, nullptr))
{
// Write console failed. This can happen only, if outputHandle is not handle
// to console screen buffer, but, for example a file or a pipe.
if (QTextCodec* codec = QTextCodec::codecForName(codecName.toLatin1()))
{
QByteArray encodedData = codec->fromUnicode(text);
WriteFile(outputHandle, encodedData.constData(), encodedData.size(), nullptr, nullptr);
}
}
#else #else
QTextStream(stdout) << text; QTextStream(stdout) << text;
#endif #endif

View File

@ -37,7 +37,7 @@ public:
Html Html
}; };
explicit PDFOutputFormatter(Style style); explicit PDFOutputFormatter(Style style, QString codecName);
~PDFOutputFormatter(); ~PDFOutputFormatter();
enum class Element enum class Element
@ -97,10 +97,10 @@ class PDFConsole
public: public:
/// Writes text to the console /// Writes text to the console
static void writeText(QString text); static void writeText(QString text, QString codecName);
/// Writes error to the console /// Writes error to the console
static void writeError(QString text); static void writeError(QString text, QString codecName);
private: private:
explicit PDFConsole() = delete; explicit PDFConsole() = delete;

View File

@ -58,7 +58,7 @@ QString PDFToolHelpApplication::getStandardString(StandardString standardString)
int PDFToolHelpApplication::execute(const PDFToolOptions& options) int PDFToolHelpApplication::execute(const PDFToolOptions& options)
{ {
PDFOutputFormatter formatter(options.outputStyle); PDFOutputFormatter formatter(options.outputStyle, options.outputCodec);
formatter.beginDocument("help", PDFToolTranslationContext::tr("PDFTool help")); formatter.beginDocument("help", PDFToolTranslationContext::tr("PDFTool help"));
formatter.endl(); formatter.endl();
@ -107,9 +107,27 @@ int PDFToolHelpApplication::execute(const PDFToolOptions& options)
formatter.endTable(); formatter.endTable();
formatter.endl();
formatter.beginHeader("text-output", PDFToolTranslationContext::tr("Text Encoding"));
formatter.writeText("header", PDFToolTranslationContext::tr("When you redirect console to a file, then specific codec is used to transform output text to target encoding. UTF-8 encoding is used by default. For XML output, you should use only UTF-8 codec. Available codecs:"));
formatter.endl();
QList<QByteArray> codecs = QTextCodec::availableCodecs();
QStringList codecNames;
for (const QByteArray& codecName : codecs)
{
codecNames << QString::fromLatin1(codecName);
}
formatter.writeText("codecs", codecNames.join(", "));
formatter.endl();
formatter.writeText("default-codec", PDFToolTranslationContext::tr("Suggested codec: UTF-8 or %1").arg(QString::fromLatin1(QTextCodec::codecForLocale()->name())));
formatter.endHeader();
formatter.endDocument(); formatter.endDocument();
PDFConsole::writeText(formatter.getString()); PDFConsole::writeText(formatter.getString(), options.outputCodec);
return ExitSuccess; return ExitSuccess;
} }
@ -130,6 +148,7 @@ void PDFToolAbstractApplication::initializeCommandLineParser(QCommandLineParser*
if (optionFlags.testFlag(ConsoleFormat)) if (optionFlags.testFlag(ConsoleFormat))
{ {
parser->addOption(QCommandLineOption("console-format", "Console output text format (valid values: text|xml|html).", "console-format", "text")); parser->addOption(QCommandLineOption("console-format", "Console output text format (valid values: text|xml|html).", "console-format", "text"));
parser->addOption(QCommandLineOption("text-codec", QString("Text codec used when writing text output to redirected standard output. UTF-8 is default."), "text-codec", "UTF-8"));
} }
if (optionFlags.testFlag(OpenDocument)) if (optionFlags.testFlag(OpenDocument))
@ -184,11 +203,13 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser
{ {
if (!consoleFormat.isEmpty()) if (!consoleFormat.isEmpty())
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Unknown console format '%1'. Defaulting to text console format.").arg(consoleFormat)); PDFConsole::writeError(PDFToolTranslationContext::tr("Unknown console format '%1'. Defaulting to text console format.").arg(consoleFormat), options.outputCodec);
} }
options.outputStyle = PDFOutputFormatter::Style::Text; options.outputStyle = PDFOutputFormatter::Style::Text;
} }
options.outputCodec = parser->value("text-codec");
} }
if (optionFlags.testFlag(OpenDocument)) if (optionFlags.testFlag(OpenDocument))
@ -225,7 +246,7 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser
} }
else if (!dateFormat.isEmpty()) else if (!dateFormat.isEmpty())
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Unknown console date/time format '%1'. Defaulting to short date/time format.").arg(dateFormat)); PDFConsole::writeError(PDFToolTranslationContext::tr("Unknown console date/time format '%1'. Defaulting to short date/time format.").arg(dateFormat), options.outputCodec);
} }
} }
@ -259,13 +280,13 @@ bool PDFToolAbstractApplication::readDocument(const PDFToolOptions& options, pdf
case pdf::PDFDocumentReader::Result::Cancelled: case pdf::PDFDocumentReader::Result::Cancelled:
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid password provided.")); PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid password provided."), options.outputCodec);
return false; return false;
} }
case pdf::PDFDocumentReader::Result::Failed: case pdf::PDFDocumentReader::Result::Failed:
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Error occured during document reading. %1").arg(reader.getErrorMessage())); PDFConsole::writeError(PDFToolTranslationContext::tr("Error occured during document reading. %1").arg(reader.getErrorMessage()), options.outputCodec);
return false; return false;
} }
@ -278,7 +299,7 @@ bool PDFToolAbstractApplication::readDocument(const PDFToolOptions& options, pdf
for (const QString& warning : reader.getWarnings()) for (const QString& warning : reader.getWarnings())
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Warning: %1").arg(warning)); PDFConsole::writeError(PDFToolTranslationContext::tr("Warning: %1").arg(warning), options.outputCodec);
} }
return true; return true;

View File

@ -42,6 +42,7 @@ struct PDFToolOptions
{ {
// For option 'ConsoleFormat' // For option 'ConsoleFormat'
PDFOutputFormatter::Style outputStyle = PDFOutputFormatter::Style::Text; PDFOutputFormatter::Style outputStyle = PDFOutputFormatter::Style::Text;
QString outputCodec;
// For option 'OpenDocument' // For option 'OpenDocument'
QString document; QString document;

View File

@ -52,7 +52,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options)
// No document specified? // No document specified?
if (options.document.isEmpty()) if (options.document.isEmpty())
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("No document specified.")); PDFConsole::writeError(PDFToolTranslationContext::tr("No document specified."), options.outputCodec);
return ErrorNoDocumentSpecified; return ErrorNoDocumentSpecified;
} }
@ -76,7 +76,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options)
case pdf::PDFDocumentReader::Result::Failed: case pdf::PDFDocumentReader::Result::Failed:
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Error occured during document reading. %1").arg(reader.getErrorMessage())); PDFConsole::writeError(PDFToolTranslationContext::tr("Error occured during document reading. %1").arg(reader.getErrorMessage()), options.outputCodec);
return ErrorDocumentReading; return ErrorDocumentReading;
} }
@ -87,7 +87,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options)
for (const QString& warning : reader.getWarnings()) for (const QString& warning : reader.getWarnings())
{ {
PDFConsole::writeError(PDFToolTranslationContext::tr("Warning: %1").arg(warning)); PDFConsole::writeError(PDFToolTranslationContext::tr("Warning: %1").arg(warning), options.outputCodec);
} }
// Verify signatures // Verify signatures
@ -107,7 +107,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options)
pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject()); pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject());
std::vector<pdf::PDFSignatureVerificationResult> signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource(), parameters); std::vector<pdf::PDFSignatureVerificationResult> signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource(), parameters);
PDFOutputFormatter formatter(options.outputStyle); PDFOutputFormatter formatter(options.outputStyle, options.outputCodec);
formatter.beginDocument("signatures", PDFToolTranslationContext::tr("Digital signatures/timestamps verification of %1").arg(options.document)); formatter.beginDocument("signatures", PDFToolTranslationContext::tr("Digital signatures/timestamps verification of %1").arg(options.document));
formatter.endl(); formatter.endl();
@ -386,7 +386,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options)
formatter.endDocument(); formatter.endDocument();
PDFConsole::writeText(formatter.getString()); PDFConsole::writeText(formatter.getString(), options.outputCodec);
return ExitSuccess; return ExitSuccess;
} }

View File

@ -210,6 +210,11 @@ int PDFToolXmlApplication::execute(const PDFToolOptions& options)
writer.setAutoFormattingIndent(2); writer.setAutoFormattingIndent(2);
} }
if (QTextCodec* codec = QTextCodec::codecForName(options.outputCodec.toLatin1()))
{
writer.setCodec(codec);
}
QString comment = QString("Processed by %1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()); QString comment = QString("Processed by %1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion());
writer.writeStartDocument(); writer.writeStartDocument();
writer.writeComment(comment); writer.writeComment(comment);
@ -240,7 +245,7 @@ int PDFToolXmlApplication::execute(const PDFToolOptions& options)
writer.writeEndElement(); writer.writeEndElement();
writer.writeEndDocument(); writer.writeEndDocument();
PDFConsole::writeText(xmlString); PDFConsole::writeText(xmlString, options.outputCodec);
return ExitSuccess; return ExitSuccess;
} }