Magnifier tool

This commit is contained in:
Jakub Melka 2020-02-22 18:27:33 +01:00
parent 5823df0443
commit e9bad256dc
13 changed files with 385 additions and 6 deletions

View File

@ -647,15 +647,35 @@ QMatrix PDFDrawWidgetProxy::createPagePointToDevicePointMatrix(const PDFPage* pa
void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
{
painter->fillRect(rect, Qt::lightGray);
drawPages(painter, rect);
// Use current paper color (it can be a bit different from white)
for (IDocumentDrawInterface* drawInterface : m_drawInterfaces)
{
painter->save();
drawInterface->drawPostRendering(painter, rect);
painter->restore();
}
}
QColor PDFDrawWidgetProxy::getPaperColor()
{
QColor paperColor = getCMSManager()->getCurrentCMS()->getPaperColor();
if (m_features.testFlag(PDFRenderer::InvertColors))
{
paperColor = invertColor(paperColor);
}
return paperColor;
}
void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect)
{
painter->fillRect(rect, Qt::lightGray);
QMatrix baseMatrix = painter->worldMatrix();
// Use current paper color (it can be a bit different from white)
QColor paperColor = getPaperColor();
// Iterate trough pages and display them on the painter device
for (const LayoutItem& item : m_layout.items)
{
@ -675,7 +695,7 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
timer.start();
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex);
QMatrix matrix = createPagePointToDevicePointMatrix(page, placedRect);
QMatrix matrix = createPagePointToDevicePointMatrix(page, placedRect) * baseMatrix;
compiledPage->draw(painter, page->getCropBox(), matrix, m_features);
PDFTextLayoutGetter layoutGetter = m_textLayoutCompiler->getTextLayoutLazy(item.pageIndex);
@ -1314,4 +1334,10 @@ void IDocumentDrawInterface::drawPage(QPainter* painter,
Q_UNUSED(pagePointToDevicePointMatrix);
}
void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) const
{
Q_UNUSED(painter);
Q_UNUSED(rect);
}
} // namespace pdf

View File

@ -58,6 +58,11 @@ public:
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const;
/// Performs drawing of additional graphics after all pages are drawn onto the painter.
/// \param painter Painter
/// \param rect Draw rectangle (usually viewport rectangle of the pdf widget)
virtual void drawPostRendering(QPainter* painter, QRect rect) const;
};
/// This class controls draw space - page layout. Pages are divided into blocks
@ -210,10 +215,19 @@ public:
/// Draws the actually visible pages on the painter using the rectangle.
/// Rectangle is space in the widget, which is used for painting the PDF.
/// This function is using drawPages function to draw all pages. After that,
/// custom drawing is performed.
/// \sa drawPages
/// \param painter Painter to paint the PDF pages
/// \param rect Rectangle in which the content is painted
void draw(QPainter* painter, QRect rect);
/// Draws the actually visible pages on the painter using the rectangle.
/// Rectangle is space in the widget, which is used for painting the PDF.
/// \param painter Painter to paint the PDF pages
/// \param rect Rectangle in which the content is painted
void drawPages(QPainter* painter, QRect rect);
/// Draws thumbnail image of the given size (so larger of the page size
/// width or height equals to pixel size and the latter size is rescaled
/// using the aspect ratio)
@ -334,6 +348,9 @@ public:
void registerDrawInterface(IDocumentDrawInterface* drawInterface) { m_drawInterfaces.insert(drawInterface); }
void unregisterDrawInterface(IDocumentDrawInterface* drawInterface) { m_drawInterfaces.erase(drawInterface); }
/// Returns current paper color
QColor getPaperColor();
signals:
void drawSpaceChanged();
void pageLayoutChanged();

View File

