File indexing completed on 2024-04-21 04:33:48

0001 /*******************************************************************************
0002  * Copyright (C) 2020 by Steve Allewell                                        *
0003  * steve.allewell@gmail.com                                                    *
0004  *                                                                             *
0005  * This program is free software; you can redistribute it and/or modify        *
0006  * it under the terms of the GNU General Public License as published by        *
0007  * the Free Software Foundation; either version 2 of the License, or           *
0008  * (at your option) any later version.                                         *
0009  ******************************************************************************/
0010 
0011 
0012 #include "ViewerTab.h"
0013 
0014 #include <QImage>
0015 #include <QListWidget>
0016 #include <QPainter>
0017 #include <QPointer>
0018 #include <QPrinter>
0019 #include <QPrintDialog>
0020 #include <QProgressDialog>
0021 #include <QResizeEvent>
0022 #include <QScrollBar>
0023 #include <QShowEvent>
0024 #include <QUrl>
0025 
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 
0029 #include "Thumbnail.h"
0030 
0031 
0032 QByteArray readData(QDataStream &stream, int size)
0033 {
0034     std::unique_ptr<char[]> buffer(new char[size]);
0035     stream.readRawData(buffer.get(), size);
0036     return QByteArray(buffer.get(), size);
0037 }
0038 
0039 
0040 QByteArray readData(QDataStream &stream)
0041 {
0042     int size;
0043     stream >> size;
0044     return readData(stream, size);
0045 }
0046 
0047 
0048 QString readString(QDataStream &stream)
0049 {
0050     quint16 size;
0051     stream >> size;
0052     return QString(readData(stream, size));
0053 }
0054 
0055 
0056 ViewerTab::ViewerTab(QWidget *parent)
0057     :   QWidget(parent),
0058         m_document(nullptr)
0059 {
0060     ui.setupUi(this);
0061 
0062     QScrollBar *scrollbar = ui.PagesList->verticalScrollBar();
0063 
0064     connect(ui.splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int,int)));
0065     connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
0066     connect(&m_timer, SIGNAL(timeout()), this, SLOT(renderPages()));
0067 }
0068 
0069 
0070 void ViewerTab::valueChanged(int value)
0071 {
0072     Q_UNUSED(value);
0073 
0074     if (isVisible()) m_timer.start(100);
0075 }
0076 
0077 
0078 ViewerTab::~ViewerTab()
0079 {
0080     delete m_document;
0081 }
0082 
0083 
0084 int ViewerTab::load(const QUrl &url)
0085 {
0086     QFile src(url.toLocalFile());
0087 
0088     if (!src.open(QIODevice::ReadOnly))
0089     {
0090         KMessageBox::error(nullptr, i18n("Failed to open source file."));
0091         return -1;
0092     }
0093 
0094     QDataStream stream(&src);
0095     stream.setByteOrder(QDataStream::LittleEndian);
0096 
0097     if (!readData(stream, 24).contains("Pattern Viewer Data File")) {
0098         KMessageBox::error(nullptr, i18n("Not a PC Stitch Pattern Viewer file."));
0099         return -1;
0100     }
0101 
0102     stream.skipRawData(12);     // some random data here, TODO determine contents
0103                                 // possibly a file format version
0104 
0105     ui.LicenceLabel->setText(QString(readData(stream, 30)));
0106 
0107     stream.skipRawData(23);     // some random data here, TODO determine contents
0108 
0109     ui.DesignerLabel->setText(readString(stream));
0110 
0111     QString copyright(readString(stream));
0112     if (copyright.at(0).unicode() == 65533)
0113         copyright.replace(0, 1, QChar(0x00A9));
0114     ui.CopyrightLabel->setText(copyright);
0115 
0116 
0117     m_tabTitle = readString(stream);
0118 
0119     readString(stream);         // there is another length encoded string here
0120                                 // but no content in any of the files checked
0121 
0122     m_website = readString(stream);
0123 
0124     m_pixmap.loadFromData(readData(stream));
0125 
0126     m_pdfData = readData(stream);
0127 
0128     m_document = Poppler::Document::loadFromData(m_pdfData);
0129 
0130     if (!m_document || m_document->isLocked()) {
0131         KMessageBox::error(nullptr, i18n("Failed to create PDF from data."));
0132         delete m_document;
0133         m_document = nullptr;
0134         return -1;
0135     }
0136 
0137     m_document->setRenderHint(Poppler::Document::RenderHint::Antialiasing, true);
0138     m_document->setRenderHint(Poppler::Document::RenderHint::TextAntialiasing, true);
0139     m_document->setRenderHint(Poppler::Document::RenderHint::TextHinting, true);
0140     m_document->setRenderHint(Poppler::Document::RenderHint::TextSlightHinting, true);
0141     m_document->setRenderHint(Poppler::Document::RenderHint::ThinLineSolid, true);
0142 
0143     int pages = m_document->numPages();
0144 
0145     for (int page = 0 ; page < pages ; ++page) {
0146         new Thumbnail(m_document->page(page), ui.PagesList);
0147     }
0148 
0149     return 0;
0150 }
0151 
0152 
0153 void ViewerTab::showEvent(QShowEvent *event)
0154 {
0155     QWidget::showEvent(event);
0156 
0157     quickScale();
0158 }
0159 
0160 
0161 QString ViewerTab::title() const
0162 {
0163     return m_tabTitle;
0164 }
0165 
0166 
0167 QByteArray ViewerTab::pdfData()
0168 {
0169     return m_pdfData;
0170 }
0171 
0172 
0173 void ViewerTab::resizeEvent(QResizeEvent *event)
0174 {
0175     QWidget::resizeEvent(event);
0176 
0177     if (isVisible()) quickScale();
0178 }
0179 
0180 
0181 void ViewerTab::splitterMoved(int pos, int index)
0182 {
0183     Q_UNUSED(pos);
0184     Q_UNUSED(index);
0185 
0186     quickScale();
0187 }
0188 
0189 
0190 void ViewerTab::quickScale()
0191 {
0192     int newIconWidth = ui.PagesList->viewport()->width() - (2 * ui.PagesList->spacing()) - ui.PagesList->contentsMargins().left() - ui.PagesList->contentsMargins().right();
0193 
0194     ui.PatternPreview->setPixmap(m_pixmap.scaled(ui.PatternPreview->width()-20, ui.PatternPreview->height()-20, Qt::KeepAspectRatio));
0195 
0196     ui.PagesList->setIconSize(QSize(newIconWidth, static_cast<Thumbnail *>(ui.PagesList->item(0))->heightForWidth(newIconWidth)));
0197 
0198     for (int page = 0, pages = m_document->numPages() ; page < pages ; ++page) {
0199         Thumbnail *thumbnail = static_cast<Thumbnail *>(ui.PagesList->item(page));
0200         QImage image = thumbnail->image();
0201 
0202         if (image.isNull()) {
0203             image = QImage(ui.PagesList->iconSize(), QImage::Format_ARGB32_Premultiplied);
0204             image.fill(Qt::white);
0205             thumbnail->setImage(image);
0206         } else {
0207             thumbnail->setImage(image.scaledToWidth(newIconWidth, Qt::FastTransformation));
0208         }
0209     }
0210 
0211     m_timer.start(100);
0212 }
0213 
0214 
0215 void ViewerTab::renderPages()
0216 {
0217     m_timer.stop();
0218 
0219     QScrollBar *scrollBar = ui.PagesList->verticalScrollBar();
0220 
0221     int numPages = m_document->numPages();
0222     int pageStep = scrollBar->pageStep();
0223     int pageLength = (scrollBar->maximum() + pageStep) / numPages;
0224     int value = scrollBar->value();
0225     int firstPage = value / pageLength;
0226     int lastPage = std::min(((value + pageStep) / pageLength), numPages - 1);
0227 
0228     for (int page = firstPage ; page <= lastPage ; ++page) {
0229         Thumbnail *thumbnail = static_cast<Thumbnail *>(ui.PagesList->item(page));
0230         thumbnail->renderPage();
0231     }
0232 }
0233 
0234 
0235 void ViewerTab::print()
0236 {
0237     QPrinter printer;
0238 
0239     QList<Poppler::Page *> pages;
0240 
0241     int pageCount = m_document->numPages();
0242 
0243     for (int page = 0 ; page < pageCount ; ++page) {
0244         pages.append(m_document->page(page));
0245     }
0246 
0247     printer.setFullPage(true);
0248     printer.setPrintRange(QPrinter::AllPages);
0249     printer.setFromTo(1, pageCount);
0250 
0251     QPointer<QPrintDialog> printDialog = new QPrintDialog(&printer, this);
0252 
0253     if (printDialog->exec() == QDialog::Accepted) {
0254         int fromPage = 1;
0255         int toPage   = pageCount;
0256 
0257         if (printer.printRange() == QPrinter::PageRange) {
0258             fromPage = printer.fromPage();
0259             toPage   = printer.toPage();
0260         }
0261 
0262         while (toPage < pages.count()) pages.removeLast();
0263         while (--fromPage) pages.removeFirst();
0264 
0265         int pageCount = pages.count();
0266 
0267         QProgressDialog progress(this);
0268         progress.setWindowModality(Qt::WindowModal);
0269         progress.setRange(0, pageCount);
0270 
0271         QPainter painter;
0272         painter.begin(&printer);
0273 
0274         for (int page = 0 ; page < pageCount ; ++page) {
0275             if (progress.wasCanceled()) {
0276                 printer.abort();
0277                 return;
0278             }
0279 
0280             if (page > 0) {
0281                 printer.newPage();
0282             }
0283 
0284             progress.setValue(page);
0285             progress.setLabelText(i18n("Printing page %1", page+1));
0286 
0287             Poppler::Page *pdfPage = (printer.pageOrder() == QPrinter::FirstPageFirst)?pages.takeFirst():pages.takeLast();
0288 
0289             if (pdfPage) {
0290                 QSizeF pageSize  = pdfPage->pageSizeF(); // size in points 1/72 inch
0291                 QRect  paintRect = painter.window();
0292 
0293                 double scale = printer.paperRect(QPrinter::DevicePixel).width() / printer.paperRect(QPrinter::Point).width();
0294 
0295                 double hScale = paintRect.width()  / pageSize.width();
0296                 double vScale = paintRect.height() / pageSize.height();
0297 
0298                 scale = std::min(hScale, vScale);
0299 
0300                 QImage image = pdfPage->renderToImage(300, 300);
0301 
0302                 painter.drawImage(QRectF(QPointF(0, 0), scale * pdfPage->pageSizeF()), image);
0303             }
0304         }
0305 
0306         painter.end();
0307 
0308         progress.setValue(pageCount);
0309     }
0310 
0311     delete printDialog;
0312 }
0313 
0314 #include "moc_ViewerTab.cpp"