Seamly2D
Code documentation
vposter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Seamly, LLC *
4  * *
5  * https://github.com/fashionfreedom/seamly2d *
6  * *
7  ***************************************************************************
8  **
9  ** Seamly2D is free software: you can redistribute it and/or modify
10  ** it under the terms of the GNU General Public License as published by
11  ** the Free Software Foundation, either version 3 of the License, or
12  ** (at your option) any later version.
13  **
14  ** Seamly2D is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  ** GNU General Public License for more details.
18  **
19  ** You should have received a copy of the GNU General Public License
20  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
21  **
22  **************************************************************************
23 
24  ************************************************************************
25  **
26  ** @file vposter.cpp
27  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
28  ** @date 11 4, 2015
29  **
30  ** @brief
31  ** @copyright
32  ** This source code is part of the Valentine project, a pattern making
33  ** program, whose allow create and modeling patterns of clothing.
34  ** Copyright (C) 2015 Seamly2D project
35  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
36  **
37  ** Seamly2D is free software: you can redistribute it and/or modify
38  ** it under the terms of the GNU General Public License as published by
39  ** the Free Software Foundation, either version 3 of the License, or
40  ** (at your option) any later version.
41  **
42  ** Seamly2D is distributed in the hope that it will be useful,
43  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
44  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  ** GNU General Public License for more details.
46  **
47  ** You should have received a copy of the GNU General Public License
48  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
49  **
50  *************************************************************************/
51 
52 #include "vposter.h"
53 
54 #include <QGraphicsLineItem>
55 #include <QGraphicsPixmapItem>
56 #include <QGraphicsTextItem>
57 #include <QPen>
58 #include <QPixmap>
59 #include <QPrinter>
60 #include <QRectF>
61 #include <QString>
62 #include <QVector>
63 #include <Qt>
64 #include <QDebug>
65 
66 #include "../vmisc/vmath.h"
67 #include "../vmisc/def.h"
68 
69 //---------------------------------------------------------------------------------------------------------------------
70 VPoster::VPoster(const QPrinter *printer)
71  :printer(printer), allowance(static_cast<quint32>(qRound(10./25.4*PrintDPI)))//1 cm
72 {
73 }
74 
75 //---------------------------------------------------------------------------------------------------------------------
76 QVector<PosterData> VPoster::Calc(const QRect &imageRect, int page, PageOrientation orientation) const
77 {
78  QVector<PosterData> poster;
79 
80  if (printer == nullptr)
81  {
82  return poster;
83  }
84 
85  const int rows = CountRows(imageRect.height(), orientation);
86  const int columns = CountColumns(imageRect.width(), orientation);
87 
88  for (int i=0; i < rows; i++)
89  {
90  for (int j=0; j< columns; j++)
91  {
92  PosterData data = Cut(i, j, imageRect, orientation);
93  data.index = static_cast<quint32>(page);
94  data.rows = static_cast<quint32>(rows);
95  data.columns = static_cast<quint32>(columns);
96  poster.append(data);
97  }
98  }
99 
100  return poster;
101 }
102 
103 //---------------------------------------------------------------------------------------------------------------------
104 QVector<QGraphicsItem *> VPoster::Borders(QGraphicsItem *parent, const PosterData &img, int sheets) const
105 {
107  QPen pen(Qt::NoBrush, 1, Qt::DashLine);
108  pen.setColor(Qt::black);
109 
110  const QRect rec = img.rect;
111  if (img.column != 0)
112  {// Left border
113  auto *line = new QGraphicsLineItem(parent);
114  line->setPen(pen);
115  line->setLine(rec.x(), rec.y(), rec.x(), rec.y() + rec.height());
116  data.append(line);
117 
118  auto *scissors = new QGraphicsPixmapItem(QPixmap("://scissors_vertical.png"), parent);
119  scissors->setPos(rec.x(), rec.y() + rec.height()-static_cast<int>(allowance));
120  data.append(scissors);
121  }
122 
123  if (img.column != img.columns-1)
124  {// Right border
125  auto *line = new QGraphicsLineItem(parent);
126  line->setPen(pen);
127  line->setLine(rec.x() + rec.width()-static_cast<int>(allowance), rec.y(),
128  rec.x() + rec.width()-static_cast<int>(allowance), rec.y() + rec.height());
129  data.append(line);
130  }
131 
132  if (img.row != 0)
133  {// Top border
134  auto *line = new QGraphicsLineItem(parent);
135  line->setPen(pen);
136  line->setLine(rec.x(), rec.y(), rec.x() + rec.width(), rec.y());
137  data.append(line);
138 
139  auto *scissors = new QGraphicsPixmapItem(QPixmap("://scissors_horizontal.png"), parent);
140  scissors->setPos(rec.x() + rec.width()-static_cast<int>(allowance), rec.y());
141  data.append(scissors);
142  }
143 
144  if (img.rows*img.columns > 1)
145  { // Don't show bottom border if only one page need
146  // Bottom border (mandatory)
147  auto *line = new QGraphicsLineItem(parent);
148  line->setPen(pen);
149  line->setLine(rec.x(), rec.y() + rec.height()-static_cast<int>(allowance),
150  rec.x() + rec.width(), rec.y() + rec.height()-static_cast<int>(allowance));
151  data.append(line);
152 
153  if (img.row == img.rows-1)
154  {
155  auto *scissors = new QGraphicsPixmapItem(QPixmap("://scissors_horizontal.png"), parent);
156  scissors->setPos(rec.x() + rec.width()-static_cast<int>(allowance),
157  rec.y() + rec.height()-static_cast<int>(allowance));
158  data.append(scissors);
159  }
160  }
161 
162  // Labels
163  auto *labels = new QGraphicsTextItem(parent);
164 
165  const int layoutX = 15;
166  const int layoutY = 5;
167  labels->setPos(rec.x() + layoutX, rec.y() + rec.height()-static_cast<int>(allowance)+layoutY);
168  labels->setTextWidth(rec.width()-(static_cast<int>(allowance)+layoutX));
169 
170  const QString grid = tr("Grid ( %1 , %2 )").arg(img.row+1).arg(img.column+1);
171  const QString page = tr("Page %1 of %2").arg(img.row*(img.columns)+img.column+1).arg(img.rows*img.columns);
172 
173  QString sheet;
174  if (sheets > 1)
175  {
176  sheet = tr("Sheet %1 of %2").arg(img.index+1).arg(sheets);
177  }
178 
179  labels->setHtml(QString("<table width='100%'>"
180  "<tr>"
181  "<td>%1</td><td align='center'>%2</td><td align='right'>%3</td>"
182  "</tr>"
183  "</table>")
184  .arg(grid, page, sheet));
185 
186  data.append(labels);
187 
188  return data;
189 }
190 
191 //---------------------------------------------------------------------------------------------------------------------
192 int VPoster::CountRows(int height, PageOrientation orientation) const
193 {
194  const qreal imgLength = height;
195  qreal pageLength = 0;
196 
197  if(orientation == PageOrientation::Landscape)
198  {
199  pageLength = PageRect().width();
200  }
201  else
202  {
203  pageLength = PageRect().height();
204  }
205 
206  // Example
207  // ― ―
208  // * *
209  // * *
210  // * *
211  // * * ―
212  // ― ― *
213  // * *
214  // * *
215  // * * ―
216  // * ― *
217  // — *
218  // * *
219  // * * ―
220  // * ― *
221  // * *
222  // — *
223  // * * ―
224  // * ― *
225  // * *
226  // * *
227  // — * ―
228  // ^ ― *
229  //(1) *
230  // *
231  // *
232  // ―
233  //(1) is without allowance
234 
235  return qCeil(imgLength/(pageLength - static_cast<int>(allowance)));
236 }
237 
238 //---------------------------------------------------------------------------------------------------------------------
239 int VPoster::CountColumns(int width, PageOrientation orientation) const
240 {
241  const qreal imgLength = width;
242  qreal pageLength = 0;
243 
244  if(orientation == PageOrientation::Landscape)
245  {
246  pageLength = PageRect().height();
247  }
248  else
249  {
250  pageLength = PageRect().width();
251  }
252 
253  // Example
254  // |----|----|----|----| <- <(1) pages without allowance
255  // |----|+++++++++++++++ <- pages with allowance
256  // |----|+++++++++++
257  // |----|+++++++
258  // |----|+++
259  // |----|
260  // ^
261 
262  return qCeil(imgLength/(pageLength-static_cast<int>(allowance)));
263 }
264 
265 //---------------------------------------------------------------------------------------------------------------------
266 PosterData VPoster::Cut(int i, int j, const QRect &imageRect, PageOrientation orientation) const
267 {
268  Q_UNUSED(imageRect)
269 
270  int pageLengthX, pageLengthY;
271 
272  if(orientation == PageOrientation::Landscape)
273  {
274  pageLengthX = PageRect().height();
275  pageLengthY = PageRect().width();
276  }
277  else
278  {
279  pageLengthX = PageRect().width();
280  pageLengthY = PageRect().height();
281  }
282 
283  const int x = j*pageLengthX - j*static_cast<int>(allowance);
284  const int y = i*pageLengthY - i*static_cast<int>(allowance);
285 
286  SCASSERT(x <= imageRect.width())
287  SCASSERT(y <= imageRect.height())
288 
289  PosterData data;
290  data.row = static_cast<quint32>(i);
291  data.column = static_cast<quint32>(j);
292  data.rect = QRect(x, y, pageLengthX, pageLengthY);
293 
294  return data;
295 }
296 
297 //---------------------------------------------------------------------------------------------------------------------
298 QRect VPoster::PageRect() const
299 {
300  // Because the Point unit is defined to be 1/72th of an inch
301  // we can't use method pageRect(QPrinter::Point). Our dpi value can be different.
302  // We convert value yourself to pixels.
303  const QRectF rect = printer->pageRect(QPrinter::Millimeter);
304 
305  if(printer->fullPage())
306  {
307  QMarginsF pMargins = printer->pageLayout().margins();
308  QRectF newRect = rect.marginsRemoved(pMargins);
309  const QRect pageRectFP(0, 0, qFloor(ToPixel(newRect.width())), qFloor(ToPixel(newRect.height())));
310  return pageRectFP;
311  }
312  else
313  {
314  const QRect pageRect(0, 0, qFloor(ToPixel(rect.width())), qFloor(ToPixel(rect.height())));
315  return pageRect;
316  }
317 }
318 
319 //---------------------------------------------------------------------------------------------------------------------
320 qreal VPoster::ToPixel(qreal val)
321 {
322  return val / 25.4 * PrintDPI; // Mm to pixels with current dpi.
323 }
QVector< PosterData > Calc(const QRect &imageRect, int page, PageOrientation orientation) const
Definition: vposter.cpp:76
int CountColumns(int width, PageOrientation orientation) const
Definition: vposter.cpp:239
quint32 allowance
allowance is the width of the strip that holds the tiled grid information and that is used for the gl...
Definition: vposter.h:99
VPoster(const QPrinter *printer)
Definition: vposter.cpp:70
const QPrinter * printer
Definition: vposter.h:94
PosterData Cut(int i, int j, const QRect &imageRect, PageOrientation orientation) const
Definition: vposter.cpp:266
QVector< QGraphicsItem * > Borders(QGraphicsItem *parent, const PosterData &img, int sheets) const
Definition: vposter.cpp:104
static qreal ToPixel(qreal val)
Definition: vposter.cpp:320
int CountRows(int height, PageOrientation orientation) const
Definition: vposter.cpp:192
QRect PageRect() const
Definition: vposter.cpp:298
const qreal PrintDPI
Definition: def.cpp:228
#define SCASSERT(cond)
Definition: def.h:317
PageOrientation
Definition: def.h:110
quint32 rows
Definition: vposter.h:78
quint32 columns
Definition: vposter.h:79
quint32 row
Definition: vposter.h:76
quint32 index
Definition: vposter.h:75
QRect rect
Definition: vposter.h:80
quint32 column
Definition: vposter.h:77