@ -664,6 +664,7 @@ PDFToolManager::PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObje
{
m_predefinedTools[FindTextTool] = new PDFFindTextTool(proxy, actions.findPrevAction, actions.findNextAction, this, parentDialog);
m_predefinedTools[SelectTextTool] = new PDFSelectTextTool(proxy, actions.selectTextToolAction, actions.copyTextAction, actions.selectAllAction, actions.deselectAction, this);
m_predefinedTools[MagnifierTool] = new PDFMagnifierTool(proxy, actions.magnifierAction, this);
for (PDFWidgetTool* tool : m_predefinedTools)
{
@ -719,6 +720,11 @@ PDFFindTextTool* PDFToolManager::getFindTextTool() const
return qobject_cast<PDFFindTextTool*>(m_predefinedTools[FindTextTool]);
}
PDFMagnifierTool* PDFToolManager::getMagnifierTool() const
{
return qobject_cast<PDFMagnifierTool*>(m_predefinedTools[MagnifierTool]);
}
void PDFToolManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
event->ignore();
@ -802,5 +808,99 @@ void PDFToolManager::onToolActionTriggered(bool checked)
}
}
PDFMagnifierTool::PDFMagnifierTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) :
BaseClass(proxy, action, parent),
m_magnifierSize(200),
m_magnifierZoom(2.0)
{
setCursor(Qt::BlankCursor);
}
void PDFMagnifierTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
}
void PDFMagnifierTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
}
void PDFMagnifierTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
QPoint mousePos = event->pos();
if (m_mousePos != mousePos)
{
m_mousePos = mousePos;
getProxy()->repaintNeeded();
}
}
void PDFMagnifierTool::drawPostRendering(QPainter* painter, QRect rect) const
{
if (!m_mousePos.isNull())
{
QPainterPath path;
path.addEllipse(m_mousePos, m_magnifierSize, m_magnifierSize);
painter->save();
// Clip the painter path for magnifier
painter->setClipPath(path, Qt::IntersectClip);
painter->fillRect(rect, getProxy()->getPaperColor());
painter->scale(m_magnifierZoom, m_magnifierZoom);
// Jakub Melka: this must be explained. We want to display the origin (mouse position)
// at the same position to remain under scaling. If scale == 1, then we translate
// by -m_mousePos + m_mousePos = (0, 0). Otherwise we are m_mousePos / scale away
// from the original position. Example:
// m_mousePos = (100, 100), scale = 2
// we are translating by -(100, 100) + (50, 50) = -(50, 50),
// because origin at (100, 100) is now at position (50, 50) after scale. So, if it has to remain
// the same, we must translate by -(50, 50).
painter->translate(m_mousePos * (1.0 / m_magnifierZoom - 1.0));
getProxy()->drawPages(painter, rect);
painter->restore();
painter->setPen(Qt::black);
painter->setBrush(Qt::NoBrush);
painter->drawPath(path);
}
}
void PDFMagnifierTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (!active)
{
m_mousePos = QPoint();
}
}
PDFReal PDFMagnifierTool::getMagnifierZoom() const
{
return m_magnifierZoom;
}
void PDFMagnifierTool::setMagnifierZoom(const PDFReal& magnifierZoom)
{
m_magnifierZoom = magnifierZoom;
}
int PDFMagnifierTool::getMagnifierSize() const
{
return m_magnifierSize;
}
void PDFMagnifierTool::setMagnifierSize(int magnifierSize)
{
m_magnifierSize = magnifierSize;
}
} // namespace pdf

View File

@ -236,6 +236,41 @@ private:
bool m_isCursorOverText;
};
/// Tool to magnify specific area in the drawing widget
class PDFFORQTLIBSHARED_EXPORT PDFMagnifierTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Constructs new magnifier tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param parent Parent object
explicit PDFMagnifierTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
int getMagnifierSize() const;
void setMagnifierSize(int magnifierSize);
PDFReal getMagnifierZoom() const;
void setMagnifierZoom(const PDFReal& magnifierZoom);
protected:
virtual void setActiveImpl(bool active) override;
private:
QPoint m_mousePos;
int m_magnifierSize;
PDFReal m_magnifierZoom;
};
/// Manager used for managing tools, their activity, availability
/// and other settings. It also defines a predefined set of tools,
/// available for various purposes (text searching, magnifier tool etc.)
@ -255,6 +290,7 @@ public:
QAction* selectAllAction = nullptr;
QAction* deselectAction = nullptr;
QAction* copyTextAction = nullptr;
QAction* magnifierAction = nullptr;
};
/// Construct new text search tool
@ -272,6 +308,7 @@ public:
{
FindTextTool,
SelectTextTool,
MagnifierTool,
ToolEnd
};
@ -285,6 +322,9 @@ public:
/// Returns find text tool
PDFFindTextTool* getFindTextTool() const;
/// Returns magnifier tool
PDFMagnifierTool* getMagnifierTool() const;
/// Handles key press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event

