mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Copy Annotation onto Multiple Pages
This commit is contained in:
@@ -76,6 +76,7 @@ SOURCES += \
|
||||
sources/pdfprogress.cpp \
|
||||
sources/pdfredact.cpp \
|
||||
sources/pdfsecurityhandler.cpp \
|
||||
sources/pdfselectpagesdialog.cpp \
|
||||
sources/pdfsignaturehandler.cpp \
|
||||
sources/pdfsnapper.cpp \
|
||||
sources/pdfstructuretree.cpp \
|
||||
@@ -143,6 +144,7 @@ HEADERS += \
|
||||
sources/pdfprogress.h \
|
||||
sources/pdfredact.h \
|
||||
sources/pdfsecurityhandler.h \
|
||||
sources/pdfselectpagesdialog.h \
|
||||
sources/pdfsignaturehandler.h \
|
||||
sources/pdfsignaturehandler_impl.h \
|
||||
sources/pdfsnapper.h \
|
||||
@@ -174,7 +176,8 @@ HEADERS += \
|
||||
sources/pdfimage.h
|
||||
|
||||
FORMS += \
|
||||
sources/pdfrenderingerrorswidget.ui
|
||||
sources/pdfrenderingerrorswidget.ui \
|
||||
sources/pdfselectpagesdialog.ui
|
||||
|
||||
RESOURCES += cmaps.qrc
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "pdfpainterutils.h"
|
||||
#include "pdfdocumentbuilder.h"
|
||||
#include "pdfobjecteditorwidget.h"
|
||||
#include "pdfselectpagesdialog.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QDialog>
|
||||
@@ -2010,7 +2011,42 @@ void PDFWidgetAnnotationManager::onShowPopupAnnotation()
|
||||
|
||||
void PDFWidgetAnnotationManager::onCopyAnnotation()
|
||||
{
|
||||
pdf::PDFSelectPagesDialog dialog(tr("Copy Annotation"), tr("Copy Annotation onto Multiple Pages"),
|
||||
m_document->getCatalog()->getPageCount(), m_proxy->getWidget()->getDrawWidget()->getCurrentPages(), m_proxy->getWidget());
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
std::vector<PDFInteger> pages = dialog.getSelectedPages();
|
||||
const PDFInteger currentPageIndex = m_document->getCatalog()->getPageIndexFromPageReference(m_editableAnnotationPage);
|
||||
|
||||
for (PDFInteger& pageIndex : pages)
|
||||
{
|
||||
--pageIndex;
|
||||
}
|
||||
|
||||
auto it = std::find(pages.begin(), pages.end(), currentPageIndex);
|
||||
if (it != pages.end())
|
||||
{
|
||||
pages.erase(it);
|
||||
}
|
||||
|
||||
if (pages.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PDFDocumentModifier modifier(m_document);
|
||||
modifier.markAnnotationsChanged();
|
||||
|
||||
for (const PDFInteger pageIndex : pages)
|
||||
{
|
||||
modifier.getBuilder()->copyAnnotation(m_document->getCatalog()->getPage(pageIndex)->getPageReference(), m_editableAnnotation);
|
||||
}
|
||||
|
||||
if (modifier.finalize())
|
||||
{
|
||||
emit documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetAnnotationManager::onEditAnnotation()
|
||||
|
@@ -1111,6 +1111,36 @@ PDFObjectReference PDFDocumentBuilder::getDocumentInfo() const
|
||||
return PDFObjectReference();
|
||||
}
|
||||
|
||||
void PDFDocumentBuilder::copyAnnotation(PDFObjectReference pageReference, PDFObjectReference annotationReference)
|
||||
{
|
||||
PDFObjectReference copiedAnnotation = addObject(getObjectByReference(annotationReference));
|
||||
|
||||
PDFObjectFactory factory;
|
||||
factory.beginDictionary();
|
||||
factory.beginDictionaryItem("P");
|
||||
factory << pageReference;
|
||||
factory.endDictionaryItem();
|
||||
factory.beginDictionaryItem("Popup");
|
||||
factory << PDFObject();
|
||||
factory.endDictionaryItem();
|
||||
factory.beginDictionaryItem("IRT");
|
||||
factory << PDFObject();
|
||||
factory.endDictionaryItem();
|
||||
factory.endDictionary();
|
||||
|
||||
mergeTo(copiedAnnotation, factory.takeObject());
|
||||
|
||||
factory.beginDictionary();
|
||||
factory.beginDictionaryItem("Annots");
|
||||
factory.beginArray();
|
||||
factory << copiedAnnotation;
|
||||
factory.endArray();
|
||||
factory.endDictionaryItem();
|
||||
factory.endDictionary();
|
||||
PDFObject pageAnnots = factory.takeObject();
|
||||
appendTo(pageReference, pageAnnots);
|
||||
}
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::getCatalogReference() const
|
||||
{
|
||||
if (const PDFDictionary* trailerDictionary = getDictionaryFromObject(m_storage.getTrailerDictionary()))
|
||||
|
@@ -416,6 +416,11 @@ public:
|
||||
/// Returns document info reference
|
||||
PDFObjectReference getDocumentInfo() const;
|
||||
|
||||
/// Copies existing annotation to another page
|
||||
/// \param pageReference Page reference (onto which is annotation copied)
|
||||
/// \param annotationReference Annotation reference
|
||||
void copyAnnotation(PDFObjectReference pageReference, PDFObjectReference annotationReference);
|
||||
|
||||
/* START GENERATED CODE */
|
||||
|
||||
/// Appends a new page after last page.
|
||||
|
@@ -78,9 +78,7 @@ PDFDocument PDFRedact::perform(Options options)
|
||||
}
|
||||
builder.setPageRotation(newPageReference, page->getPageRotation());
|
||||
|
||||
// TODO: Popisek redakce anotace, Overlay text
|
||||
// TODO: Redact searched text
|
||||
// TODO: Duplikace redakce na vice stranek
|
||||
|
||||
PDFPageContentStreamBuilder contentStreamBuilder(&builder);
|
||||
|
||||
@@ -131,7 +129,12 @@ PDFDocument PDFRedact::perform(Options options)
|
||||
|
||||
if (options.testFlag(CopyOutline))
|
||||
{
|
||||
builder.setOutline(m_document->getCatalog()->getOutlineRootPtr().data());
|
||||
const PDFOutlineItem* outlineItem = m_document->getCatalog()->getOutlineRootPtr().data();
|
||||
|
||||
if (outlineItem)
|
||||
{
|
||||
builder.setOutline(outlineItem);
|
||||
}
|
||||
}
|
||||
|
||||
PDFDocument redactedDocument = builder.build();
|
||||
@@ -141,5 +144,4 @@ PDFDocument PDFRedact::perform(Options options)
|
||||
return optimizer.takeOptimizedDocument();
|
||||
}
|
||||
|
||||
|
||||
} // namespace pdf
|
||||
|
135
Pdf4QtLib/sources/pdfselectpagesdialog.cpp
Normal file
135
Pdf4QtLib/sources/pdfselectpagesdialog.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2020 Jakub Melka
|
||||
//
|
||||
// This file is part of Pdf4Qt.
|
||||
//
|
||||
// Pdf4Qt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pdf4Qt 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with Pdf4Qt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfselectpagesdialog.h"
|
||||
#include "ui_pdfselectpagesdialog.h"
|
||||
|
||||
#include "pdfwidgetutils.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFSelectPagesDialog::PDFSelectPagesDialog(QString windowTitle,
|
||||
QString groupBoxTitle,
|
||||
pdf::PDFInteger pageCount,
|
||||
const std::vector<pdf::PDFInteger>& visiblePages,
|
||||
QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PDFSelectPagesDialog),
|
||||
m_pageCount(pageCount),
|
||||
m_visiblePages(visiblePages)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowTitle(windowTitle);
|
||||
ui->selectPagesGroupBox->setTitle(groupBoxTitle);
|
||||
|
||||
for (pdf::PDFInteger& pageIndex : m_visiblePages)
|
||||
{
|
||||
++pageIndex;
|
||||
}
|
||||
|
||||
m_evenPages.reserve(pageCount / 2);
|
||||
m_oddPages.reserve(pageCount / 2 + 1);
|
||||
|
||||
for (pdf::PDFInteger i = 1; i <= pageCount; ++i)
|
||||
{
|
||||
if (i % 2 == 1)
|
||||
{
|
||||
m_oddPages.push_back(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_evenPages.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->customPageRangeRadioButton, &QRadioButton::toggled, this, &PDFSelectPagesDialog::updateUi);
|
||||
|
||||
setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, 400));
|
||||
updateUi();
|
||||
}
|
||||
|
||||
PDFSelectPagesDialog::~PDFSelectPagesDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
std::vector<pdf::PDFInteger> PDFSelectPagesDialog::getSelectedPages() const
|
||||
{
|
||||
std::vector<pdf::PDFInteger> result;
|
||||
|
||||
if (ui->visiblePagesRadioButton->isChecked())
|
||||
{
|
||||
result = m_visiblePages;
|
||||
}
|
||||
else if (ui->allPagesRadioButton->isChecked())
|
||||
{
|
||||
result.resize(m_pageCount, 0);
|
||||
std::iota(result.begin(), result.end(), 1);
|
||||
}
|
||||
else if (ui->evenPagesRadioButton->isChecked())
|
||||
{
|
||||
result = m_evenPages;
|
||||
}
|
||||
else if (ui->oddPagesRadioButton->isChecked())
|
||||
{
|
||||
result = m_oddPages;
|
||||
}
|
||||
else if (ui->customPageRangeRadioButton->isChecked())
|
||||
{
|
||||
QString errorMessage;
|
||||
result = pdf::PDFClosedIntervalSet::parse(1, m_pageCount, ui->customPageRangeEdit->text(), &errorMessage).unfold();
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFSelectPagesDialog::updateUi()
|
||||
{
|
||||
ui->customPageRangeEdit->setEnabled(ui->customPageRangeRadioButton->isChecked());
|
||||
}
|
||||
|
||||
void PDFSelectPagesDialog::accept()
|
||||
{
|
||||
if (ui->customPageRangeRadioButton->isChecked())
|
||||
{
|
||||
QString errorMessage;
|
||||
pdf::PDFClosedIntervalSet::parse(1, m_pageCount, ui->customPageRangeEdit->text(), &errorMessage);
|
||||
if (!errorMessage.isEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getSelectedPages().empty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Selected page range is empty."));
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
62
Pdf4QtLib/sources/pdfselectpagesdialog.h
Normal file
62
Pdf4QtLib/sources/pdfselectpagesdialog.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2020 Jakub Melka
|
||||
//
|
||||
// This file is part of Pdf4Qt.
|
||||
//
|
||||
// Pdf4Qt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pdf4Qt 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with Pdf4Qt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFSELECTPAGESDIALOG_H
|
||||
#define PDFSELECTPAGESDIALOG_H
|
||||
|
||||
#include "pdfutils.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PDFSelectPagesDialog;
|
||||
}
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
class Pdf4QtLIBSHARED_EXPORT PDFSelectPagesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFSelectPagesDialog(QString windowTitle,
|
||||
QString groupBoxTitle,
|
||||
pdf::PDFInteger pageCount,
|
||||
const std::vector<pdf::PDFInteger>& visiblePages,
|
||||
QWidget* parent);
|
||||
|
||||
virtual ~PDFSelectPagesDialog() override;
|
||||
|
||||
virtual void accept() override;
|
||||
|
||||
std::vector<pdf::PDFInteger> getSelectedPages() const;
|
||||
|
||||
private:
|
||||
void updateUi();
|
||||
|
||||
Ui::PDFSelectPagesDialog* ui;
|
||||
pdf::PDFInteger m_pageCount;
|
||||
std::vector<pdf::PDFInteger> m_visiblePages;
|
||||
std::vector<pdf::PDFInteger> m_evenPages;
|
||||
std::vector<pdf::PDFInteger> m_oddPages;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFSELECTPAGESDIALOG_H
|
121
Pdf4QtLib/sources/pdfselectpagesdialog.ui
Normal file
121
Pdf4QtLib/sources/pdfselectpagesdialog.ui
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PDFSelectPagesDialog</class>
|
||||
<widget class="QDialog" name="PDFSelectPagesDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>218</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="selectPagesGroupBox">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="allPagesRadioButton">
|
||||
<property name="text">
|
||||
<string>All pages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="customPageRangeEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="evenPagesRadioButton">
|
||||
<property name="text">
|
||||
<string>Even pages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="oddPagesRadioButton">
|
||||
<property name="text">
|
||||
<string>Odd pages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QRadioButton" name="customPageRangeRadioButton">
|
||||
<property name="text">
|
||||
<string>Custom page range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QRadioButton" name="visiblePagesRadioButton">
|
||||
<property name="text">
|
||||
<string>Visible pages</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PDFSelectPagesDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PDFSelectPagesDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Reference in New Issue
Block a user