mupdf-qt  0.1
Qt5 interface of the popular PDF library MuPDF
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Pages
mupdf-page.cpp
Go to the documentation of this file.
1 #include "mupdf-page.h"
2 #include "mupdf-page_p.h"
3 #include "mupdf-document.h"
4 #include "mupdf-document_p.h"
5 #include "mupdf-textbox.h"
6 #include "mupdf-textbox_p.h"
7 extern "C" {
8 #include <mupdf/fitz.h>
9 }
10 #include <QImage>
11 #include <QSizeF>
12 
13 static void clear_bgr_samples_with_value(
14  unsigned char *samples, int size,
15  int b, int g, int r, int a)
16 {
17  int i = 0;
18 
19  while (i < size) {
20  *(samples + i++) = b;
21  *(samples + i++) = g;
22  *(samples + i++) = r;
23  *(samples + i++) = a;
24  }
25 }
26 
27 static void clear_rgb_samples_with_value(
28  unsigned char *samples, int size,
29  int b, int g, int r, int a)
30 {
31  int i = 0;
32 
33  while (i < size) {
34  *(samples + i++) = r;
35  *(samples + i++) = g;
36  *(samples + i++) = b;
37  *(samples + i++) = a;
38  }
39 }
40 
44 static inline void imageCleanupHandler(void *data)
45 {
46  unsigned char *samples = static_cast<unsigned char *>(data);
47 
48  if (samples) {
49  delete []samples;
50  }
51 }
52 
53 namespace MuPDF
54 {
55 
56 QPointF mapToOrigin(const QPointF &pos, float scaleX, float scaleY, float rotation)
57 {
58  fz_matrix transform = fz_identity;
59  fz_matrix inverse;
60  fz_point point;
61 
62  // build matrix
63  fz_rotate(&transform, rotation);
64  fz_pre_scale(&transform, scaleX, scaleY);
65 
66  // invert matrix
67  fz_invert_matrix(&inverse, &transform);
68 
69  // map
70  point.x = pos.x();
71  point.y = pos.y();
72  fz_transform_point(&point, &inverse);
73 
74  return QPointF(point.x, point.y);
75 }
76 
77 QSizeF mapToOrigin(const QSizeF &size, float scaleX, float scaleY, float rotation)
78 {
79  fz_matrix transform = fz_identity;
80  fz_matrix inverse;
81  fz_point vector;
82 
83  // build matrix
84  fz_rotate(&transform, rotation);
85  fz_pre_scale(&transform, scaleX, scaleY);
86 
87  // invert matrix
88  fz_invert_matrix(&inverse, &transform);
89 
90  // map
91  vector.x = size.width();
92  vector.y = size.height();
93  fz_transform_vector(&vector, &inverse);
94 
95  return QSizeF(vector.x, vector.y);
96 }
97 
98 QRectF mapToOrigin(const QRectF &rect, float scaleX, float scaleY, float rotation)
99 {
100  fz_matrix transform = fz_identity;
101  fz_matrix inverse;
102  fz_rect r;
103 
104  // build matrix
105  fz_rotate(&transform, rotation);
106  fz_pre_scale(&transform, scaleX, scaleY);
107 
108  // invert matrix
109  fz_invert_matrix(&inverse, &transform);
110 
111  // map
112  r.x0 = rect.left();
113  r.y0 = rect.top();
114  r.x1 = rect.right();
115  r.y1 = rect.bottom();
116  fz_transform_rect(&r, &inverse);
117 
118  return QRectF(r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
119 }
120 
121 QPointF mapFromOrigin(const QPointF &pos, float scaleX, float scaleY, float rotation)
122 {
123  fz_matrix transform = fz_identity;
124  fz_point point;
125 
126  // build matrix
127  fz_rotate(&transform, rotation);
128  fz_pre_scale(&transform, scaleX, scaleY);
129 
130  // map
131  point.x = pos.x();
132  point.y = pos.y();
133  fz_transform_point(&point, &transform);
134 
135  return QPointF(point.x, point.y);
136 }
137 
138 QSizeF mapFromOrigin(const QSizeF &size, float scaleX, float scaleY, float rotation)
139 {
140  fz_matrix transform = fz_identity;
141  fz_point vector;
142 
143  // build matrix
144  fz_rotate(&transform, rotation);
145  fz_pre_scale(&transform, scaleX, scaleY);
146 
147  // map
148  vector.x = size.width();
149  vector.y = size.height();
150  fz_transform_vector(&vector, &transform);
151 
152  return QSizeF(vector.x, vector.y);
153 }
154 
155 QRectF mapFromOrigin(const QRectF &rect, float scaleX, float scaleY, float rotation)
156 {
157  fz_matrix transform = fz_identity;
158  fz_rect r;
159 
160  // build matrix
161  fz_rotate(&transform, rotation);
162  fz_pre_scale(&transform, scaleX, scaleY);
163 
164  // map
165  r.x0 = rect.left();
166  r.y0 = rect.top();
167  r.x1 = rect.right();
168  r.y1 = rect.bottom();
169  fz_transform_rect(&r, &transform);
170 
171  return QRectF(r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
172 }
173 
175 {
176  delete d;
177  d = NULL;
178 }
179 
180 PagePrivate::PagePrivate(DocumentPrivate *dp, int index)
181  : documentp(dp)
182  , context(documentp->context)
183  , document(documentp->document)
184  , page(NULL)
185  , display_list(NULL)
186  , text_sheet(NULL)
187  , text_page(NULL)
188  , transparent(documentp->transparent)
189  , b(documentp->b), g(documentp->g), r(documentp->r), a(documentp->a)
190 {
191  fz_try(context)
192  {
193  fz_rect bounds;
194  fz_device *list_device;
195  fz_device *text_device;
196 
197  // load page
198  page = fz_load_page(document, index);
199 
200  // display list
201  display_list = fz_new_display_list(context);
202  list_device = fz_new_list_device(context, display_list);
203  fz_run_page(document, page, list_device, &fz_identity, NULL);
204  fz_free_device(list_device);
205 
206  // create text sheet and text page
207  text_sheet = fz_new_text_sheet(context);
208  text_page = fz_new_text_page(context);
209  text_device = fz_new_text_device(context, text_sheet, text_page);
210  fz_bound_page(document, page, &bounds);
211  fz_run_display_list(display_list, text_device, &fz_identity, &bounds, NULL);
212  fz_free_device(text_device);
213  }
214  fz_catch(context)
215  {
216  deleteData();
217  }
218 }
219 
223 bool Page::isValid() const
224 {
225  return (d && d->page) ? true : false;
226 }
227 
239 QImage Page::renderImage(float scaleX, float scaleY, float rotation) const
240 {
241  fz_pixmap *pixmap = NULL;
242  unsigned char *samples = NULL;
243  unsigned char *copyed_samples = NULL;
244  int width = 0;
245  int height = 0;
246  int size = 0;
247 
248  // build transform matrix
249  fz_matrix transform = fz_identity;
250  fz_rotate(&transform, rotation);
251  fz_pre_scale(&transform, scaleX, scaleY);
252 
253  // get transformed page size
254  fz_rect bounds;
255  fz_irect bbox;
256  fz_bound_page(d->document, d->page, &bounds);
257  fz_transform_rect(&bounds, &transform);
258  fz_round_rect(&bbox, &bounds);
259 
260  // render to pixmap
261  fz_device *dev = NULL;
262  fz_try(d->context)
263  {
264  // fz_pixmap will always include a separate alpha channel
265 #if QT_VERSION < 0x050200
266  pixmap = fz_new_pixmap_with_bbox(d->context, fz_device_bgr(d->context), &bbox);
267 #else
268  // use rgba for Qt5.2
269  pixmap = fz_new_pixmap_with_bbox(d->context, fz_device_rgb(d->context), &bbox);
270 #endif
271  samples = fz_pixmap_samples(d->context, pixmap);
272  width = fz_pixmap_width(d->context, pixmap);
273  height = fz_pixmap_height(d->context, pixmap);
274  size = width * height * 4;
275  if (!d->transparent) {
276  if (d->b >= 0 && d->g >= 0 && d->r >= 0 && d->a >= 0) {
277  // with user defined background color
278 #if QT_VERSION < 0x050200
279  clear_bgr_samples_with_value(samples, size, d->b, d->g, d->r, d->a);
280 #else
281  // use rgba for Qt5.2
282  clear_rgb_samples_with_value(samples, size, d->b, d->g, d->r, d->a);
283 #endif
284  } else {
285  // with white background
286  fz_clear_pixmap_with_value(d->context, pixmap, 0xff);
287  }
288  }
289  dev = fz_new_draw_device(d->context, pixmap);
290  fz_run_display_list(d->display_list, dev, &transform, &bounds, NULL);
291  }
292  fz_always(d->context)
293  {
294  if (dev) {
295  fz_free_device(dev);
296  }
297  dev = NULL;
298  }
299  fz_catch(d->context)
300  {
301  if (pixmap) {
302  fz_drop_pixmap(d->context, pixmap);
303  }
304  pixmap = NULL;
305  }
306 
307  // render to QImage
308  QImage image;
309  if (NULL == pixmap) {
310  return image;
311  }
312  copyed_samples = new unsigned char[size];
313  memcpy(copyed_samples, samples, size);
314  fz_drop_pixmap(d->context, pixmap);
315 #if QT_VERSION < 0x050200
316  // most computers use little endian, so Format_ARGB32 means bgra order
317  // note: this is not correct for computers with big endian architecture
318  image = QImage(copyed_samples,
319  width, height, QImage::Format_ARGB32, imageCleanupHandler, copyed_samples);
320 #else
321  // with Qt 5.2, Format_RGBA8888 is correct for any architecture
322  image = QImage(copyed_samples,
323  width, height, QImage::Format_RGBA8888, imageCleanupHandler, copyed_samples);
324 #endif
325  return image;
326 }
327 
331 QSizeF Page::size() const
332 {
333  fz_rect rect;
334 
335  fz_bound_page(d->document, d->page, &rect);
336  return QSizeF(rect.x1 - rect.x0, rect.y1 - rect.y0);
337 }
338 
347 {
348  d->transparent = enable;
349 }
350 
365 void Page::setBackgroundColor(int r, int g, int b, int a)
366 {
367  d->r = r;
368  d->g = g;
369  d->b = b;
370  d->a = a;
371 }
372 
378 QString Page::text(const QRectF &rect) const
379 {
380  QString ret;
381  fz_rect r;
382  char *str;
383 
384  // build fz_rect
385  r.x0 = rect.left();
386  r.y0 = rect.top();
387  r.x1 = rect.right();
388  r.y1 = rect.bottom();
389 
390  // get text
391  if (!fz_is_infinite_rect(&r)) {
392  str = fz_copy_selection(d->context, d->text_page, r);
393  ret = QString::fromUtf8(str);
394  free(str);
395  }
396 
397  return ret;
398 }
399 
406 QList<TextBox *> Page::textList() const
407 {
408  QList<TextBox *> ret;
409  TextBox *box;
410  TextBoxPrivate *boxp;
411  fz_text_block *block;
412  fz_text_line *line;
413  fz_text_span *span;
414 
415  for (int block_num = 0; block_num < d->text_page->len; ++block_num) {
416  // get block
417  if (d->text_page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) {
418  continue;
419  }
420  block = d->text_page->blocks[block_num].u.text;
421 
422  for (line = block->lines; line < block->lines + block->len; ++line) { // lines
423  for (span = line->first_span; span; span = span->next) { // spans
424  boxp = new TextBoxPrivate(span);
425  box = new TextBox(boxp);
426  ret << box;
427  }
428  }
429  }
430 
431  return ret;
432 }
433 
434 PagePrivate::~PagePrivate()
435 {
436  if (page) {
437  deleteData();
438  documentp->pages.removeAt(documentp->pages.indexOf(this));
439  }
440 }
441 
442 } // end namespace MuPDF