Finishing of outline items

This commit is contained in:
Jakub Melka
2019-11-30 16:26:32 +01:00
parent 39059c645e
commit 5954b7f409
21 changed files with 702 additions and 21 deletions

View File

@@ -17,5 +17,6 @@
<file>resources/zoom-in.svg</file>
<file>resources/zoom-out.svg</file>
<file>resources/bookmark.svg</file>
<file>resources/security.svg</file>
</qresource>
</RCC>

View File

@@ -47,6 +47,11 @@
#include <QSpinBox>
#include <QLabel>
#include <QDoubleSpinBox>
#include <QDesktopServices>
#ifdef Q_OS_WIN
#include "Windows.h"
#endif
namespace pdfviewer
{
@@ -251,7 +256,216 @@ void PDFViewerMainWindow::onPageZoomSpinboxEditingFinished()
void PDFViewerMainWindow::onActionTriggered(const pdf::PDFAction* action)
{
Q_ASSERT(action);
for (const pdf::PDFAction* currentAction : action->getActionList())
{
switch (action->getType())
{
case pdf::ActionType::GoTo:
{
const pdf::PDFActionGoTo* typedAction = dynamic_cast<const pdf::PDFActionGoTo*>(currentAction);
pdf::PDFDestination destination = typedAction->getDestination();
if (destination.getDestinationType() == pdf::DestinationType::Named)
{
if (const pdf::PDFDestination* targetDestination = m_pdfDocument->getCatalog()->getDestination(destination.getName()))
{
destination = *targetDestination;
}
else
{
destination = pdf::PDFDestination();
QMessageBox::critical(this, tr("Go to action"), tr("Failed to go to destination '%1'. Destination wasn't found.").arg(pdf::PDFEncoding::convertTextString(destination.getName())));
}
}
if (destination.getDestinationType() != pdf::DestinationType::Invalid &&
destination.getPageReference() != pdf::PDFObjectReference())
{
const size_t pageIndex = m_pdfDocument->getCatalog()->getPageIndexFromPageReference(destination.getPageReference());
if (pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX)
{
m_pdfWidget->getDrawWidgetProxy()->goToPage(pageIndex);
}
}
break;
}
case pdf::ActionType::Launch:
{
if (!m_settings->getSettings().m_allowLaunchApplications)
{
// Launching of applications is disabled -> continue to next action
continue;
}
const pdf::PDFActionLaunch* typedAction = dynamic_cast<const pdf::PDFActionLaunch*>(currentAction);
#ifdef Q_OS_WIN
const pdf::PDFActionLaunch::Win& winSpecification = typedAction->getWinSpecification();
if (!winSpecification.file.isEmpty())
{
QString message = tr("Would you like to launch application '%1' in working directory '%2' with parameters '%3'?").arg(QString::fromLatin1(winSpecification.file), QString::fromLatin1(winSpecification.directory), QString::fromLatin1(winSpecification.parameters));
if (QMessageBox::question(this, tr("Launch application"), message) == QMessageBox::Yes)
{
auto getStringOrNULL = [](const QByteArray& array) -> LPCSTR
{
if (!array.isEmpty())
{
return array.data();
}
return NULL;
};
const HINSTANCE result = ::ShellExecuteA(NULL, getStringOrNULL(winSpecification.operation), getStringOrNULL(winSpecification.file), getStringOrNULL(winSpecification.parameters), getStringOrNULL(winSpecification.directory), SW_SHOWNORMAL);
if (result <= HINSTANCE(32))
{
// Error occured
QMessageBox::warning(this, tr("Launch application"), tr("Executing application failed. Error code is %1.").arg(reinterpret_cast<intptr_t>(result)));
}
}
// Continue next action
continue;
}
const pdf::PDFFileSpecification& fileSpecification = typedAction->getFileSpecification();
QString plaftormFileName = fileSpecification.getPlatformFileName();
if (!plaftormFileName.isEmpty())
{
QString message = tr("Would you like to launch application '%1'?").arg(plaftormFileName);
if (QMessageBox::question(this, tr("Launch application"), message) == QMessageBox::Yes)
{
const HINSTANCE result = ::ShellExecuteW(NULL, NULL, plaftormFileName.toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
if (result <= HINSTANCE(32))
{
// Error occured
QMessageBox::warning(this, tr("Launch application"), tr("Executing application failed. Error code is %1.").arg(reinterpret_cast<intptr_t>(result)));
}
}
// Continue next action
continue;
}
#endif
break;
}
case pdf::ActionType::URI:
{
if (!m_settings->getSettings().m_allowLaunchURI)
{
// Launching of URI is disabled -> continue to next action
continue;
}
const pdf::PDFActionURI* typedAction = dynamic_cast<const pdf::PDFActionURI*>(currentAction);
QByteArray URI = m_pdfDocument->getCatalog()->getBaseURI() + typedAction->getURI();
QString urlString = QString::fromLatin1(URI);
QString message = tr("Would you like to open URL '%1'?").arg(urlString);
if (QMessageBox::question(this, tr("Open URL"), message) == QMessageBox::Yes)
{
if (!QDesktopServices::openUrl(QUrl(urlString)))
{
// Error occured
QMessageBox::warning(this, tr("Open URL"), tr("Opening url '%1' failed.").arg(urlString));
}
}
break;
}
case pdf::ActionType::Named:
{
const pdf::PDFActionNamed* typedAction = dynamic_cast<const pdf::PDFActionNamed*>(currentAction);
switch (typedAction->getNamedActionType())
{
case pdf::PDFActionNamed::NamedActionType::NextPage:
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateNextPage);
break;
case pdf::PDFActionNamed::NamedActionType::PrevPage:
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigatePreviousPage);
break;
case pdf::PDFActionNamed::NamedActionType::FirstPage:
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateDocumentStart);
break;
case pdf::PDFActionNamed::NamedActionType::LastPage:
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateDocumentEnd);
break;
default:
break;
}
break;
}
case pdf::ActionType::SetOCGState:
{
const pdf::PDFActionSetOCGState* typedAction = dynamic_cast<const pdf::PDFActionSetOCGState*>(currentAction);
const pdf::PDFActionSetOCGState::StateChangeItems& stateChanges = typedAction->getStateChangeItems();
const bool isRadioButtonsPreserved = typedAction->isRadioButtonsPreserved();
if (m_optionalContentActivity)
{
for (const pdf::PDFActionSetOCGState::StateChangeItem& stateChange : stateChanges)
{
pdf::OCState newState = pdf::OCState::Unknown;
switch (stateChange.first)
{
case pdf::PDFActionSetOCGState::SwitchType::ON:
newState = pdf::OCState::ON;
break;
case pdf::PDFActionSetOCGState::SwitchType::OFF:
newState = pdf::OCState::OFF;
break;
case pdf::PDFActionSetOCGState::SwitchType::Toggle:
{
pdf::OCState oldState = m_optionalContentActivity->getState(stateChange.second);
switch (oldState)
{
case pdf::OCState::ON:
newState = pdf::OCState::OFF;
break;
case pdf::OCState::OFF:
newState = pdf::OCState::ON;
break;
case pdf::OCState::Unknown:
break;
default:
Q_ASSERT(false);
break;
}
break;
}
default:
Q_ASSERT(false);
}
if (newState != pdf::OCState::Unknown)
{
m_optionalContentActivity->setState(stateChange.second, newState, isRadioButtonsPreserved);
}
}
}
break;
}
default:
break;
}
}
}
void PDFViewerMainWindow::onProgressStarted()

