From c5fedfb1ec79b97edec4a82b70f082fba93a5b5d Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Mon, 31 Dec 2018 12:56:32 +0100 Subject: image export in progress --- src/data/pdfdocument.cpp | 132 +++++++++++++++++++++++++++++++++++++ src/data/pdfdocument.h | 28 ++++++++ src/gui/exportdialog.cpp | 80 +++++++++++++++++++++++ src/gui/exportdialog.h | 34 ++++++++++ src/gui/exportdialog.ui | 162 ++++++++++++++++++++++++++++++++++++++++++++++ src/gui/latexprocess.cpp | 2 +- src/gui/previewwindow.cpp | 100 ++++++++++++++++------------ src/gui/previewwindow.h | 19 ++++-- src/tikzit.cpp | 5 ++ src/tikzit.h | 2 + tikzit.pro | 13 ++-- 11 files changed, 522 insertions(+), 55 deletions(-) create mode 100644 src/data/pdfdocument.cpp create mode 100644 src/data/pdfdocument.h create mode 100644 src/gui/exportdialog.cpp create mode 100644 src/gui/exportdialog.h create mode 100644 src/gui/exportdialog.ui diff --git a/src/data/pdfdocument.cpp b/src/data/pdfdocument.cpp new file mode 100644 index 0000000..bfedeca --- /dev/null +++ b/src/data/pdfdocument.cpp @@ -0,0 +1,132 @@ +#include "pdfdocument.h" + +#include +#include +#include +#include +#include +#include +#include + +PdfDocument::PdfDocument(QString file, QObject *parent) : QObject(parent) +{ + // use loadFromData to avoid holding a lock on the PDF file in windows + QFile f(file); + if (f.open(QFile::ReadOnly)) { + QByteArray data = f.readAll(); + f.close(); + _doc = Poppler::Document::loadFromData(data); + } else { + _doc = nullptr; + } + + if (!_doc) { + _doc = nullptr; + _page = nullptr; + } else { + _doc->setRenderHint(Poppler::Document::Antialiasing); + _doc->setRenderHint(Poppler::Document::TextAntialiasing); + _doc->setRenderHint(Poppler::Document::TextHinting); + _page = _doc->page(0); + } +} + +void PdfDocument::renderTo(QLabel *label, QRect rect) +{ + if (!isValid()) return; + + QSizeF pageSize = _page->pageSizeF(); + + qreal ratio = label->devicePixelRatioF(); + //QRect rect = ui->scrollArea->visibleRegion().boundingRect(); + int w = static_cast(ratio * (rect.width() - 20)); + int h = static_cast(ratio * (rect.height() - 20)); + qreal scale = fmin(static_cast(w) / pageSize.width(), + static_cast(h) / pageSize.height()); + + + int dpi = static_cast(scale * 72.0); + int w1 = static_cast(scale * pageSize.width()); + int h1 = static_cast(scale * pageSize.height()); + + //qDebug() << "hidpi ratio:" << ratio; + //qDebug() << "visible width:" << w; + //qDebug() << "visible height:" << h; + //qDebug() << "doc width:" << pageSize.width(); + //qDebug() << "doc height:" << pageSize.height(); + //qDebug() << "scale:" << scale; + //qDebug() << "dpi:" << dpi; + + QPixmap pm = QPixmap::fromImage(_page->renderToImage(dpi, dpi, (w1 - w)/2, (h1 - h)/2, w, h)); + pm.setDevicePixelRatio(ratio); + label->setPixmap(pm); +} + +bool PdfDocument::isValid() +{ + return (_page != nullptr); +} + +bool PdfDocument::exportImage(QString file, const char *format, QSize outputSize) +{ + QImage img = asImage(outputSize); + if (!img.isNull()) return img.save(file, format); + else return false; +} + +bool PdfDocument::exportPdf(QString file) +{ + if (!isValid()) return false; + Poppler::PDFConverter *conv = _doc->pdfConverter(); + conv->setOutputFileName(file); + bool success = conv->convert(); + delete conv; + return success; +} + +void PdfDocument::copyImageToClipboard(QSize outputSize) +{ + QImage img = asImage(outputSize); + if (!img.isNull()) { + QApplication::clipboard()->setImage(img, QClipboard::Clipboard); + } +} + +QImage PdfDocument::asImage(QSize outputSize) +{ + if (!isValid()) return QImage(); + if (outputSize.isNull()) outputSize = size(); + QSize pageSize = _page->pageSize(); + int dpix = (72 * outputSize.width()) / pageSize.width(); + int dpiy = (72 * outputSize.width()) / pageSize.width(); + QImage img = _page->renderToImage(dpix, dpiy, 0, 0, + outputSize.width(), outputSize.height()); + return img; +} + +// CRASHES TikZiT when figures contain text, due to limitations of Arthur backend +//void PdfDocument::exportToSvg(QString file, QSize size) { +// QSvgGenerator gen; +// gen.setFileName(file); +// gen.setSize(size); +// gen.setViewBox(QRect(0,0,size.width(),size.height())); +// gen.setDescription("SVG generated from PDF by TikZiT"); +// QPainter painter; + +// // set the backend to Qt for renderToPainter() support +// Poppler::Document::RenderBackend backend = _doc->renderBackend(); +// _doc->setRenderBackend(Poppler::Document::ArthurBackend); +// painter.begin(&gen); +// _page->renderToPainter(&painter); +// painter.end(); +// _doc->setRenderBackend(backend); +//} + +QSize PdfDocument::size() +{ + if (isValid()) { + return _page->pageSize(); + } +} + + diff --git a/src/data/pdfdocument.h b/src/data/pdfdocument.h new file mode 100644 index 0000000..ebd33e9 --- /dev/null +++ b/src/data/pdfdocument.h @@ -0,0 +1,28 @@ +#ifndef PDFDOCUMENT_H +#define PDFDOCUMENT_H + +#include +#include +#include + +#include + +class PdfDocument : public QObject +{ + Q_OBJECT +public: + explicit PdfDocument(QString file, QObject *parent = nullptr); + void renderTo(QLabel *label, QRect rect); + bool isValid(); +// void exportToSvg(QString file, QSize size); + bool exportImage(QString file, const char *format, QSize outputSize=QSize()); + bool exportPdf(QString file); + void copyImageToClipboard(QSize outputSize=QSize()); + QImage asImage(QSize outputSize=QSize()); + QSize size(); +private: + Poppler::Document *_doc; + Poppler::Page *_page; +}; + +#endif // PDFDOCUMENT_H diff --git a/src/gui/exportdialog.cpp b/src/gui/exportdialog.cpp new file mode 100644 index 0000000..2d0d8a0 --- /dev/null +++ b/src/gui/exportdialog.cpp @@ -0,0 +1,80 @@ +#include "exportdialog.h" +#include "ui_exportdialog.h" + +#include "tikzit.h" + +ExportDialog::ExportDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ExportDialog) +{ + ui->setupUi(this); + + QIntValidator *v = new QIntValidator(this); + v->setBottom(1); + ui->width->setValidator(v); + ui->height->setValidator(v); + connect(ui->width, SIGNAL(editingFinished()), + this, SLOT(setHeightFromWidth())); + connect(ui->height, SIGNAL(editingFinished()), + this, SLOT(setWidthFromHeight())); + + PdfDocument *doc = tikzit->previewWindow()->doc(); + if (doc) { + QSize size = doc->size() * 4; + ui->width->blockSignals(true); + ui->height->blockSignals(true); + ui->width->setText(QString::number(size.width())); + ui->height->setText(QString::number(size.height())); + ui->width->blockSignals(false); + ui->height->blockSignals(false); + } +} + +ExportDialog::~ExportDialog() +{ + delete ui; +} + +void ExportDialog::setHeightFromWidth() +{ + if (ui->keepAspect->isChecked()) { + PdfDocument *doc = tikzit->previewWindow()->doc(); + if (doc == nullptr || doc->size().width() == 0 || doc->size().height() == 0) return; + int w = ui->width->text().toInt(); + int h = (w * doc->size().height()) / doc->size().width(); + + ui->height->blockSignals(true); + ui->height->setText(QString::number(h)); + ui->height->blockSignals(false); + } +} + +void ExportDialog::setWidthFromHeight() +{ + if (ui->keepAspect->isChecked()) { + PdfDocument *doc = tikzit->previewWindow()->doc(); + if (doc == nullptr || doc->size().width() == 0 || doc->size().height() == 0) return; + int h = ui->height->text().toInt(); + int w = (h * doc->size().width()) / doc->size().height(); + + ui->width->blockSignals(true); + ui->width->setText(QString::number(w)); + ui->width->blockSignals(false); + } +} + +void ExportDialog::on_keepAspect_stateChanged(int state) +{ + if (state == Qt::Checked) setHeightFromWidth(); +} + +void ExportDialog::on_browseButton_clicked() +{ + +} + +void ExportDialog::on_fileFormat_currentIndexChanged(int f) +{ + ui->width->setEnabled(f != PDF); + ui->height->setEnabled(f != PDF); +} diff --git a/src/gui/exportdialog.h b/src/gui/exportdialog.h new file mode 100644 index 0000000..064c968 --- /dev/null +++ b/src/gui/exportdialog.h @@ -0,0 +1,34 @@ +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include + +namespace Ui { +class ExportDialog; +} + +class ExportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportDialog(QWidget *parent = nullptr); + ~ExportDialog(); + enum Format { + PNG = 0, + JPG = 1, + PDF = 2 + }; + +protected slots: + void setHeightFromWidth(); + void setWidthFromHeight(); + void on_keepAspect_stateChanged(int state); + void on_browseButton_clicked(); + void on_fileFormat_currentIndexChanged(int f); + +private: + Ui::ExportDialog *ui; +}; + +#endif // EXPORTDIALOG_H diff --git a/src/gui/exportdialog.ui b/src/gui/exportdialog.ui new file mode 100644 index 0000000..69e2a22 --- /dev/null +++ b/src/gui/exportdialog.ui @@ -0,0 +1,162 @@ + + + ExportDialog + + + + 0 + 0 + 394 + 119 + + + + + 0 + 0 + + + + Dialog + + + + + + + + File + + + + + + + Format + + + + + + + + + + + + ... + + + + + + + + + + Portable Network Graphics (PNG) + + + + + Jpeg Image (JPG) + + + + + Original (PDF) + + + + + + + + Dimensions + + + + + + + + + + + + X + + + + + + + + + + + + + + + + + + + Keep aspect ratio + + + true + + + + + + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + buttonBox + accepted() + ExportDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ExportDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/latexprocess.cpp b/src/gui/latexprocess.cpp index 39a7a51..0e2185a 100644 --- a/src/gui/latexprocess.cpp +++ b/src/gui/latexprocess.cpp @@ -19,7 +19,7 @@ LatexProcess::LatexProcess(PreviewWindow *preview, QObject *parent) : QObject(pa connect(_proc, SIGNAL(finished(int)), this, SLOT(finished(int))); // for debug purposes - // _workingDir.setAutoRemove(false); + _workingDir.setAutoRemove(false); } void LatexProcess::makePreview(QString tikz) diff --git a/src/gui/previewwindow.cpp b/src/gui/previewwindow.cpp index 7fd6376..d9d22c2 100644 --- a/src/gui/previewwindow.cpp +++ b/src/gui/previewwindow.cpp @@ -3,6 +3,7 @@ #include "tikzit.h" #include "latexprocess.h" +#include "exportdialog.h" #include #include @@ -16,6 +17,7 @@ #include #include #include +#include PreviewWindow::PreviewWindow(QWidget *parent) : QDialog(parent), @@ -31,7 +33,6 @@ PreviewWindow::PreviewWindow(QWidget *parent) : } _doc = nullptr; - _page = nullptr; _loader = new QLabel(ui->tabWidget->tabBar()); _loader->setMinimumSize(QSize(16,16)); @@ -44,6 +45,27 @@ PreviewWindow::PreviewWindow(QWidget *parent) : render(); } +void PreviewWindow::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu menu(this); + QAction *act; + + act = new QAction("Export Image..."); + connect(act, SIGNAL(triggered()), this, SLOT(exportImage())); + menu.addAction(act); + + act = new QAction("Copy to Clipboard"); + connect(act, SIGNAL(triggered()), this, SLOT(copyImageToClipboard())); + menu.addAction(act); + + menu.exec(event->globalPos()); +} + +PdfDocument *PreviewWindow::doc() const +{ + return _doc; +} + PreviewWindow::~PreviewWindow() { delete ui; @@ -51,30 +73,24 @@ PreviewWindow::~PreviewWindow() void PreviewWindow::setPdf(QString file) { - Poppler::Document *oldDoc = _doc; - // use loadFromData to avoid holding a lock on the PDF file in windows - QFile f(file); - f.open(QFile::ReadOnly); - QByteArray data = f.readAll(); - f.close(); - Poppler::Document *newDoc = Poppler::Document::loadFromData(data); - - if (!newDoc) { + //QFile f(file); + //f.open(QFile::ReadOnly); + //QByteArray data = f.readAll(); + //f.close(); + PdfDocument *newDoc = new PdfDocument(file, this); + + if (newDoc->isValid()) { + PdfDocument *oldDoc = _doc; + _doc = newDoc; + if (oldDoc != nullptr) delete oldDoc; + render(); + } else { QMessageBox::warning(nullptr, "Could not read PDF", "Could not read: '" + file + "'."); - return; + delete newDoc; } - - _doc = newDoc; - _doc->setRenderHint(Poppler::Document::Antialiasing); - _doc->setRenderHint(Poppler::Document::TextAntialiasing); - _doc->setRenderHint(Poppler::Document::TextHinting ); - _page = _doc->page(0); - render(); - - if (oldDoc != nullptr) delete oldDoc; } QPlainTextEdit *PreviewWindow::outputTextEdit() @@ -130,30 +146,28 @@ void PreviewWindow::showEvent(QShowEvent *e) { } void PreviewWindow::render() { - if (_page == nullptr) return; - - QSizeF size = _page->pageSizeF(); - - qreal ratio = devicePixelRatioF(); - QRect rect = ui->scrollArea->visibleRegion().boundingRect(); - int w = static_cast(ratio * (rect.width() - 20)); - int h = static_cast(ratio * (rect.height() - 20)); - qreal scale = fmin(static_cast(w) / size.width(), - static_cast(h) / size.height()); + if (_doc != nullptr) { + _doc->renderTo(ui->pdf, + ui->scrollArea->visibleRegion().boundingRect()); + ui->pdf->repaint(); + } +} +void PreviewWindow::exportImage() +{ + if (_doc == nullptr) return; + ExportDialog *d = new ExportDialog(this); + int ret = d->exec(); + if (ret == QDialog::Accepted) { + qDebug() << "save accepted"; + } +} - int dpi = static_cast(scale * 72.0); - int w1 = static_cast(scale * size.width()); - int h1 = static_cast(scale * size.height()); +void PreviewWindow::copyImageToClipboard() +{ + if (_doc != nullptr) { + _doc->copyImageToClipboard(_doc->size() * 4); + } +} - // qDebug() << "visible width:" << w; - // qDebug() << "visible height:" << h; - // qDebug() << "doc width:" << size.width(); - // qDebug() << "doc height:" << size.height(); - // qDebug() << "scale:" << scale; - // qDebug() << "dpi:" << dpi; - QPixmap pm = QPixmap::fromImage(_page->renderToImage(dpi, dpi, (w1 - w)/2, (h1 - h)/2, w, h)); - pm.setDevicePixelRatio(ratio); - ui->pdf->setPixmap(pm); -} diff --git a/src/gui/previewwindow.h b/src/gui/previewwindow.h index a937263..20dd042 100644 --- a/src/gui/previewwindow.h +++ b/src/gui/previewwindow.h @@ -1,10 +1,12 @@ #ifndef PREVIEWWINDOW_H #define PREVIEWWINDOW_H +#include "pdfdocument.h" #include #include #include +#include #include namespace Ui { @@ -20,24 +22,27 @@ public: Running, Success, Failed }; explicit PreviewWindow(QWidget *parent = nullptr); - ~PreviewWindow(); + ~PreviewWindow() override; void setPdf(QString file); QString preparePreview(QString tikz); QPlainTextEdit *outputTextEdit(); void setStatus(Status status); + PdfDocument *doc() const; + public slots: void render(); + void exportImage(); + void copyImageToClipboard(); protected: - void resizeEvent(QResizeEvent *e); - void showEvent(QShowEvent *e); - void closeEvent(QCloseEvent *e); - + void resizeEvent(QResizeEvent *e) override; + void showEvent(QShowEvent *e) override; + void closeEvent(QCloseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *event) override; private: Ui::PreviewWindow *ui; - Poppler::Document *_doc; - Poppler::Page *_page; + PdfDocument *_doc; QLabel *_loader; }; diff --git a/src/tikzit.cpp b/src/tikzit.cpp index e81706c..2e36b21 100644 --- a/src/tikzit.cpp +++ b/src/tikzit.cpp @@ -489,6 +489,11 @@ void Tikzit::cleanupLatex() } } +PreviewWindow *Tikzit::previewWindow() const +{ + return _preview; +} + //StylePalette *Tikzit::stylePalette() const //{ // return _stylePalette; diff --git a/src/tikzit.h b/src/tikzit.h index 24bf56b..15f0b46 100644 --- a/src/tikzit.h +++ b/src/tikzit.h @@ -136,6 +136,8 @@ public: QString styleFilePath() const; void updateRecentFiles(); + PreviewWindow *previewWindow() const; + public slots: void clearRecentFiles(); void setCheckForUpdates(bool check); diff --git a/tikzit.pro b/tikzit.pro index 3d3b12d..d8abd30 100644 --- a/tikzit.pro +++ b/tikzit.pro @@ -1,6 +1,6 @@ # CONFIG += debug -QT += core gui widgets network +QT += core gui widgets network svg test { CONFIG += testcase @@ -73,7 +73,9 @@ SOURCES += src/gui/mainwindow.cpp \ src/gui/styleeditor.cpp \ src/data/stylelist.cpp \ src/gui/previewwindow.cpp \ - src/gui/latexprocess.cpp + src/gui/latexprocess.cpp \ + src/data/pdfdocument.cpp \ + src/gui/exportdialog.cpp HEADERS += src/gui/mainwindow.h \ src/gui/toolpalette.h \ @@ -101,14 +103,17 @@ HEADERS += src/gui/mainwindow.h \ src/gui/styleeditor.h \ src/data/stylelist.h \ src/gui/previewwindow.h \ - src/gui/latexprocess.h + src/gui/latexprocess.h \ + src/data/pdfdocument.h \ + src/gui/exportdialog.h FORMS += src/gui/mainwindow.ui \ src/gui/propertypalette.ui \ src/gui/mainmenu.ui \ src/gui/stylepalette.ui \ src/gui/styleeditor.ui \ - src/gui/previewwindow.ui + src/gui/previewwindow.ui \ + src/gui/exportdialog.ui INCLUDEPATH += src src/gui src/data -- cgit v1.2.3