diff --git a/PdfTool/pdfoutputformatter.cpp b/PdfTool/pdfoutputformatter.cpp index b9793e5..862a44d 100644 --- a/PdfTool/pdfoutputformatter.cpp +++ b/PdfTool/pdfoutputformatter.cpp @@ -17,6 +17,7 @@ #include "pdfoutputformatter.h" +#include #include #include #include @@ -91,7 +92,7 @@ private: class PDFXmlOutputFormatterImpl : public PDFOutputFormatterImpl { public: - PDFXmlOutputFormatterImpl(); + PDFXmlOutputFormatterImpl(QString codec); virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override; virtual void endElement() override; @@ -109,7 +110,7 @@ private: class PDFHtmlOutputFormatterImpl : public PDFOutputFormatterImpl { public: - PDFHtmlOutputFormatterImpl(); + PDFHtmlOutputFormatterImpl(QString codecName); virtual void beginElement(PDFOutputFormatter::Element type, QString name, QString description, Qt::Alignment alignment, int reference) override; virtual void endElement() override; @@ -308,14 +309,17 @@ const PDFTextOutputFormatterImpl::TableCell& PDFTextOutputFormatterImpl::getTabl return dummy; } -PDFHtmlOutputFormatterImpl::PDFHtmlOutputFormatterImpl() : +PDFHtmlOutputFormatterImpl::PDFHtmlOutputFormatterImpl(QString codecName) : m_string(), m_streamWriter(&m_string), m_depth(0), m_headerDepth(1), 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) @@ -483,7 +487,7 @@ void PDFHtmlOutputFormatterImpl::endl() m_streamWriter.writeEndElement(); } -PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl() : +PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl(QString codecName) : m_string(), m_streamWriter(&m_string), m_depth(0) @@ -491,6 +495,11 @@ PDFXmlOutputFormatterImpl::PDFXmlOutputFormatterImpl() : m_streamWriter.setAutoFormatting(true); m_streamWriter.setAutoFormattingIndent(2); + if (QTextCodec* codec = QTextCodec::codecForName(codecName.toLatin1())) + { + m_streamWriter.setCodec(codec); + } + m_namespace = "https://github.com/JakubMelka/PdfForQt"; m_prefix = "pdftool"; } @@ -575,7 +584,7 @@ QString PDFXmlOutputFormatterImpl::getString() const return m_string; } -PDFOutputFormatter::PDFOutputFormatter(Style style) : +PDFOutputFormatter::PDFOutputFormatter(Style style, QString codecName) : m_impl(nullptr) { switch (style) @@ -585,11 +594,11 @@ PDFOutputFormatter::PDFOutputFormatter(Style style) : break; case Style::Xml: - m_impl = new PDFXmlOutputFormatterImpl(); + m_impl = new PDFXmlOutputFormatterImpl(codecName); break; case Style::Html: - m_impl = new PDFHtmlOutputFormatterImpl(); + m_impl = new PDFHtmlOutputFormatterImpl(codecName); break; } @@ -621,19 +630,39 @@ QString PDFOutputFormatter::getString() const return m_impl->getString(); } -void PDFConsole::writeText(QString text) +void PDFConsole::writeText(QString text, QString codecName) { #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 QTextStream(stdout) << text; #endif } -void PDFConsole::writeError(QString text) +void PDFConsole::writeError(QString text, QString codecName) { #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 QTextStream(stdout) << text; #endif diff --git a/PdfTool/pdfoutputformatter.h b/PdfTool/pdfoutputformatter.h index 9e05649..31ea106 100644 --- a/PdfTool/pdfoutputformatter.h +++ b/PdfTool/pdfoutputformatter.h @@ -37,7 +37,7 @@ public: Html }; - explicit PDFOutputFormatter(Style style); + explicit PDFOutputFormatter(Style style, QString codecName); ~PDFOutputFormatter(); enum class Element @@ -97,10 +97,10 @@ class PDFConsole public: /// Writes text to the console - static void writeText(QString text); + static void writeText(QString text, QString codecName); /// Writes error to the console - static void writeError(QString text); + static void writeError(QString text, QString codecName); private: explicit PDFConsole() = delete; diff --git a/PdfTool/pdftoolabstractapplication.cpp b/PdfTool/pdftoolabstractapplication.cpp index 86cf9e8..559119a 100644 --- a/PdfTool/pdftoolabstractapplication.cpp +++ b/PdfTool/pdftoolabstractapplication.cpp @@ -58,7 +58,7 @@ QString PDFToolHelpApplication::getStandardString(StandardString standardString) int PDFToolHelpApplication::execute(const PDFToolOptions& options) { - PDFOutputFormatter formatter(options.outputStyle); + PDFOutputFormatter formatter(options.outputStyle, options.outputCodec); formatter.beginDocument("help", PDFToolTranslationContext::tr("PDFTool help")); formatter.endl(); @@ -107,9 +107,27 @@ int PDFToolHelpApplication::execute(const PDFToolOptions& options) 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 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(); - PDFConsole::writeText(formatter.getString()); + PDFConsole::writeText(formatter.getString(), options.outputCodec); return ExitSuccess; } @@ -130,6 +148,7 @@ void PDFToolAbstractApplication::initializeCommandLineParser(QCommandLineParser* if (optionFlags.testFlag(ConsoleFormat)) { 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)) @@ -184,11 +203,13 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser { 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.outputCodec = parser->value("text-codec"); } if (optionFlags.testFlag(OpenDocument)) @@ -225,7 +246,7 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser } 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: { - PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid password provided.")); + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid password provided."), options.outputCodec); return false; } 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; } @@ -278,7 +299,7 @@ bool PDFToolAbstractApplication::readDocument(const PDFToolOptions& options, pdf 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; diff --git a/PdfTool/pdftoolabstractapplication.h b/PdfTool/pdftoolabstractapplication.h index 5a942f0..bdf1e66 100644 --- a/PdfTool/pdftoolabstractapplication.h +++ b/PdfTool/pdftoolabstractapplication.h @@ -42,6 +42,7 @@ struct PDFToolOptions { // For option 'ConsoleFormat' PDFOutputFormatter::Style outputStyle = PDFOutputFormatter::Style::Text; + QString outputCodec; // For option 'OpenDocument' QString document; diff --git a/PdfTool/pdftoolverifysignatures.cpp b/PdfTool/pdftoolverifysignatures.cpp index 8b563ac..4348d87 100644 --- a/PdfTool/pdftoolverifysignatures.cpp +++ b/PdfTool/pdftoolverifysignatures.cpp @@ -52,7 +52,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options) // No document specified? if (options.document.isEmpty()) { - PDFConsole::writeError(PDFToolTranslationContext::tr("No document specified.")); + PDFConsole::writeError(PDFToolTranslationContext::tr("No document specified."), options.outputCodec); return ErrorNoDocumentSpecified; } @@ -76,7 +76,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options) 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; } @@ -87,7 +87,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options) 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 @@ -107,7 +107,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options) pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject()); std::vector 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.endl(); @@ -386,7 +386,7 @@ int PDFToolVerifySignaturesApplication::execute(const PDFToolOptions& options) formatter.endDocument(); - PDFConsole::writeText(formatter.getString()); + PDFConsole::writeText(formatter.getString(), options.outputCodec); return ExitSuccess; } diff --git a/PdfTool/pdftoolxml.cpp b/PdfTool/pdftoolxml.cpp index 52f6c5a..f1c54f6 100644 --- a/PdfTool/pdftoolxml.cpp +++ b/PdfTool/pdftoolxml.cpp @@ -210,6 +210,11 @@ int PDFToolXmlApplication::execute(const PDFToolOptions& options) 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()); writer.writeStartDocument(); writer.writeComment(comment); @@ -240,7 +245,7 @@ int PDFToolXmlApplication::execute(const PDFToolOptions& options) writer.writeEndElement(); writer.writeEndDocument(); - PDFConsole::writeText(xmlString); + PDFConsole::writeText(xmlString, options.outputCodec); return ExitSuccess; }