/*
Copyright 2015 Martin Banky
Copyright 2014-2015 Adam Reichold
This file is part of qpdfview.
qpdfview is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
qpdfview is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with qpdfview. If not, see .
*/
#include "fitzmodel.h"
#include
#include
extern "C"
{
#include
#include
typedef struct pdf_document_s pdf_document;
pdf_document* pdf_specifics(fz_context*, fz_document*);
}
namespace
{
using namespace qpdfview;
using namespace qpdfview::Model;
void loadOutline(fz_outline* outline, QStandardItem* parent)
{
QStandardItem* item = new QStandardItem(QString::fromUtf8(outline->title));
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
if(outline->dest.kind != FZ_LINK_NONE)
{
const int page = outline->dest.ld.gotor.page + 1;
item->setData(page, Document::PageRole);
QStandardItem* pageItem = item->clone();
pageItem->setText(QString::number(page));
pageItem->setTextAlignment(Qt::AlignRight);
parent->appendRow(QList< QStandardItem* >() << item << pageItem);
}
else
{
parent->appendRow(item);
}
if(outline->next != 0)
{
loadOutline(outline->next, parent);
}
if(outline->down != 0)
{
loadOutline(outline->down, item);
}
}
} // anonymous
namespace qpdfview
{
namespace Model
{
FitzPage::FitzPage(const FitzDocument* parent, fz_page* page) :
m_parent(parent),
m_page(page)
{
}
FitzPage::~FitzPage()
{
fz_drop_page(m_parent->m_context, m_page);
}
QSizeF FitzPage::size() const
{
QMutexLocker mutexLocker(&m_parent->m_mutex);
fz_rect rect;
fz_bound_page(m_parent->m_context, m_page, &rect);
return QSizeF(rect.x1 - rect.x0, rect.y1 - rect.y0);
}
QImage FitzPage::render(qreal horizontalResolution, qreal verticalResolution, Rotation rotation, const QRect& boundingRect) const
{
QMutexLocker mutexLocker(&m_parent->m_mutex);
fz_matrix matrix;
fz_scale(&matrix, horizontalResolution / 72.0f, verticalResolution / 72.0f);
switch(rotation)
{
default:
case RotateBy0:
fz_pre_rotate(&matrix, 0.0);
break;
case RotateBy90:
fz_pre_rotate(&matrix, 90.0);
break;
case RotateBy180:
fz_pre_rotate(&matrix, 180.0);
break;
case RotateBy270:
fz_pre_rotate(&matrix, 270.0);
break;
}
fz_rect rect;
fz_bound_page(m_parent->m_context, m_page, &rect);
fz_transform_rect(&rect, &matrix);
fz_irect irect;
fz_round_rect(&irect, &rect);
fz_context* context = fz_clone_context(m_parent->m_context);
fz_display_list* display_list = fz_new_display_list(context);
fz_device* device = fz_new_list_device(context, display_list);
fz_run_page(m_parent->m_context, m_page, device, &matrix, 0);
fz_drop_device(m_parent->m_context, device);
mutexLocker.unlock();
fz_matrix tileMatrix;
fz_translate(&tileMatrix, -rect.x0, -rect.y0);
fz_rect tileRect = fz_infinite_rect;
int tileWidth = irect.x1 - irect.x0;
int tileHeight = irect.y1 - irect.y0;
if(!boundingRect.isNull())
{
fz_pre_translate(&tileMatrix, -boundingRect.x(), -boundingRect.y());
tileRect.x0 = tileRect.y0 = 0.0;
tileWidth = tileRect.x1 = boundingRect.width();
tileHeight = tileRect.y1 = boundingRect.height();
}
QImage image(tileWidth, tileHeight, QImage::Format_RGB32);
image.fill(m_parent->m_paperColor);
fz_pixmap* pixmap = fz_new_pixmap_with_data(context, fz_device_bgr(context), image.width(), image.height(), image.bits());
device = fz_new_draw_device(context, pixmap);
fz_run_display_list(context, display_list, device, &tileMatrix, &tileRect, 0);
fz_drop_device(context, device);
fz_drop_pixmap(context, pixmap);
fz_drop_display_list(context, display_list);
fz_drop_context(context);
return image;
}
QList< Link* > FitzPage::links() const
{
QMutexLocker mutexLocker(&m_parent->m_mutex);
QList< Link* > links;
fz_rect rect;
fz_bound_page(m_parent->m_context, m_page, &rect);
const qreal width = qAbs(rect.x1 - rect.x0);
const qreal height = qAbs(rect.y1 - rect.y0);
fz_link* first_link = fz_load_links(m_parent->m_context, m_page);
for(fz_link* link = first_link; link != 0; link = link->next)
{
const QRectF boundary = QRectF(link->rect.x0 / width, link->rect.y0 / height, (link->rect.x1 - link->rect.x0) / width, (link->rect.y1 - link->rect.y0) / height).normalized();
if(link->dest.kind == FZ_LINK_GOTO)
{
const int page = link->dest.ld.gotor.page + 1;
links.append(new Link(boundary, page));
}
else if(link->dest.kind == FZ_LINK_GOTOR)
{
const int page = link->dest.ld.gotor.page + 1;
if(link->dest.ld.gotor.file_spec != 0)
{
links.append(new Link(boundary, QString::fromUtf8(link->dest.ld.gotor.file_spec), page));
}
else
{
links.append(new Link(boundary, page));
}
}
else if(link->dest.kind == FZ_LINK_URI)
{
const QString url = QString::fromUtf8(link->dest.ld.uri.uri);
links.append(new Link(boundary, url));
}
else if(link->dest.kind == FZ_LINK_LAUNCH)
{
const QString url = QString::fromUtf8(link->dest.ld.launch.file_spec);
links.append(new Link(boundary, url));
}
}
fz_drop_link(m_parent->m_context, first_link);
return links;
}
FitzDocument::FitzDocument(fz_context* context, fz_document* document) :
m_mutex(),
m_context(context),
m_document(document),
m_paperColor(Qt::white)
{
}
FitzDocument::~FitzDocument()
{
fz_drop_document(m_context, m_document);
fz_drop_context(m_context);
}
int FitzDocument::numberOfPages() const
{
QMutexLocker mutexLocker(&m_mutex);
return fz_count_pages(m_context, m_document);
}
Page* FitzDocument::page(int index) const
{
QMutexLocker mutexLocker(&m_mutex);
fz_page* page = fz_load_page(m_context, m_document, index);
return page != 0 ? new FitzPage(this, page) : 0;
}
bool FitzDocument::canBePrintedUsingCUPS() const
{
QMutexLocker mutexLocker(&m_mutex);
return pdf_specifics(m_context, m_document) != 0;
}
void FitzDocument::setPaperColor(const QColor& paperColor)
{
m_paperColor = paperColor;
}
void FitzDocument::loadOutline(QStandardItemModel* outlineModel) const
{
Document::loadOutline(outlineModel);
QMutexLocker mutexLocker(&m_mutex);
fz_outline* outline = fz_load_outline(m_context, m_document);
if(outline != 0)
{
::loadOutline(outline, outlineModel->invisibleRootItem());
fz_drop_outline(m_context, outline);
}
}
} // Model
FitzPlugin::FitzPlugin(QObject* parent) : QObject(parent)
{
setObjectName("FitzPlugin");
m_locks_context.user = this;
m_locks_context.lock = FitzPlugin::lock;
m_locks_context.unlock = FitzPlugin::unlock;
m_context = fz_new_context(0, &m_locks_context, FZ_STORE_DEFAULT);
fz_register_document_handlers(m_context);
}
FitzPlugin::~FitzPlugin()
{
fz_drop_context(m_context);
}
Model::Document* FitzPlugin::loadDocument(const QString& filePath) const
{
fz_context* context = fz_clone_context(m_context);
if(context == 0)
{
return 0;
}
#ifdef _MSC_VER
fz_document* document = fz_open_document(context, filePath.toUtf8());
#else
fz_document* document = fz_open_document(context, QFile::encodeName(filePath));
#endif // _MSC_VER
if(document == 0)
{
fz_drop_context(context);
return 0;
}
return new Model::FitzDocument(context, document);
}
void FitzPlugin::lock(void* user, int lock)
{
static_cast< FitzPlugin* >(user)->m_mutex[lock].lock();
}
void FitzPlugin::unlock(void* user, int lock)
{
static_cast< FitzPlugin* >(user)->m_mutex[lock].unlock();
}
} // qpdfview
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
Q_EXPORT_PLUGIN2(qpdfview_fitz, qpdfview::FitzPlugin)
#endif // QT_VERSION