File indexing completed on 2024-04-21 05:54:06

0001 /**
0002  * SPDX-FileCopyrightText: 2021 by Alexander Stippich <a.stippich@gmx.net>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "DocumentSaver.h"
0008 
0009 #include <QTransform>
0010 
0011 #include <KLocalizedString>
0012 
0013 #include "skanpage_debug.h"
0014 #include "OCREngine.h"
0015 
0016 DocumentSaver::DocumentSaver(QObject *parent)
0017     : QObject(parent)
0018     , m_OCREngine(nullptr)
0019 {
0020 }
0021 
0022 DocumentSaver::~DocumentSaver()
0023 {
0024 }
0025 
0026 void DocumentSaver::setOCREngine(OCREngine *engine)
0027 {
0028     m_OCREngine = engine;
0029 }
0030 
0031 void DocumentSaver::saveDocument(const QUrl &fileUrl, const SkanpageUtils::DocumentPages &document, const SkanpageUtils::FileType type, const QString &title)
0032 {
0033     if (fileUrl.isEmpty()) {
0034         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18n("No file path given."));
0035         return;
0036     }
0037     if (document.isEmpty()) {
0038         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18n("Nothing to save."));
0039         return;
0040     }
0041     if (!fileUrl.isLocalFile()) {
0042         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18n("Saving to non-local directories is currently unsupported."));
0043         return;
0044     }
0045     qCDebug(SKANPAGE_LOG) << QStringLiteral("Saving document to") << fileUrl;
0046 
0047     const QFileInfo &fileInfo = QFileInfo(fileUrl.toLocalFile());
0048     const QString &fileSuffix = fileInfo.suffix();
0049 
0050     qCDebug(SKANPAGE_LOG) << QStringLiteral("Selected file suffix is") << fileSuffix;
0051 
0052     if (type == SkanpageUtils::OCRDocument) {
0053         m_OCREngine->InitForOCR();
0054         saveSearchablePDF(fileUrl, document, title);
0055     } else if (fileSuffix == QLatin1String("pdf") || fileSuffix.isEmpty()) {
0056         savePDF(fileUrl, document, type);
0057     } else {
0058         saveImage(fileInfo, document, type);
0059     }
0060 }
0061 
0062 void DocumentSaver::savePDF(const QUrl &fileUrl, const SkanpageUtils::DocumentPages &document, const SkanpageUtils::FileType type)
0063 {
0064     QFile file(fileUrl.toLocalFile());
0065     bool ok = file.open(QIODevice::ReadWrite);
0066     if (!ok) {
0067         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18nc("%1 is the error message", "An error ocurred while saving: %1.", file.errorString()));
0068         return;
0069     }
0070     QPdfWriter writer(&file);
0071     writer.setCreator(QStringLiteral("org.kde.skanpage"));
0072     QPainter painter;
0073 
0074     for (int i = 0; i < document.count(); ++i) {
0075         printPage(writer, painter, document.at(i), i == 0);
0076     }
0077 
0078     if (type == SkanpageUtils::EntireDocument || type == SkanpageUtils::PageSelection) {
0079         Q_EMIT showUserMessage(SkanpageUtils::InformationMessage, i18n("Document saved as PDF."));
0080     }
0081 
0082     if (type == SkanpageUtils::EntireDocument) {
0083         Q_EMIT fileSaved({fileUrl}, document);
0084     } else if (type == SkanpageUtils::SharingDocument) {
0085         Q_EMIT sharingFileSaved({fileUrl});
0086     }
0087 }
0088 
0089 void DocumentSaver::saveSearchablePDF(const QUrl &fileUrl, const SkanpageUtils::DocumentPages &document, const QString &title)
0090 {
0091     if (m_OCREngine == nullptr) {
0092         return;
0093     }
0094 
0095     QFile file(fileUrl.toLocalFile());
0096     bool ok = file.open(QIODevice::ReadWrite);
0097     if (!ok) {
0098         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18nc("%1 is the error message", "An error ocurred while saving: %1.", file.errorString()));
0099         return;
0100     }
0101     QPdfWriter writer(&file);
0102     writer.setCreator(QStringLiteral("org.kde.skanpage"));
0103     writer.setTitle(title);
0104     QPainter painter;
0105     
0106     for (int i = 0; i < document.count(); ++i) {
0107         printPage(writer, painter, document.at(i), i == 0);
0108         m_OCREngine->OCRPage(writer, painter, document.at(i));
0109     }
0110     
0111     Q_EMIT showUserMessage(SkanpageUtils::InformationMessage, i18n("Document saved with OCR as PDF."));
0112 }
0113 
0114 void DocumentSaver::printPage(QPdfWriter &writer, QPainter &painter, const SkanpageUtils::PageProperties &page, bool firstPage)
0115 {
0116     writer.setResolution(page.dpi);
0117     writer.setPageSize(page.pageSize);
0118     writer.setPageMargins(QMarginsF(0, 0, 0, 0));
0119     int rotationAngle = page.rotationAngle;
0120     if (rotationAngle == 90 || rotationAngle == 270) {
0121         writer.setPageOrientation(QPageLayout::Landscape);
0122     } else {
0123         writer.setPageOrientation(QPageLayout::Portrait);
0124     }
0125     writer.newPage();
0126 
0127     QTransform transformation;
0128     if (rotationAngle != 0) {
0129         transformation.translate(writer.width()/2, writer.height()/2);
0130         transformation.rotate(rotationAngle);
0131         transformation.translate(-writer.width()/2, -writer.height()/2);
0132     }
0133     if (rotationAngle == 90 || rotationAngle == 270) {
0134         //strange that this is needed and Qt does not do this automatically
0135         transformation.translate((writer.width()-writer.height())/2, (writer.height()-writer.width())/2);
0136     }
0137 
0138     if (firstPage) {
0139         painter.begin(&writer);
0140     }
0141 
0142     painter.setTransform(transformation);
0143     QImage pageImage(page.temporaryFile->fileName());
0144     painter.drawImage(QPoint(0, 0), pageImage, pageImage.rect());
0145 }
0146 
0147 void DocumentSaver::saveImage(const QFileInfo &fileInfo, const SkanpageUtils::DocumentPages &document, const SkanpageUtils::FileType type)
0148 {
0149     const int count = document.count();
0150     QImage pageImage;
0151     QString fileName;
0152     QList<QUrl> fileUrls;
0153 
0154     bool success = true;
0155     if (count == 1) {
0156         pageImage.load(document.at(0).temporaryFile->fileName());
0157         fileName = fileInfo.absoluteFilePath();
0158         const int rotationAngle = document.at(0).rotationAngle;
0159         if (rotationAngle != 0) {
0160             pageImage = pageImage.transformed(QTransform().rotate(rotationAngle));
0161         }
0162         success = pageImage.save(fileName, fileInfo.suffix().toLocal8Bit().constData());
0163         fileUrls.append(QUrl::fromLocalFile(fileName));
0164     } else {
0165         fileUrls.reserve(count);
0166         for (int i = 0; i < count; ++i) {
0167             pageImage.load(document.at(i).temporaryFile->fileName());
0168             const int rotationAngle = document.at(i).rotationAngle;
0169             if (rotationAngle != 0) {
0170                 pageImage = pageImage.transformed(QTransform().rotate(rotationAngle));
0171             }
0172             fileName =
0173                 QStringLiteral("%1/%2%3.%4").arg(fileInfo.absolutePath(), fileInfo.baseName(), QLocale().toString(i).rightJustified(4, QLatin1Char('0')), fileInfo.suffix());
0174             if(!pageImage.save(fileName, fileInfo.suffix().toLocal8Bit().constData())) {
0175                 success = false;
0176             }
0177             fileUrls.append(QUrl::fromLocalFile(fileName));
0178         }
0179     }
0180 
0181     if (success) {
0182         if (type == SkanpageUtils::EntireDocument || type == SkanpageUtils::PageSelection) {
0183             Q_EMIT showUserMessage(SkanpageUtils::InformationMessage, i18n("Document saved as image."));
0184         }
0185         if (type == SkanpageUtils::EntireDocument) {
0186             Q_EMIT fileSaved(fileUrls, document);
0187         } else if (type == SkanpageUtils::SharingDocument) {
0188             Q_EMIT sharingFileSaved(fileUrls);
0189         }
0190     } else {
0191         if (type == SkanpageUtils::EntireDocument || type == SkanpageUtils::PageSelection) {
0192             Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18n("Failed to save document as image."));
0193         }
0194     }
0195 }
0196 
0197 void DocumentSaver::saveNewPageTemporary(const int pageID, const QImage &image)
0198 {
0199     const QPageSize pageSize = QPageSize(QSizeF(image.width() * 1000.0 / image.dotsPerMeterX() , image.height() * 1000.0 / image.dotsPerMeterY()), QPageSize::Millimeter);
0200     const int dpi = qRound(image.dotsPerMeterX() / 1000.0 * 25.4);
0201     QTemporaryFile *tempImageFile = new QTemporaryFile();
0202     tempImageFile->open();
0203     if (image.save(tempImageFile, "PNG")) {
0204         qCDebug(SKANPAGE_LOG) << "Saved new image to temporary file.";
0205     } else {
0206         qCDebug(SKANPAGE_LOG) << "Saving new image to temporary file failed!";
0207         Q_EMIT showUserMessage(SkanpageUtils::ErrorMessage, i18n("Failed to save image"));
0208     }
0209     qCDebug(SKANPAGE_LOG) << image << tempImageFile << "with page size" << pageSize << "and resolution of" << dpi << "dpi";
0210     tempImageFile->close();
0211     Q_EMIT pageTemporarilySaved(pageID, {std::shared_ptr<QTemporaryFile>(tempImageFile), pageSize, dpi});
0212 }
0213 
0214 #include "moc_DocumentSaver.cpp"