View File

@@ -22,6 +22,8 @@ void PDFViewerSettings::readSettings(QSettings& settings)
m_settings.m_preferredMeshResolutionRatio = settings.value("preferredMeshResolutionRatio", defaultSettings.m_preferredMeshResolutionRatio).toDouble();
m_settings.m_minimalMeshResolutionRatio = settings.value("minimalMeshResolutionRatio", defaultSettings.m_minimalMeshResolutionRatio).toDouble();
m_settings.m_colorTolerance = settings.value("colorTolerance", defaultSettings.m_colorTolerance).toDouble();
m_settings.m_allowLaunchApplications = settings.value("allowLaunchApplications", defaultSettings.m_allowLaunchApplications).toBool();
m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool();
settings.endGroup();
emit settingsChanged();
@@ -38,6 +40,8 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.setValue("preferredMeshResolutionRatio", m_settings.m_preferredMeshResolutionRatio);
settings.setValue("minimalMeshResolutionRatio", m_settings.m_minimalMeshResolutionRatio);
settings.setValue("colorTolerance", m_settings.m_colorTolerance);
settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications);
settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI);
settings.endGroup();
}

View File

@@ -28,7 +28,9 @@ public:
m_rendererSamples(16),
m_preferredMeshResolutionRatio(0.02),
m_minimalMeshResolutionRatio(0.005),
m_colorTolerance(0.01)
m_colorTolerance(0.01),
m_allowLaunchApplications(true),
m_allowLaunchURI(true)
{
}
@@ -41,6 +43,8 @@ public:
pdf::PDFReal m_preferredMeshResolutionRatio;
pdf::PDFReal m_minimalMeshResolutionRatio;
pdf::PDFReal m_colorTolerance;
bool m_allowLaunchApplications;
bool m_allowLaunchURI;
};
const Settings& getSettings() const { return m_settings; }