View File

@ -40,5 +40,6 @@
<file>resources/pause.svg</file>
<file>resources/play.svg</file>
<file>resources/stop.svg</file>
<file>resources/magnifier.svg</file>
</qresource>
</RCC>

View File

@ -189,6 +189,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
// Tools
ui->mainToolBar->addAction(ui->actionSelectText);
ui->mainToolBar->addAction(ui->actionMagnifier);
connect(ui->actionZoom_In, &QAction::triggered, this, [this] { m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::ZoomIn); });
connect(ui->actionZoom_Out, &QAction::triggered, this, [this] { m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::ZoomOut); });
@ -251,8 +252,10 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
actions.selectAllAction = ui->actionSelectTextAll;
actions.deselectAction = ui->actionDeselectText;
actions.copyTextAction = ui->actionCopyText;
actions.magnifierAction = ui->actionMagnifier;
m_toolManager = new pdf::PDFToolManager(m_pdfWidget->getDrawWidgetProxy(), actions, this, this);
m_pdfWidget->setToolManager(m_toolManager);
updateMagnifierToolSettings();
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFViewerMainWindow::onDrawSpaceChanged);
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::onPageLayoutChanged);
@ -1123,6 +1126,13 @@ void PDFViewerMainWindow::on_actionRendering_Errors_triggered()
renderingErrorsDialog.exec();
}
void PDFViewerMainWindow::updateMagnifierToolSettings()
{
pdf::PDFMagnifierTool* magnifierTool = m_toolManager->getMagnifierTool();
magnifierTool->setMagnifierSize(PDFWidgetUtils::scaleDPI_x(this, m_settings->getSettings().m_magnifierSize));
magnifierTool->setMagnifierZoom(m_settings->getSettings().m_magnifierZoom);
}
void PDFViewerMainWindow::on_actionOptions_triggered()
{
PDFViewerSettingsDialog::OtherSettings otherSettings;
@ -1136,6 +1146,7 @@ void PDFViewerMainWindow::on_actionOptions_triggered()
m_CMSManager->setSettings(m_settings->getColorManagementSystemSettings());
m_recentFileManager->setRecentFilesLimit(dialog.getOtherSettings().maximumRecentFileCount);
m_textToSpeech->setSettings(m_settings);
updateMagnifierToolSettings();
}
}

View File

@ -127,6 +127,7 @@ private:
void updateRenderingOptionActions();
void updateUI(bool fullUpdate);
void updateActionsAvailability();
void updateMagnifierToolSettings();
void onViewerSettingsChanged();
void onRenderingOptionTriggered(bool checked);

View File

@ -86,6 +86,8 @@
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionMagnifier"/>
<addaction name="separator"/>
<addaction name="actionRendering_Errors"/>
<addaction name="separator"/>
<addaction name="actionOptions"/>
@ -456,6 +458,21 @@
<string>Render to Images</string>
</property>
</action>
<action name="actionMagnifier">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="pdfforqtviewer.qrc">
<normaloff>:/resources/magnifier.svg</normaloff>:/resources/magnifier.svg</iconset>
</property>
<property name="text">
<string>Magnifier</string>
</property>
<property name="toolTip">
<string>Magnifier Tool</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -54,6 +54,8 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_allowLaunchApplications = settings.value("allowLaunchApplications", defaultSettings.m_allowLaunchApplications).toBool();
m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool();
m_settings.m_multithreadingStrategy = static_cast<pdf::PDFExecutionPolicy::Strategy>(settings.value("mutlithreadingStrategy", static_cast<int>(defaultSettings.m_multithreadingStrategy)).toInt());
m_settings.m_magnifierSize = settings.value("magnifierSize", defaultSettings.m_magnifierSize).toInt();
m_settings.m_magnifierZoom = settings.value("magnifierZoom", defaultSettings.m_magnifierZoom).toDouble();
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -100,6 +102,8 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications);
settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI);
settings.setValue("mutlithreadingStrategy", static_cast<int>(m_settings.m_multithreadingStrategy));
settings.setValue("magnifierSize", m_settings.m_magnifierSize);
settings.setValue("magnifierZoom", m_settings.m_magnifierZoom);
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -226,7 +230,9 @@ PDFViewerSettings::Settings::Settings() :
m_multithreadingStrategy(pdf::PDFExecutionPolicy::Strategy::PageMultithreaded),
m_speechRate(0.0),
m_speechPitch(0.0),
m_speechVolume(1.0)
m_speechVolume(1.0),
m_magnifierSize(100),
m_magnifierZoom(2.0)
{
}

