// Copyright (C) 2024 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 // with the written consent of the copyright owner, 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 . #include "pdfpagecontenteditorediteditemsettings.h" #include "ui_pdfpagecontenteditorediteditemsettings.h" #include "pdfpagecontentelements.h" #include "pdfpagecontenteditorprocessor.h" #include "pdfwidgetutils.h" #include "pdfpainterutils.h" #include #include #include #include #include namespace pdf { PDFPageContentEditorEditedItemSettings::PDFPageContentEditorEditedItemSettings(QWidget* parent) : QWidget(parent), ui(new Ui::PDFPageContentEditorEditedItemSettings) { ui->setupUi(this); connect(ui->loadImageButton, &QPushButton::clicked, this, &PDFPageContentEditorEditedItemSettings::selectImage); for (const QString& colorName : QColor::colorNames()) { QColor color(colorName); QIcon icon = getIconForColor(color); ui->penColorCombo->addItem(icon, colorName, color); ui->brushColorCombo->addItem(icon, colorName, color); } ui->penStyleCombo->addItem(tr("Solid"), int(Qt::SolidLine)); ui->penStyleCombo->addItem(tr("Dashed"), int(Qt::DashLine)); ui->penStyleCombo->addItem(tr("Dotted"), int(Qt::DotLine)); ui->penStyleCombo->addItem(tr("Dash-dot"), int(Qt::DashDotLine)); ui->penStyleCombo->addItem(tr("Dash-dot-dot"), int(Qt::DashDotDotLine)); ui->penStyleCombo->addItem(tr("Custom"), int(Qt::CustomDashLine)); ui->brushStyleCombo->addItem(tr("Solid"), int(Qt::SolidPattern)); connect(ui->selectPenColorButton, &QToolButton::clicked, this, &PDFPageContentEditorEditedItemSettings::onSelectPenColorButtonClicked); connect(ui->selectBrushColorButton, &QToolButton::clicked, this, &PDFPageContentEditorEditedItemSettings::onSelectBrushColorButtonClicked); connect(ui->penWidthEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorEditedItemSettings::onPenWidthChanged); connect(ui->penStyleCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onPenStyleChanged); connect(ui->brushStyleCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onBrushStyleChanged); connect(ui->penColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorEditedItemSettings::onPenColorComboTextChanged); connect(ui->penColorCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onPenColorComboIndexChanged); connect(ui->brushColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorEditedItemSettings::onBrushColorComboTextChanged); connect(ui->brushColorCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onBrushColorComboIndexChanged); } QIcon PDFPageContentEditorEditedItemSettings::getIconForColor(QColor color) const { QIcon icon; QSize iconSize = PDFWidgetUtils::scaleDPI(this, QSize(16, 16)); QPixmap pixmap(iconSize.width(), iconSize.height()); pixmap.fill(color); icon.addPixmap(pixmap); return icon; } PDFPageContentEditorEditedItemSettings::~PDFPageContentEditorEditedItemSettings() { delete ui; } void PDFPageContentEditorEditedItemSettings::loadFromElement(PDFPageContentElementEdited* editedElement) { const PDFEditedPageContentElement* contentElement = editedElement->getElement(); ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->imageTab)); ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->textTab)); ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->styleTab)); ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->transformationTab)); if (const PDFEditedPageContentElementImage* imageElement = contentElement->asImage()) { ui->tabWidget->addTab(ui->imageTab, tr("Image")); m_image = imageElement->getImage(); setImage(imageElement->getImage()); } if (PDFEditedPageContentElementText* textElement = editedElement->getElement()->asText()) { ui->tabWidget->addTab(ui->textTab, tr("Text")); QString text = textElement->getItemsAsText(); ui->plainTextEdit->setPlainText(text); } if (editedElement->getElement()->asText() || editedElement->getElement()->asPath()) { ui->tabWidget->addTab(ui->styleTab, tr("Style")); } QTransform matrix = editedElement->getElement()->getTransform(); PDFTransformationDecomposition decomposedTransformation = PDFPainterHelper::decomposeTransform(matrix); ui->rotationAngleEdit->setValue(qRadiansToDegrees(decomposedTransformation.rotationAngle)); ui->scaleInXEdit->setValue(decomposedTransformation.scaleX); ui->scaleInYEdit->setValue(decomposedTransformation.scaleY); ui->shearFactorEdit->setValue(decomposedTransformation.shearFactor); ui->translateInXEdit->setValue(decomposedTransformation.translateX); ui->translateInYEdit->setValue(decomposedTransformation.translateY); ui->tabWidget->addTab(ui->transformationTab, tr("Transformation")); // Style const PDFEditedPageContentElement* element = editedElement->getElement(); StyleFeatures features = None; if (element->asPath()) { features.setFlag(Pen); features.setFlag(PenColor); features.setFlag(Brush); features.setFlag(StrokeFill); } if (element->asText()) { features.setFlag(PenColor); } const bool hasPen = features.testFlag(Pen); const bool hasPenColor = features.testFlag(PenColor); const bool hasBrush = features.testFlag(Brush); const bool hasStrokeFill = features.testFlag(StrokeFill); ui->penWidthEdit->setEnabled(hasPen); ui->penWidthLabel->setEnabled(hasPen); ui->penStyleCombo->setEnabled(hasPen); ui->penStyleLabel->setEnabled(hasPen); ui->penColorCombo->setEnabled(hasPenColor); ui->penColorLabel->setEnabled(hasPenColor); ui->selectPenColorButton->setEnabled(hasPenColor); ui->brushStyleLabel->setEnabled(hasBrush); ui->brushStyleCombo->setEnabled(hasBrush); ui->brushColorCombo->setEnabled(hasBrush); ui->brushColorLabel->setEnabled(hasBrush); ui->selectBrushColorButton->setEnabled(hasBrush); ui->strokePathCheckBox->setEnabled(hasStrokeFill); ui->fillPathCheckBox->setEnabled(hasStrokeFill); if (const PDFEditedPageContentElementPath* pathElement = element->asPath()) { ui->strokePathCheckBox->setChecked(pathElement->getStrokePath()); ui->fillPathCheckBox->setChecked(pathElement->getFillPath()); } const PDFPageContentProcessorState& graphicState = element->getState(); QPen pen = pdf::PDFPainterHelper::createPenFromState(&graphicState, graphicState.getAlphaStroking()); QBrush brush = pdf::PDFPainterHelper::createBrushFromState(&graphicState, graphicState.getAlphaFilling()); if (element->asText()) { pen.setColor(brush.color()); } setPen(pen, true); setBrush(brush, true); } void PDFPageContentEditorEditedItemSettings::setPen(const QPen& pen, bool forceUpdate) { if (m_pen != pen || forceUpdate) { m_pen = pen; ui->penWidthEdit->setValue(pen.widthF()); ui->penStyleCombo->setCurrentIndex(ui->penStyleCombo->findData(int(pen.style()))); setColorToComboBox(ui->penColorCombo, pen.color()); } } void PDFPageContentEditorEditedItemSettings::setBrush(const QBrush& brush, bool forceUpdate) { if (m_brush != brush || forceUpdate) { m_brush = brush; ui->brushStyleCombo->setCurrentIndex(ui->brushStyleCombo->findData(int(brush.style()))); setColorToComboBox(ui->brushColorCombo, brush.color()); } } void PDFPageContentEditorEditedItemSettings::setColorToComboBox(QComboBox* comboBox, QColor color) { if (!color.isValid()) { return; } QString name = color.name(QColor::HexArgb); int index = comboBox->findData(color, Qt::UserRole, Qt::MatchExactly); if (index == -1) { // Jakub Melka: try to find text (color name) index = comboBox->findText(name); } if (index != -1) { comboBox->setCurrentIndex(index); } else { comboBox->addItem(getIconForColor(color), name, color); comboBox->setCurrentIndex(comboBox->count() - 1); } } void PDFPageContentEditorEditedItemSettings::saveToElement(PDFPageContentElementEdited* editedElement) { if (PDFEditedPageContentElementImage* imageElement = editedElement->getElement()->asImage()) { imageElement->setImage(m_image); imageElement->setImageObject(PDFObject()); } if (PDFEditedPageContentElementText* textElement = editedElement->getElement()->asText()) { textElement->setItemsAsText(ui->plainTextEdit->toPlainText()); } if (PDFEditedPageContentElementPath* pathElement = editedElement->getElement()->asPath()) { pathElement->setStrokePath(ui->strokePathCheckBox->isChecked()); pathElement->setFillPath(ui->fillPathCheckBox->isChecked()); } PDFTransformationDecomposition decomposedTransformation; decomposedTransformation.rotationAngle = ui->rotationAngleEdit->value(); decomposedTransformation.shearFactor = ui->shearFactorEdit->value(); decomposedTransformation.scaleX = ui->scaleInXEdit->value(); decomposedTransformation.scaleY = ui->scaleInYEdit->value(); decomposedTransformation.translateX = ui->translateInXEdit->value(); decomposedTransformation.translateY = ui->translateInYEdit->value(); QTransform transform = PDFPainterHelper::composeTransform(decomposedTransformation); editedElement->getElement()->setTransform(transform); if (editedElement->getElement()->asPath()) { PDFPageContentProcessorState graphicState = editedElement->getElement()->getState(); PDFPainterHelper::applyPenToGraphicState(&graphicState, m_pen); PDFPainterHelper::applyBrushToGraphicState(&graphicState, m_brush); editedElement->getElement()->setState(graphicState); } if (editedElement->getElement()->asText()) { PDFPageContentProcessorState graphicState = editedElement->getElement()->getState(); QBrush brush(m_pen.color(), Qt::SolidPattern); PDFPainterHelper::applyBrushToGraphicState(&graphicState, brush); editedElement->getElement()->setState(graphicState); } } static int PDF_gcd(int a, int b) { if (b == 0) { return a; } return PDF_gcd(b, a % b); } void PDFPageContentEditorEditedItemSettings::setImage(QImage image) { QSize imageSize = QSize(200, 200); int width = image.width(); int height = image.height(); int n = width; int d = height; int divisor = PDF_gcd(n, d); if (divisor > 1) { n /= divisor; d /= divisor; } ui->imageWidthEdit->setText(QString("%1 px").arg(width)); ui->imageHeightEdit->setText(QString("%1 px").arg(height)); ui->imageRatioEdit->setText(QString("%1 : %2").arg(n).arg(d)); image.setDevicePixelRatio(this->devicePixelRatioF()); image = image.scaled(imageSize * this->devicePixelRatioF(), Qt::KeepAspectRatio); ui->imageLabel->setPixmap(QPixmap::fromImage(image)); ui->imageLabel->setFixedSize(PDFWidgetUtils::scaleDPI(this, imageSize)); } void PDFPageContentEditorEditedItemSettings::selectImage() { QString imageDirectory; QStringList pictureDirectiories = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!pictureDirectiories.isEmpty()) { imageDirectory = pictureDirectiories.last(); } else { imageDirectory = QDir::currentPath(); } QList mimeTypes = QImageReader::supportedMimeTypes(); QStringList mimeTypeFilters; for (const QByteArray& mimeType : mimeTypes) { mimeTypeFilters.append(mimeType); } QFileDialog dialog(this, tr("Select Image")); dialog.setDirectory(imageDirectory); dialog.setMimeTypeFilters(mimeTypeFilters); dialog.selectMimeTypeFilter("image/svg+xml"); dialog.setAcceptMode(QFileDialog::AcceptOpen); dialog.setFileMode(QFileDialog::ExistingFile); if (dialog.exec() == QFileDialog::Accepted) { QString fileName = dialog.selectedFiles().constFirst(); QImageReader reader(fileName); QImage image = reader.read(); if (!image.isNull()) { setImage(image); m_image = std::move(image); } } } void PDFPageContentEditorEditedItemSettings::setPenColor(QColor color) { if (color.isValid()) { m_pen.setColor(color); setColorToComboBox(ui->penColorCombo, color); } } void PDFPageContentEditorEditedItemSettings::onSelectPenColorButtonClicked() { QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Pen"), QColorDialog::ShowAlphaChannel); setPenColor(color); } void PDFPageContentEditorEditedItemSettings::setBrushColor(QColor color) { if (color.isValid() && m_brush.color() != color) { m_brush.setColor(color); setColorToComboBox(ui->brushColorCombo, color); } } const QBrush& PDFPageContentEditorEditedItemSettings::getBrush() const { return m_brush; } const QPen& PDFPageContentEditorEditedItemSettings::getPen() const { return m_pen; } void PDFPageContentEditorEditedItemSettings::onSelectBrushColorButtonClicked() { QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Brush"), QColorDialog::ShowAlphaChannel); setBrushColor(color); } void PDFPageContentEditorEditedItemSettings::onPenWidthChanged(double value) { m_pen.setWidthF(value); } void PDFPageContentEditorEditedItemSettings::onPenStyleChanged() { Qt::PenStyle penStyle = static_cast(ui->penStyleCombo->currentData().toInt()); m_pen.setStyle(penStyle); } void PDFPageContentEditorEditedItemSettings::onBrushStyleChanged() { Qt::BrushStyle brushStyle = static_cast(ui->brushStyleCombo->currentData().toInt()); m_brush.setStyle(brushStyle); } void PDFPageContentEditorEditedItemSettings::onPenColorComboTextChanged() { QColor color(ui->penColorCombo->currentText()); if (color.isValid()) { setColorToComboBox(ui->penColorCombo, color); m_pen.setColor(color); } else if (ui->penColorCombo->currentIndex() != -1) { ui->penColorCombo->setEditText(ui->penColorCombo->itemText(ui->penColorCombo->currentIndex())); } } void PDFPageContentEditorEditedItemSettings::onPenColorComboIndexChanged() { const int index = ui->penColorCombo->currentIndex(); QColor color = ui->penColorCombo->itemData(index, Qt::UserRole).value(); if (color.isValid() && m_pen.color() != color) { m_pen.setColor(color); } } void PDFPageContentEditorEditedItemSettings::onBrushColorComboTextChanged() { QColor color(ui->brushColorCombo->currentText()); if (color.isValid()) { setColorToComboBox(ui->brushColorCombo, color); m_brush.setColor(color); } else if (ui->brushColorCombo->currentIndex() != -1) { ui->brushColorCombo->setEditText(ui->brushColorCombo->itemText(ui->brushColorCombo->currentIndex())); } } void PDFPageContentEditorEditedItemSettings::onBrushColorComboIndexChanged() { const int index = ui->brushColorCombo->currentIndex(); QColor color = ui->brushColorCombo->itemData(index, Qt::UserRole).value(); if (color.isValid()) { m_brush.setColor(color); } } } // namespace pdf