View File

@@ -21,6 +21,7 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
new QListWidgetItem(QIcon(":/resources/engine.svg"), tr("Engine"), ui->optionsPagesWidget, EngineSettings);
new QListWidgetItem(QIcon(":/resources/rendering.svg"), tr("Rendering"), ui->optionsPagesWidget, RenderingSettings);
new QListWidgetItem(QIcon(":/resources/shading.svg"), tr("Shading"), ui->optionsPagesWidget, ShadingSettings);
new QListWidgetItem(QIcon(":/resources/security.svg"), tr("Security"), ui->optionsPagesWidget, SecuritySettings);
ui->renderingEngineComboBox->addItem(tr("Software"), static_cast<int>(pdf::RendererEngine::Software));
ui->renderingEngineComboBox->addItem(tr("Hardware accelerated (OpenGL)"), static_cast<int>(pdf::RendererEngine::OpenGL));
@@ -30,20 +31,20 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i);
}
for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel })
for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel, ui->securityInfoLabel })
{
widget->setMinimumWidth(widget->sizeHint().width());
}
for (QCheckBox* checkBox : { ui->multisampleAntialiasingCheckBox, ui->antialiasingCheckBox, ui->textAntialiasingCheckBox, ui->smoothPicturesCheckBox, ui->ignoreOptionalContentCheckBox, ui->clipToCropBoxCheckBox })
for (QCheckBox* checkBox : findChildren<QCheckBox*>())
{
connect(checkBox, &QCheckBox::clicked, this, &PDFViewerSettingsDialog::saveData);
}
for (QComboBox* comboBox : { ui->renderingEngineComboBox, ui->multisampleAntialiasingSamplesCountComboBox })
for (QComboBox* comboBox : findChildren<QComboBox*>())
{
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFViewerSettingsDialog::saveData);
}
for (QDoubleSpinBox* spinBox : { ui->preferredMeshResolutionEdit, ui->minimalMeshResolutionEdit, ui->colorToleranceEdit })
for (QDoubleSpinBox* spinBox : findChildren<QDoubleSpinBox*>())
{
connect(spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFViewerSettingsDialog::saveData);
}
@@ -76,6 +77,10 @@ void PDFViewerSettingsDialog::on_optionsPagesWidget_currentItemChanged(QListWidg
ui->stackedWidget->setCurrentWidget(ui->shadingPage);
break;
case SecuritySettings:
ui->stackedWidget->setCurrentWidget(ui->securityPage);
break;
default:
Q_ASSERT(false);
break;
@@ -123,6 +128,10 @@ void PDFViewerSettingsDialog::loadData()
ui->preferredMeshResolutionEdit->setValue(m_settings.m_preferredMeshResolutionRatio);
ui->minimalMeshResolutionEdit->setValue(m_settings.m_minimalMeshResolutionRatio);
ui->colorToleranceEdit->setValue(m_settings.m_colorTolerance);
// Security
ui->allowLaunchCheckBox->setChecked(m_settings.m_allowLaunchApplications);
ui->allowRunURICheckBox->setChecked(m_settings.m_allowLaunchURI);
}
void PDFViewerSettingsDialog::saveData()
@@ -178,6 +187,14 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_colorTolerance = ui->colorToleranceEdit->value();
}
else if (sender == ui->allowLaunchCheckBox)
{
m_settings.m_allowLaunchApplications = ui->allowLaunchCheckBox->isChecked();
}
else if (sender == ui->allowRunURICheckBox)
{
m_settings.m_allowLaunchURI = ui->allowRunURICheckBox->isChecked();
}
loadData();
}