View File

@ -71,6 +71,10 @@ public:
double m_speechRate;
double m_speechPitch;
double m_speechVolume;
// Magnifier tool settings
int m_magnifierSize;
double m_magnifierZoom;
};
const Settings& getSettings() const { return m_settings; }

View File

@ -256,6 +256,8 @@ void PDFViewerSettingsDialog::loadData()
// UI
ui->maximumRecentFileCountEdit->setValue(m_otherSettings.maximumRecentFileCount);
ui->magnifierSizeEdit->setValue(m_settings.m_magnifierSize);
ui->magnifierZoomEdit->setValue(m_settings.m_magnifierZoom);
// CMS
ui->cmsTypeComboBox->setCurrentIndex(ui->cmsTypeComboBox->findData(int(m_cmsSettings.system)));
@ -472,6 +474,14 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_speechVolume = ui->speechVolumeEdit->value();
}
else if (sender == ui->magnifierSizeEdit)
{
m_settings.m_magnifierSize = ui->magnifierSizeEdit->value();
}
else if (sender == ui->magnifierZoomEdit)
{
m_settings.m_magnifierZoom = ui->magnifierZoomEdit->value();
}
const bool loadData = !qobject_cast<const QDoubleSpinBox*>(sender) && !qobject_cast<const QSpinBox*>(sender);
if (loadData)

View File

@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>8</number>
<number>7</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@ -847,6 +847,13 @@
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="uiGroupBoxLayout">
<item row="1" column="0">
<widget class="QLabel" name="magnifierSizeLabel">
<property name="text">
<string>Magnifier size</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="maximumRecentFileCountLabel">
<property name="text">
@ -857,12 +864,42 @@
<item row="0" column="1">
<widget class="QSpinBox" name="maximumRecentFileCountEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="magnifierZoomLabel">
<property name="text">
<string>Magnifier zoom</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="magnifierSizeEdit">
<property name="suffix">
<string>px</string>
</property>
<property name="minimum">
<number>50</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="magnifierZoomEdit">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>10.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="uiInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum count of recent files controls, how much of recent files will be displayed in menu. When document is opened, then document is addet to the top of recent file list, and recent file list is truncated from bottom, if number of recent files is greater, than maximum.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum count of recent files controls, how much of recent files will be displayed in menu. When document is opened, then document is addet to the top of recent file list, and recent file list is truncated from bottom, if number of recent files is greater, than maximum.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Magnifier tool settings &lt;/span&gt;set the appearance of the magnifier. Magnifier tool magnifies area under the mouse cursor. You can specify how big the magnifier will be (in pixels) and how much it will zoom.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30mm"
height="30mm"
viewBox="0 0 30 30"
version="1.1"
id="svg5291"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="magnifier.svg">
<defs
id="defs5285">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 15 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="30 : 15 : 1"
inkscape:persp3d-origin="15 : 10 : 1"
id="perspective5921" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="96.866908"
inkscape:cy="75.073314"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2035"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1" />
<metadata
id="metadata5288">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Jakub Melka</dc:title>
</cc:Agent>
</dc:creator>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-267)">
<flowRoot
xml:space="preserve"
id="flowRoot5913"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
id="flowRegion5915"><rect
id="rect5917"
width="129.22377"
height="91.747108"
x="-13.788582"
y="-33.515606" /></flowRegion><flowPara
id="flowPara5919" /></flowRoot> <ellipse
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5985"
cx="10.996745"
cy="278.08231"
rx="6.4988279"
ry="6.4161458" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 15.64349,283.14245 6.482291,7.27604"
id="path5987"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB