Engauge Digitizer  2
DlgImportCroppingPdf.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "DlgImportCroppingPdf.h"
8 #include "EngaugeAssert.h"
9 #include "Logger.h"
10 #include "MainWindow.h"
11 #include "PdfCropping.h"
12 #include "poppler-qt5.h"
13 #include <QApplication>
14 #include <QGraphicsPixmapItem>
15 #include <QGraphicsScene>
16 #include <QGridLayout>
17 #include <QImage>
18 #include <QLabel>
19 #include <QLayout>
20 #include <QPushButton>
21 #include <QSettings>
22 #include <QSpinBox>
23 #include <QTimer>
24 #include "Settings.h"
25 #include "ViewPreview.h"
26 
27 using namespace Poppler;
28 
29 int DlgImportCroppingPdf::MINIMUM_DIALOG_WIDTH = 350;
30 int DlgImportCroppingPdf::MINIMUM_PREVIEW_HEIGHT = 200;
31 const int X_TOP_LEFT = 0, Y_TOP_LEFT = 0;
32 const int WIDTH = -1, HEIGHT = -1; // Negative values give full page
33 const int FIRST_PAGE_1_BASED = 1;
34 const int SMALLEST_DELAY_MS = 500; // Below 500 triggers "double jump" bug in linux
35 
36 DlgImportCroppingPdf::DlgImportCroppingPdf(const Poppler::Document &document,
37  int resolution) :
38  m_document (document),
39  m_resolution (resolution),
40  m_pixmap (nullptr)
41 {
42  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::DlgImportCroppingPdf";
43 
44  setWindowTitle (tr ("PDF File Import Cropping"));
45  setModal (true);
46 
47  QWidget *subPanel = new QWidget ();
48  QGridLayout *layout = new QGridLayout (subPanel);
49  subPanel->setLayout (layout);
50 
51  int row = 0;
52 
53  createTimer ();
54  createPageSpinner (layout, row);
55  createPreview (layout, row);
56  finishPanel (subPanel);
57  updatePreview ();
58 
59  // Bring the two middle columns together
60  layout->setColumnStretch (0, 1);
61  layout->setColumnStretch (1, 0);
62  layout->setColumnStretch (2, 0);
63  layout->setColumnStretch (3, 1);
64 }
65 
66 DlgImportCroppingPdf::~DlgImportCroppingPdf()
67 {
68  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::~DlgImportCroppingPdf";
69 }
70 
71 void DlgImportCroppingPdf::createPageSpinner (QGridLayout *layout,
72  int &row)
73 {
74  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::createPageSpinner";
75 
76  const int MIN_WIDTH_SPINNER = 90;
77 
78  QLabel *labelPage = new QLabel (QString ("%1:").arg (tr ("Page")));
79  layout->addWidget (labelPage, row, 1, 1, 1);
80 
81  m_spinPage = new QSpinBox;
82  m_spinPage->setMinimumWidth (MIN_WIDTH_SPINNER);
83  m_spinPage->setWhatsThis (tr ("Page number that will be imported"));
84  m_spinPage->setRange (1, m_document.numPages());
85  layout->addWidget (m_spinPage, row++, 2, 1, 1);
86  connect (m_spinPage, SIGNAL (valueChanged (int)), this, SLOT (slotPage (int)));
87 }
88 
89 void DlgImportCroppingPdf::createPdfCropping ()
90 {
91  // Create frame that shows what will be included, and what will be excluded, during the import
92  m_pdfCropping = new PdfCropping (*m_scenePreview,
93  *m_viewPreview);
94 }
95 
96 void DlgImportCroppingPdf::createPreview (QGridLayout *layout,
97  int &row)
98 {
99  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::createPreview";
100 
101  QLabel *labelPreview = new QLabel (tr ("Preview"));
102  layout->addWidget (labelPreview, row++, 0, 1, 1, Qt::AlignLeft);
103 
104  m_scenePreview = new QGraphicsScene (this);
105  m_viewPreview = new ViewPreview (m_scenePreview,
106  ViewPreview::VIEW_ASPECT_RATIO_ONE_TO_ONE,
107  this);
108  m_viewPreview->setWhatsThis (tr ("Preview window that shows what part of the image will be imported. "
109  "The image portion inside the rectangular frame will be imported from the currently selected page. "
110  "The frame can be moved and resized by dragging the corner handles."));
111  m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
112  m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
113  m_viewPreview->setMinimumHeight (MINIMUM_PREVIEW_HEIGHT);
114  layout->addWidget (m_viewPreview, row++, 0, 1, 4);
115 
116  // More preview initialization
117  initializeFrameGeometryAndPixmap (); // Before first call to updatePreview
118  createPdfCropping ();
119 }
120 
121 void DlgImportCroppingPdf::createTimer ()
122 {
123  m_timer = new QTimer;
124  m_timer->setSingleShot (true);
125  connect (m_timer, SIGNAL (timeout ()), this, SLOT (slotTimeout ()));
126 }
127 
128 void DlgImportCroppingPdf::finishPanel (QWidget *subPanel)
129 {
130  const int STRETCH_OFF = 0, STRETCH_ON = 1;
131 
132  QVBoxLayout *panelLayout = new QVBoxLayout (this);
133 
134  setMinimumWidth (MINIMUM_DIALOG_WIDTH);
135  setLayout (panelLayout);
136 
137  panelLayout->addWidget (subPanel);
138  panelLayout->setStretch (panelLayout->count () - 1, STRETCH_ON);
139 
140  QWidget *panelButtons = new QWidget (this);
141  QHBoxLayout *buttonLayout = new QHBoxLayout (panelButtons);
142 
143  QHBoxLayout *layoutRightSide = new QHBoxLayout;
144 
145  QWidget *widgetRightSide = new QWidget;
146  widgetRightSide->setLayout (layoutRightSide);
147  buttonLayout->addWidget (widgetRightSide);
148 
149  QSpacerItem *spacerExpanding = new QSpacerItem (40, 5, QSizePolicy::Expanding, QSizePolicy::Expanding);
150  layoutRightSide->addItem (spacerExpanding);
151 
152  m_btnOk = new QPushButton (tr ("Ok"));
153  layoutRightSide->addWidget (m_btnOk, 0, Qt::AlignRight);
154  connect (m_btnOk, SIGNAL (released ()), this, SLOT (slotOk ()));
155 
156  QSpacerItem *spacerFixed = new QSpacerItem (40, 5, QSizePolicy::Fixed, QSizePolicy::Fixed);
157  layoutRightSide->addItem (spacerFixed);
158 
159  m_btnCancel = new QPushButton (tr ("Cancel"));
160  layoutRightSide->addWidget (m_btnCancel, 0, Qt::AlignRight);
161  connect (m_btnCancel, SIGNAL (released ()), this, SLOT (slotCancel ()));
162 
163  panelLayout->addWidget (panelButtons, STRETCH_ON);
164  panelLayout->setStretch (panelLayout->count () - 1, STRETCH_OFF);
165 }
166 
168 {
169  // If the entire page was to be returned, then this method would simply return m_image. However, only the framed
170  // portion is to be returned
171  ENGAUGE_ASSERT (m_pdfCropping != nullptr);
172  QRectF rectFramePixels = m_pdfCropping->frameRect ();
173 
174  return m_image.copy (rectFramePixels.toRect ());
175 }
176 
177 void DlgImportCroppingPdf::initializeFrameGeometryAndPixmap ()
178 {
179  m_image = loadImage (FIRST_PAGE_1_BASED);
180  QGraphicsPixmapItem *pixmap = new QGraphicsPixmapItem (QPixmap::fromImage (m_image));
181  m_scenePreview->addItem (pixmap);
182 
183  // Force resize so image fills preview area. We do this only once initially for speed
184  m_viewPreview->setSceneRect (pixmap->boundingRect ());
185 }
186 
187 QImage DlgImportCroppingPdf::loadImage (int page1Based) const
188 {
189  QImage image;
190 
191  int page0Based = page1Based - 1;
192  Page *page = m_document.page (page0Based);
193  if (page != nullptr) {
194 
195  image = page->renderToImage (m_resolution,
196  m_resolution,
197  X_TOP_LEFT,
198  Y_TOP_LEFT,
199  WIDTH,
200  HEIGHT);
201 
202  delete page;
203  }
204 
205  return image;
206 }
207 
208 void DlgImportCroppingPdf::saveGeometryToSettings()
209 {
210  // Store the settings for use by showEvent
211  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
212  settings.beginGroup (SETTINGS_GROUP_IMPORT_CROPPING);
213  settings.setValue (SETTINGS_IMPORT_CROPPING_POS, saveGeometry ());
214  settings.endGroup();
215 }
216 
217 void DlgImportCroppingPdf::showEvent (QShowEvent * /* event */)
218 {
219  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
220  settings.beginGroup (SETTINGS_GROUP_IMPORT_CROPPING);
221  if (settings.contains (SETTINGS_IMPORT_CROPPING_POS)) {
222 
223  // Restore the settings that were stored by the last call to saveGeometryToSettings
224  restoreGeometry (settings.value (SETTINGS_IMPORT_CROPPING_POS).toByteArray ());
225  }
226  settings.endGroup ();
227 }
228 
229 void DlgImportCroppingPdf::slotCancel ()
230 {
231  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::slotCancel";
232 
233  // Restore cursor in case updatePreview has not already completed and then restored it
234  QApplication::restoreOverrideCursor ();
235 
236  setResult (QDialog::Rejected);
237  saveGeometryToSettings();
238  hide();
239 }
240 
241 void DlgImportCroppingPdf::slotOk ()
242 {
243  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::slotOk";
244 
245  // Restore cursor in case updatePreview has not already completed and then restored it
246  QApplication::restoreOverrideCursor ();
247 
248  setResult (QDialog::Accepted);
249  saveGeometryToSettings();
250  hide();
251 }
252 
253 void DlgImportCroppingPdf::slotPage (int page)
254 {
255  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::slotPage"
256  << " page=" << page
257  << " stepBy=" << m_spinPage->singleStep ();
258 
259  // Show wait cursor until slow calculations are over
260  QApplication::setOverrideCursor (Qt::WaitCursor);
261 
262  m_timer->start (SMALLEST_DELAY_MS);
263 }
264 
265 void DlgImportCroppingPdf::slotTimeout ()
266 {
267  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::slotTimeout";
268 
269  updatePreview ();
270 }
271 
272 void DlgImportCroppingPdf::updatePreview ()
273 {
274  LOG4CPP_INFO_S ((*mainCat)) << "DlgImportCroppingPdf::updatePreview";
275 
276  if (m_pixmap != nullptr) {
277  m_scenePreview->removeItem (m_pixmap);
278  }
279 
280  m_image = loadImage (m_spinPage->value ());
281  m_pixmap = new QGraphicsPixmapItem (QPixmap::fromImage (m_image));
282  m_scenePreview->addItem (m_pixmap);
283 
284  // Calculations for preview updating are now over
285  QApplication::restoreOverrideCursor ();
286 }
DlgImportCroppingPdf(const Poppler::Document &document, int resolution)
Single constructor.
QRectF frameRect() const
Frame rectangle selected by user.
Definition: PdfCropping.cpp:69
Class that modifies QGraphicsView to automatically expand/shrink the view to fit the window,...
Definition: ViewPreview.h:14
This class shows a frame around the selected portion of the pdf import preview window.
Definition: PdfCropping.h:24
QImage image() const
Image that was selected. Value is null if loading failed.
virtual void showEvent(QShowEvent *event)
Do preparation before dialog is displayed.