View File

@@ -27,7 +27,8 @@ public:
{
EngineSettings,
RenderingSettings,
ShadingSettings
ShadingSettings,
SecuritySettings
};
const PDFViewerSettings::Settings& getSettings() const { return m_settings; }

View File

@@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
<number>3</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@@ -366,6 +366,86 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="securityPage">
<layout class="QVBoxLayout" name="securityPageLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="securitySettingsGroupBox">
<property name="title">
<string>Security Settings</string>
</property>
<layout class="QVBoxLayout" name="securitySettingsGroupBoxLayout">
<item>
<layout class="QGridLayout" name="securitySettingsGridLayout">
<item row="0" column="1">
<widget class="QCheckBox" name="allowLaunchCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="allowRunURILabel">
<property name="text">
<string>Allow URI link launch</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="allowRunExeLabel">
<property name="text">
<string>Allow application launch</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="allowRunURICheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="securityInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If launch of application is allowed, then, after &lt;span style=&quot; font-weight:600;&quot;&gt;Launch&lt;/span&gt; action is triggered, message box appears, which asks the user, if he wants to execute external application. If user confirms the dialog, then external application is executed (for example, by windows function &lt;span style=&quot; font-weight:600;&quot;&gt;ShellExecute&lt;/span&gt;). When this option is turned off, then no dialog appears and external application is never executed.&lt;/p&gt;&lt;p&gt;If &lt;span style=&quot; font-weight:600;&quot;&gt;URI link &lt;/span&gt;launch is allowed, then, again, message box appears and asks the user, if he wants to open the URI link. If user confirms the dialog, then URI link is opened by standard web browser (or standard application, if URI links to the file system).&lt;/p&gt;&lt;p&gt;User should be really carefull with these settings, and only confirm the execution, if the document is safe, i.e. it comes from reliable source. Launching external applications and following URI links can be harmfull to the user's computer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="securityPageSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>74</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@@ -0,0 +1,122 @@
<?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="security.svg">
<defs
id="defs5285">
<inkscape:path-effect
effect="bspline"
id="path-effect831"
is_visible="true"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-887.45193"
inkscape:cy="248.19877"
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)">
<rect
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect827"
width="22.395088"
height="12.425969"
x="4.2049851"
y="280.18005"
ry="1.3063761"
rx="0.80319941" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 8.8351933,280.08557 c -0.015751,-1.9059 -0.0315,-3.81153 0.2362696,-5.37063 0.2677698,-1.55909 0.8190596,-2.77193 2.8738961,-3.37025 2.054836,-0.59831 5.615048,-0.58257 7.670115,0.0158 2.055068,0.59836 2.606322,1.77962 2.889831,3.29942 0.283509,1.51979 0.299258,3.37799 0.31501,5.23664"
id="path829"
inkscape:connector-curvature="0"
inkscape:path-effect="#path-effect831"
inkscape:original-d="m 8.8351933,280.08557 c -0.015483,-1.9059 -0.031233,-3.81153 -0.047247,-5.71689 0.5514038,-1.21278 1.1026949,-2.42562 1.6536457,-3.63803 3.558604,0.0155 7.118816,0.0312 10.677827,0.0472 0.55144,1.18083 1.102695,2.36209 1.653646,3.54353 0.01602,1.8583 0.03176,3.7165 0.04725,5.57515" />
<ellipse
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path839"
cx="15.56845"
cy="285.39047"
rx="2.5390604"
ry="2.4555387" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect845"
width="2.5513394"
height="4.9136906"
x="14.315848"
y="286.88913"
rx="0.80319828"
ry="1.1575521" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB