diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-06-19 00:46:36 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-06-19 00:46:36 +0200 |
| commit | dd7090dda0370f35e0e0e2e17f3120d906aa2080 (patch) | |
| tree | 557d9fb68beab0133785307f311b4504ddf4c895 /src/browsertab.cpp | |
| parent | a716f0804fd10a37ccc7d87c58ffebeb4f261d10 (diff) | |
| download | kristall-dd7090dda0370f35e0e0e2e17f3120d906aa2080.tar.gz | |
Adds iconv implementation to convert between input charset and UTF-8 for display.
Diffstat (limited to 'src/browsertab.cpp')
| -rw-r--r-- | src/browsertab.cpp | 125 |
1 files changed, 111 insertions, 14 deletions
diff --git a/src/browsertab.cpp b/src/browsertab.cpp index 0733f53..1738483 100644 --- a/src/browsertab.cpp +++ b/src/browsertab.cpp @@ -7,6 +7,8 @@ #include "geminirenderer.hpp" #include "plaintextrenderer.hpp" +#include "mimeparser.hpp" + #include "certificateselectiondialog.hpp" #include "geminiclient.hpp" @@ -36,6 +38,8 @@ #include <QGraphicsPixmapItem> #include <QGraphicsTextItem> +#include <iconv.h> + BrowserTab::BrowserTab(MainWindow *mainWindow) : QWidget(nullptr), ui(new Ui::BrowserTab), mainWindow(mainWindow), @@ -228,11 +232,104 @@ void BrowserTab::on_certificateRequired(const QString &reason) this->updateUI(); } -void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) +static QByteArray convertToUtf8(QByteArray const & input, QString const & charSet) +{ + QFile temp { "/tmp/raw.dat" }; + temp.open(QFile::WriteOnly); + IoUtil::writeAll(temp, input); + + auto charset_u8 = charSet.toUpper().toUtf8(); + + // TRANSLIT will try to mix-match other code points to reflect to correct encoding + iconv_t cd = iconv_open("UTF-8", charset_u8.data()); + if(cd == (iconv_t)-1) { + return QByteArray { }; + } + + QByteArray result; + + char temp_buffer[4096]; + + char const * input_ptr = reinterpret_cast<char const *>(input.data()); + size_t input_size = input.size(); + + while(input_size > 0) + { + char * out_ptr = temp_buffer; + size_t out_size = sizeof(temp_buffer); + + size_t n = iconv(cd, const_cast<char **>(&input_ptr), &input_size, &out_ptr, &out_size); + if (n == size_t(-1)) + { + if(errno == E2BIG) { + // silently ignore E2BIG, as we will continue conversion in the next loop + } + else if(errno == EILSEQ) { + // this is an invalid multibyte sequence. + // append an "replacement character" and skip a byte + if(input_size > 0) { + input_size --; + input_ptr++; + result.append(u8"�"); + } + } + else if(errno == EINVAL) { + // the file ends with an invalid multibyte sequence. + // just drop it and display the replacement-character + if(input_size > 0) { + input_size --; + input_ptr++; + result.append(u8"�"); + } + } + else { + perror("iconv conversion error"); + break; + } + } + + size_t len = out_ptr - temp_buffer; + result.append(temp_buffer, len); + } + + iconv_close(cd); + + return result; +} + +void BrowserTab::on_requestComplete(const QByteArray &ref_data, const QString &mime_text) { - qDebug() << "Loaded" << data.length() << "bytes of type" << mime; + QByteArray data = ref_data; + MimeType mime = MimeParser::parse(mime_text); + + qDebug() << "Loaded" << ref_data.length() << "bytes of type" << mime.type << "/" << mime.subtype; +// for(auto & key : mime.parameters.keys()) { +// qDebug() << key << mime.parameters[key]; +// } + + auto charset = mime.parameter("charset", "utf-8").toUpper(); + if(not ref_data.isEmpty() and (mime.type == "text") and (charset != "UTF-8")) + { + auto temp = convertToUtf8(ref_data, charset); + bool ok = (temp.size() > 0); + if(ok) { + data = std::move(temp); + } else { + auto response = QMessageBox::question( + this, + "Kristall", + QString("Failed to convert input charset %1 to UTF-8. Cannot display the file.\r\nDo you want to display unconverted data anyways?").arg(charset) + ); + + if(response != QMessageBox::Yes) { + setErrorMessage(QString("Failed to convert input charset %1 to UTF-8.").arg(charset)); + return; + } + } + } + - this->current_mime = mime; + this->current_mime = mime_text; this->current_buffer = data; this->graphics_scene.clear(); @@ -258,7 +355,7 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) bool plaintext_only = (global_options.text_display == GenericSettings::PlainText); - if (not plaintext_only and mime.startsWith("text/gemini")) + if (not plaintext_only and mime_text.startsWith("text/gemini")) { document = GeminiRenderer::render( data, @@ -266,18 +363,18 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) doc_style, this->outline); } - else if (not plaintext_only and mime.startsWith("text/gophermap")) + else if (not plaintext_only and mime_text.startsWith("text/gophermap")) { document = GophermapRenderer::render( data, this->current_location, doc_style); } - else if (not plaintext_only and mime.startsWith("text/finger")) + else if (not plaintext_only and mime_text.startsWith("text/finger")) { document = PlainTextRenderer::render(data, doc_style); } - else if (not plaintext_only and mime.startsWith("text/html")) + else if (not plaintext_only and mime_text.startsWith("text/html")) { document = std::make_unique<QTextDocument>(); @@ -287,7 +384,7 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) document->setHtml(QString::fromUtf8(data)); } #if defined(QT_FEATURE_textmarkdownreader) - else if (not plaintext_only and mime.startsWith("text/markdown")) + else if (not plaintext_only and mime_text.startsWith("text/markdown")) { document = std::make_unique<QTextDocument>(); document->setDefaultFont(doc_style.standard_font); @@ -296,11 +393,11 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) document->setMarkdown(QString::fromUtf8(data)); } #endif - else if (mime.startsWith("text/")) + else if (mime_text.startsWith("text/")) { document = PlainTextRenderer::render(data, doc_style); } - else if (mime.startsWith("image/")) + else if (mime_text.startsWith("image/")) { doc_type = Image; @@ -333,10 +430,10 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) this->ui->graphics_browser->fitInView(graphics_scene.sceneRect(), Qt::KeepAspectRatio); } - else if (mime.startsWith("video/") or mime.startsWith("audio/")) + else if (mime_text.startsWith("video/") or mime_text.startsWith("audio/")) { doc_type = Media; - this->ui->media_browser->setMedia(data, this->current_location, mime); + this->ui->media_browser->setMedia(data, this->current_location, mime_text); } else { @@ -352,7 +449,7 @@ Info: MIME Type: %1 File Size: %2 )md") - .arg(mime) + .arg(mime_text) .arg(IoUtil::size_human(data.size()))); } @@ -370,7 +467,7 @@ File Size: %2 QString title = this->current_location.toString(); emit this->titleChanged(title); - emit this->fileLoaded(data.size(), mime, this->timer.elapsed()); + emit this->fileLoaded(data.size(), mime_text, this->timer.elapsed()); this->successfully_loaded = true; |
