Issue #159: Renaming applications

This commit is contained in:
Jakub Melka
2024-03-16 17:02:44 +01:00
parent a70f45c8a9
commit 28807b4f12
450 changed files with 1440 additions and 1416 deletions

View File

@@ -0,0 +1,36 @@
# Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
add_library(OutputPreviewPlugin SHARED
inkcoveragedialog.cpp
outputpreviewdialog.cpp
outputpreviewplugin.cpp
outputpreviewwidget.cpp
inkcoveragedialog.ui
outputpreviewdialog.ui
icons.qrc
)
target_link_libraries(OutputPreviewPlugin PRIVATE Pdf4QtLibCore Pdf4QtLibWidgets Qt6::Core Qt6::Gui Qt6::Widgets)
set_target_properties(OutputPreviewPlugin PROPERTIES
VERSION ${PDF4QT_VERSION}
SOVERSION ${PDF4QT_VERSION}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR})
install(TARGETS OutputPreviewPlugin RUNTIME DESTINATION ${PDF4QT_PLUGINS_DIR} LIBRARY DESTINATION ${PDF4QT_PLUGINS_DIR})

View File

@@ -0,0 +1,7 @@
{
"Name" : "OutputPreview",
"Author" : "Jakub Melka",
"Version" : "1.0.0",
"License" : "LGPL v3",
"Description" : "View prepress output preview (overprint, spot colors, advanced transparency). Measure ink coverage for process and spot colors for all pages."
}

View File

@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/pdfplugins/outputpreview">
<file>preview.svg</file>
<file>ink-coverage.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<g>
<path fill="#292D32" d="M22.077,17.182c0.128-0.621,0.193-1.346,0.203-2.216V9.03c-0.012-0.89-0.076-1.593-0.204-2.21
c-0.253-1.393-0.759-2.461-1.55-3.271c-0.59-0.618-1.389-1.092-2.377-1.404c-0.905-0.281-2.021-0.425-3.311-0.425l-5.68,0.002
c-5.146,0-7.439,2.293-7.439,7.438v5.688c0,5.139,2.295,7.43,7.439,7.43h5.681c1.291,0,2.403-0.145,3.312-0.424
c0.98-0.306,1.762-0.762,2.379-1.396C21.315,19.65,21.821,18.58,22.077,17.182z M18.22,20.134
c-0.643,0.284-1.443,0.446-2.451,0.496c-0.304,0.021-0.604,0.031-0.927,0.031H9.161c-4.354,0-5.821-1.466-5.821-5.812v-5.69
c0-4.323,1.44-5.79,5.715-5.812l0.107-0.021h5.68c0.33,0,0.646,0.011,0.938,0.029c1.016,0.063,1.818,0.229,2.453,0.51
c1.193,0.507,1.901,1.432,2.228,2.91c0.131,0.632,0.196,1.367,0.205,2.252v5.938c-0.009,0.89-0.074,1.624-0.205,2.255
C20.135,18.702,19.423,19.628,18.22,20.134z"/>
</g>
</g>
<g>
<path fill="#292D32" d="M16.32,12.727c-0.234,0.012-0.43,0.006-0.618-0.062c-0.476-0.166-0.578-0.688-0.19-1.007
c0.133-0.108,0.297-0.182,0.451-0.258c0.102-0.051,0.186-0.111,0.248-0.203c0.073-0.104,0.068-0.209-0.02-0.303
c-0.047-0.049-0.102-0.097-0.162-0.127c-0.12-0.059-0.244-0.113-0.371-0.154c-0.149-0.049-0.279-0.125-0.396-0.225
c-0.3-0.26-0.332-0.568-0.076-0.871c0.114-0.135,0.266-0.24,0.396-0.363c0.115-0.109,0.24-0.215,0.335-0.344
c0.142-0.194,0.157-0.416,0.037-0.629c-0.14-0.25-0.362-0.388-0.64-0.438c-0.24-0.046-0.435,0.026-0.588,0.231
c-0.104,0.139-0.231,0.264-0.369,0.366c-0.299,0.224-0.637,0.304-0.996,0.179c-0.703-0.244-1.113-1.127-0.762-1.783
c0.049-0.092,0.065-0.215,0.061-0.319c-0.014-0.201-0.16-0.315-0.329-0.396c-0.3-0.14-0.617-0.203-0.948-0.167
c-0.281,0.03-0.488,0.201-0.559,0.449c-0.045,0.156-0.016,0.305,0.045,0.454c0.068,0.168,0.137,0.341,0.172,0.517
c0.059,0.291-0.107,0.506-0.4,0.545c-0.125,0.018-0.244-0.01-0.365-0.051c-0.149-0.055-0.302-0.109-0.459-0.141
C9.289,7.521,8.934,7.775,8.861,8.309C8.839,8.467,8.839,8.629,8.83,8.79C8.82,8.96,8.799,9.128,8.717,9.279
C8.63,9.443,8.49,9.512,8.308,9.479C8.07,9.436,7.884,9.297,7.696,9.158C7.609,9.096,7.518,9.032,7.419,8.99
c-0.431-0.18-0.888-0.018-1.058,0.386c-0.17,0.401-0.136,0.804,0.063,1.187c0.104,0.201,0.209,0.24,0.431,0.195
c0.254-0.052,0.505,0.083,0.604,0.324c0.095,0.23,0.025,0.389-0.211,0.473c-0.018,0.008-0.035,0.012-0.052,0.018
c-0.187,0.068-0.373,0.135-0.559,0.203c-0.354,0.131-0.42,0.643-0.135,0.89c0.132,0.114,0.283,0.175,0.458,0.194
c0.228,0.027,0.458,0.059,0.679,0.113c0.319,0.08,0.459,0.396,0.324,0.693c-0.086,0.191-0.217,0.346-0.401,0.447
c-0.217,0.123-0.444,0.223-0.688,0.281c-0.156,0.037-0.251,0.139-0.266,0.289c-0.014,0.146-0.006,0.301,0.015,0.446
c0.053,0.363,0.23,0.656,0.537,0.863c0.146,0.098,0.306,0.155,0.485,0.141c0.188-0.014,0.333-0.11,0.457-0.243
c0.182-0.193,0.309-0.423,0.393-0.675c0.059-0.178,0.175-0.301,0.362-0.342c0.264-0.062,0.535,0.069,0.637,0.323
c0.078,0.195,0.132,0.4,0.191,0.605c0.037,0.129,0.049,0.266,0.095,0.391c0.059,0.16,0.186,0.229,0.355,0.223
c0.155-0.006,0.286-0.07,0.396-0.18c0.072-0.072,0.142-0.148,0.217-0.219c0.115-0.109,0.24-0.209,0.396-0.259
c0.156-0.051,0.293-0.014,0.403,0.104c0.154,0.166,0.188,0.359,0.132,0.576c-0.082,0.314,0.012,0.574,0.291,0.744
c0.311,0.191,0.654,0.273,1.016,0.242c0.265-0.023,0.455-0.166,0.527-0.426c0.039-0.146,0.055-0.303,0.049-0.453
c-0.006-0.172-0.051-0.344-0.072-0.518c-0.028-0.213-0.035-0.428,0.039-0.635c0.06-0.16,0.17-0.271,0.344-0.293
c0.086-0.011,0.175-0.013,0.26,0.002c0.221,0.037,0.387,0.164,0.52,0.338c0.07,0.096,0.158,0.16,0.275,0.188
c0.256,0.055,0.439-0.115,0.397-0.371c-0.013-0.074-0.035-0.148-0.063-0.219c-0.033-0.088-0.08-0.172-0.119-0.26
c-0.144-0.318,0.008-0.691,0.326-0.816c0.104-0.041,0.196-0.031,0.279,0.045c0.07,0.064,0.137,0.133,0.209,0.193
c0.12,0.1,0.234,0.209,0.367,0.289c0.26,0.16,0.494,0.119,0.695-0.107c0.176-0.199,0.291-0.43,0.354-0.684
c0.098-0.391-0.048-0.689-0.411-0.858C16.747,12.741,16.52,12.71,16.32,12.727z M10.189,17.535
c-0.017-0.176-0.057-0.34-0.146-0.488c-0.107-0.18-0.249-0.225-0.438-0.131c-0.207,0.102-0.354,0.266-0.453,0.473
c-0.159,0.332-0.027,0.627,0.324,0.736c0.041,0.014,0.083,0.02,0.125,0.025c0.222,0.029,0.421-0.08,0.503-0.287
C10.148,17.76,10.163,17.645,10.189,17.535z M17.51,11.159c0.403,0.001,0.6-0.191,0.577-0.525c-0.021-0.294-0.246-0.504-0.51-0.47
c-0.28,0.037-0.492,0.186-0.636,0.425c-0.103,0.169-0.069,0.306,0.096,0.417C17.191,11.111,17.367,11.154,17.51,11.159z
M8.573,7.156c0.322,0.002,0.442-0.133,0.396-0.42C8.937,6.54,8.851,6.365,8.709,6.223C8.497,6.006,8.227,6.018,8.007,6.25
C7.802,6.465,7.811,6.751,8.03,6.943C8.193,7.087,8.388,7.152,8.573,7.156z M8.127,8.549c0.001-0.096-0.078-0.177-0.174-0.18
C7.857,8.367,7.775,8.445,7.771,8.541c-0.003,0.1,0.08,0.186,0.178,0.186C8.043,8.727,8.126,8.645,8.127,8.549z M6.267,13.596
c0.001-0.094-0.08-0.176-0.174-0.18c-0.095,0-0.178,0.078-0.182,0.174c-0.004,0.098,0.08,0.184,0.178,0.184
C6.184,13.773,6.266,13.691,6.267,13.596z M13.882,7.575c0.001-0.099-0.073-0.179-0.171-0.181c-0.102-0.002-0.184,0.078-0.182,0.18
c0,0.098,0.078,0.174,0.175,0.175C13.805,7.749,13.88,7.674,13.882,7.575z M14.326,16.34c0.099-0.002,0.175-0.076,0.175-0.176
c0.001-0.098-0.073-0.176-0.173-0.178s-0.182,0.08-0.18,0.18C14.148,16.264,14.229,16.34,14.326,16.34z"/>
<path fill="#292D32" d="M16.32,12.727c0.199-0.017,0.427,0.016,0.644,0.116c0.363,0.169,0.509,0.468,0.411,0.858
c-0.063,0.254-0.18,0.484-0.354,0.684c-0.201,0.227-0.437,0.268-0.695,0.107c-0.133-0.08-0.247-0.189-0.367-0.289
c-0.072-0.061-0.139-0.129-0.209-0.193c-0.083-0.076-0.175-0.086-0.279-0.045c-0.318,0.125-0.47,0.498-0.326,0.816
c0.039,0.088,0.086,0.172,0.119,0.26c0.028,0.07,0.053,0.145,0.063,0.219c0.042,0.256-0.144,0.426-0.397,0.371
c-0.117-0.027-0.205-0.092-0.275-0.188c-0.133-0.174-0.299-0.301-0.52-0.338c-0.085-0.015-0.174-0.013-0.26-0.002
c-0.174,0.022-0.284,0.133-0.344,0.293c-0.074,0.207-0.067,0.422-0.039,0.635c0.021,0.174,0.066,0.346,0.072,0.518
c0.006,0.15-0.01,0.307-0.049,0.453c-0.072,0.26-0.264,0.402-0.527,0.426c-0.361,0.031-0.706-0.051-1.016-0.242
c-0.279-0.17-0.373-0.43-0.291-0.744c0.057-0.217,0.022-0.409-0.132-0.575c-0.11-0.119-0.247-0.156-0.403-0.104
c-0.156,0.049-0.28,0.147-0.396,0.258c-0.076,0.069-0.145,0.146-0.217,0.219c-0.109,0.109-0.241,0.174-0.396,0.18
c-0.17,0.008-0.296-0.063-0.354-0.223c-0.046-0.125-0.058-0.262-0.095-0.391c-0.059-0.203-0.113-0.408-0.191-0.604
c-0.102-0.255-0.373-0.386-0.637-0.324c-0.188,0.041-0.304,0.164-0.362,0.342c-0.084,0.253-0.211,0.479-0.393,0.675
c-0.124,0.133-0.269,0.229-0.457,0.244c-0.18,0.016-0.34-0.044-0.485-0.142c-0.307-0.207-0.484-0.5-0.537-0.863
c-0.021-0.146-0.029-0.301-0.015-0.446c0.015-0.15,0.11-0.252,0.266-0.289c0.244-0.06,0.471-0.158,0.688-0.281
c0.184-0.102,0.315-0.256,0.401-0.447c0.135-0.299-0.005-0.613-0.324-0.693c-0.222-0.056-0.451-0.086-0.679-0.113
c-0.175-0.021-0.326-0.079-0.458-0.194c-0.285-0.247-0.218-0.759,0.135-0.89c0.186-0.068,0.372-0.135,0.559-0.202
c0.017-0.007,0.035-0.011,0.052-0.019c0.236-0.084,0.306-0.242,0.211-0.473c-0.099-0.24-0.35-0.376-0.604-0.324
c-0.221,0.045-0.326,0.006-0.431-0.195c-0.2-0.383-0.233-0.785-0.063-1.187c0.17-0.401,0.627-0.565,1.058-0.386
c0.098,0.042,0.19,0.104,0.276,0.168c0.188,0.139,0.375,0.277,0.612,0.32C8.49,9.512,8.63,9.443,8.717,9.279
C8.799,9.128,8.819,8.96,8.83,8.79c0.01-0.161,0.01-0.323,0.031-0.481c0.073-0.533,0.428-0.787,0.955-0.682
c0.157,0.031,0.31,0.086,0.461,0.141c0.119,0.043,0.238,0.068,0.365,0.053c0.293-0.041,0.459-0.254,0.399-0.547
c-0.036-0.176-0.104-0.348-0.172-0.516c-0.062-0.15-0.089-0.298-0.045-0.455c0.069-0.248,0.278-0.417,0.559-0.448
c0.332-0.035,0.648,0.028,0.948,0.167c0.168,0.08,0.314,0.194,0.327,0.396c0.006,0.105-0.011,0.229-0.06,0.32
c-0.353,0.656,0.058,1.539,0.76,1.781c0.361,0.125,0.699,0.045,0.996-0.178c0.139-0.104,0.267-0.229,0.369-0.367
c0.154-0.205,0.348-0.277,0.59-0.23c0.275,0.051,0.498,0.188,0.639,0.438c0.119,0.215,0.104,0.436-0.037,0.629
c-0.094,0.129-0.221,0.234-0.335,0.346C15.45,9.277,15.3,9.384,15.186,9.52c-0.256,0.303-0.224,0.61,0.076,0.869
c0.117,0.102,0.247,0.178,0.396,0.227c0.127,0.041,0.251,0.096,0.371,0.154c0.062,0.029,0.115,0.076,0.162,0.125
c0.088,0.094,0.093,0.198,0.02,0.305c-0.063,0.091-0.146,0.152-0.248,0.201c-0.154,0.076-0.318,0.149-0.451,0.259
c-0.388,0.317-0.283,0.841,0.19,1.007C15.891,12.73,16.086,12.738,16.32,12.727z"/>
<path fill="#292D32" d="M10.189,17.535c-0.026,0.109-0.041,0.225-0.082,0.328c-0.084,0.207-0.282,0.316-0.504,0.287
c-0.042-0.006-0.084-0.012-0.125-0.025c-0.352-0.109-0.483-0.404-0.324-0.736c0.099-0.207,0.246-0.371,0.453-0.473
c0.189-0.094,0.331-0.049,0.438,0.131C10.133,17.195,10.173,17.359,10.189,17.535z"/>
<path fill="#292D32" d="M17.51,11.159c-0.143-0.005-0.316-0.048-0.473-0.153c-0.165-0.111-0.197-0.248-0.096-0.417
c0.144-0.239,0.354-0.388,0.636-0.425c0.264-0.034,0.489,0.176,0.51,0.47C18.109,10.968,17.913,11.16,17.51,11.159z"/>
<path fill="#292D32" d="M8.573,7.156C8.388,7.152,8.193,7.087,8.03,6.943C7.811,6.751,7.802,6.465,8.007,6.25
c0.22-0.232,0.49-0.244,0.703-0.027C8.851,6.365,8.937,6.54,8.969,6.736C9.015,7.023,8.895,7.158,8.573,7.156z"/>
<path fill="#292D32" d="M8.127,8.549C8.126,8.645,8.043,8.727,7.949,8.727c-0.099,0-0.182-0.086-0.178-0.186
c0.003-0.096,0.086-0.174,0.181-0.172C8.049,8.372,8.128,8.453,8.127,8.549z"/>
<path fill="#292D32" d="M6.267,13.596c-0.001,0.096-0.083,0.178-0.178,0.178c-0.098,0-0.182-0.086-0.178-0.184
c0.003-0.096,0.086-0.174,0.182-0.174C6.188,13.42,6.268,13.502,6.267,13.596z"/>
<path fill="#292D32" d="M13.882,7.575c-0.002,0.099-0.077,0.174-0.178,0.174c-0.097-0.001-0.175-0.077-0.175-0.175
c-0.002-0.102,0.08-0.182,0.182-0.18C13.809,7.396,13.883,7.477,13.882,7.575z"/>
<path fill="#292D32" d="M14.326,16.34c-0.1,0-0.178-0.076-0.178-0.174c-0.002-0.1,0.08-0.182,0.18-0.18s0.174,0.08,0.173,0.178
C14.501,16.264,14.425,16.338,14.326,16.34z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,402 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#include "inkcoveragedialog.h"
#include "ui_inkcoveragedialog.h"
#include "pdfdrawspacecontroller.h"
#include "pdfwidgetutils.h"
#include <QCloseEvent>
#include <QtConcurrent/QtConcurrent>
namespace pdfplugin
{
InkCoverageDialog::InkCoverageDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent) :
QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
ui(new Ui::InkCoverageDialog),
m_inkMapper(widget->getCMSManager(), document),
m_document(document),
m_widget(widget),
m_model(nullptr),
m_futureWatcher(nullptr)
{
ui->setupUi(this);
m_inkMapper.createSpotColors(true);
m_model = new InkCoverageStatisticsModel(this);
ui->coverageTableView->setModel(m_model);
ui->coverageTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600)));
updateInkCoverage();
pdf::PDFWidgetUtils::style(this);
}
InkCoverageDialog::~InkCoverageDialog()
{
delete ui;
}
void InkCoverageDialog::updateInkCoverage()
{
Q_ASSERT(!m_future.isRunning());
Q_ASSERT(!m_futureWatcher);
auto calculateInkCoverage = [this]() -> InkCoverageResults
{
InkCoverageResults results;
pdf::PDFTransparencyRendererSettings settings;
// Jakub Melka: debug is very slow, use multithreading
#ifdef QT_DEBUG
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::MultithreadedPathSampler, true);
#endif
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::ActiveColorMask, false);
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SeparationSimulation, m_inkMapper.getActiveSpotColorCount() > 0);
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
pdf::PDFInkCoverageCalculator calculator(m_document,
m_widget->getDrawWidgetProxy()->getFontCache(),
m_widget->getCMSManager(),
m_widget->getDrawWidgetProxy()->getOptionalContentActivity(),
&m_inkMapper,
m_widget->getDrawWidgetProxy()->getProgress(),
settings);
std::vector<pdf::PDFInteger> pageIndices;
pageIndices.resize(m_document->getCatalog()->getPageCount(), 0);
std::iota(pageIndices.begin(), pageIndices.end(), 0);
calculator.perform(QSize(RESOLUTION, RESOLUTION), pageIndices);
CoverageInfo coverageInfo;
for (const pdf::PDFInteger pageIndex : pageIndices)
{
const std::vector<pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo>* coverage = calculator.getInkCoverage(pageIndex);
if (!coverage)
{
// Jakub Melka: Something bad happened?
continue;
}
for (const auto& info : *coverage)
{
if (!pdf::PDFInkCoverageCalculator::findCoverageInfoByName(coverageInfo, info.name))
{
coverageInfo.push_back(info);
coverageInfo.back().coveredArea = 0.0;
coverageInfo.back().ratio = 0.0;
}
}
}
CoverageInfo templateInfo = coverageInfo;
CoverageInfo sumInfo = coverageInfo;
for (const pdf::PDFInteger pageIndex : pageIndices)
{
const std::vector<pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo>* coverage = calculator.getInkCoverage(pageIndex);
results.pageInfo.push_back(templateInfo);
if (!coverage)
{
// Jakub Melka: Something bad happened?
continue;
}
CoverageInfo& currentInfo = results.pageInfo.back();
for (const auto& info : *coverage)
{
pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo* channelInfo = pdf::PDFInkCoverageCalculator::findCoverageInfoByName(currentInfo, info.name);
pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo* sumChannelInfo = pdf::PDFInkCoverageCalculator::findCoverageInfoByName(sumInfo, info.name);
channelInfo->coveredArea = info.coveredArea;
channelInfo->ratio = info.ratio;
sumChannelInfo->coveredArea += info.coveredArea;
}
}
results.sumInfo = qMove(sumInfo);
return results;
};
m_future = QtConcurrent::run(calculateInkCoverage);
m_futureWatcher = new QFutureWatcher<InkCoverageResults>();
connect(m_futureWatcher, &QFutureWatcher<InkCoverageResults>::finished, this, &InkCoverageDialog::onInkCoverageCalculated);
m_futureWatcher->setFuture(m_future);
}
void InkCoverageDialog::closeEvent(QCloseEvent* event)
{
if (!isInkCoverageCalculated())
{
event->ignore();
return;
}
BaseClass::closeEvent(event);
}
void InkCoverageDialog::accept()
{
if (!isInkCoverageCalculated())
{
return;
}
BaseClass::accept();
}
void InkCoverageDialog::reject()
{
if (!isInkCoverageCalculated())
{
return;
}
BaseClass::reject();
}
void InkCoverageDialog::onInkCoverageCalculated()
{
Q_ASSERT(m_future.isFinished());
InkCoverageResults results = m_future.result();
m_model->setInkCoverageResults(qMove(results));
}
bool InkCoverageDialog::isInkCoverageCalculated() const
{
return !(m_futureWatcher && m_futureWatcher->isRunning());
}
InkCoverageStatisticsModel::InkCoverageStatisticsModel(QObject* parent) :
BaseClass(parent)
{
}
QModelIndex InkCoverageStatisticsModel::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
return createIndex(row, column, nullptr);
}
QModelIndex InkCoverageStatisticsModel::parent(const QModelIndex& child) const
{
Q_UNUSED(child);
return QModelIndex();
}
int InkCoverageStatisticsModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
if (m_inkCoverageResults.pageInfo.empty())
{
return 0;
}
return int(m_inkCoverageResults.pageInfo.size() + 1);
}
int InkCoverageStatisticsModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return LastStandardColumn + getChannelCount() * LastChannelColumn;
}
QVariant InkCoverageStatisticsModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch (role)
{
case Qt::DisplayRole:
{
const int row = index.row();
QLocale locale;
const bool isTotalRow = row == rowCount(index.parent()) - 1;
if (index.column() == StandardColumnPageIndex)
{
if (isTotalRow)
{
return tr("Total");
}
return locale.toString(row + 1);
}
Q_ASSERT(index.column() >= LastStandardColumn);
const CoverageInfo* info = nullptr;
if (row < m_inkCoverageResults.pageInfo.size())
{
info = &m_inkCoverageResults.pageInfo[row];
}
else
{
info = &m_inkCoverageResults.sumInfo;
}
const int channelIndex = getChannelIndex(index.column());
EChannelColumn channelColumn = getChannelColumn(index.column());
ChannelCoverageInfo channelInfo = info->at(channelIndex);
switch (channelColumn)
{
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnColorant:
return QString();
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageArea:
return locale.toString(channelInfo.coveredArea, 'f', 2);
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageRatio:
return !isTotalRow ? locale.toString(channelInfo.ratio * 100.0, 'f', 0) : QString();
default:
Q_ASSERT(false);
break;
}
break;
}
case Qt::TextAlignmentRole:
{
if (index.column() == StandardColumnPageIndex)
{
return int(Qt::AlignCenter);
}
Q_ASSERT(index.column() >= LastStandardColumn);
EChannelColumn channelColumn = getChannelColumn(index.column());
switch (channelColumn)
{
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnColorant:
return int(Qt::AlignLeft | Qt::AlignVCenter);
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageArea:
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageRatio:
return int(Qt::AlignRight | Qt::AlignVCenter);
default:
Q_ASSERT(false);
break;
}
break;
}
case Qt::BackgroundRole:
{
if (index.column() >= LastStandardColumn && getChannelColumn(index.column()) == ChannelColumnColorant)
{
const int channelIndex = getChannelIndex(index.column());
return QBrush(m_inkCoverageResults.sumInfo[channelIndex].color);
}
break;
}
default:
break;
}
return QVariant();
}
QVariant InkCoverageStatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
{
return QVariant();
}
if (section < LastStandardColumn)
{
Q_ASSERT(section == StandardColumnPageIndex);
return tr("Page Index");
}
const EChannelColumn channelColumn = getChannelColumn(section);
switch (channelColumn)
{
case ChannelColumnColorant:
{
const int channelIndex = getChannelIndex(section);
return m_inkCoverageResults.sumInfo[channelIndex].textName;
}
case ChannelColumnCoverageArea:
return tr("[ mm² ]");
case ChannelColumnCoverageRatio:
return tr("[ % ]");
case LastChannelColumn:
Q_ASSERT(false);
break;
default:
Q_ASSERT(false);
break;
}
return QVariant();
}
void InkCoverageStatisticsModel::setInkCoverageResults(InkCoverageResults inkCoverageResults)
{
beginResetModel();
m_inkCoverageResults = qMove(inkCoverageResults);
endResetModel();
}
int InkCoverageStatisticsModel::getChannelCount() const
{
return int(m_inkCoverageResults.sumInfo.size());
}
InkCoverageStatisticsModel::EChannelColumn InkCoverageStatisticsModel::getChannelColumn(int index) const
{
Q_ASSERT(index >= LastStandardColumn);
index -= LastStandardColumn;
return static_cast<EChannelColumn>(index % LastChannelColumn);
}
int InkCoverageStatisticsModel::getChannelIndex(int index) const
{
Q_ASSERT(index >= LastStandardColumn);
index -= LastStandardColumn;
return index / LastChannelColumn;
}
} // pdfplugin

View File

@@ -0,0 +1,125 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#ifndef INKCOVERAGEDIALOG_H
#define INKCOVERAGEDIALOG_H
#include "pdfdocument.h"
#include "pdfdrawwidget.h"
#include "pdftransparencyrenderer.h"
#include <QDialog>
#include <QFuture>
#include <QFutureWatcher>
#include <QAbstractItemModel>
namespace Ui
{
class InkCoverageDialog;
}
namespace pdfplugin
{
class InkCoverageStatisticsModel;
using ChannelCoverageInfo = pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo;
using CoverageInfo = std::vector<ChannelCoverageInfo>;
struct InkCoverageResults
{
std::vector<CoverageInfo> pageInfo;
CoverageInfo sumInfo;
};
class InkCoverageDialog : public QDialog
{
Q_OBJECT
private:
using BaseClass = QDialog;
public:
explicit InkCoverageDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent);
virtual ~InkCoverageDialog() override;
void updateInkCoverage();
virtual void closeEvent(QCloseEvent* event) override;
virtual void accept() override;
virtual void reject() override;
private:
Ui::InkCoverageDialog* ui;
static constexpr int RESOLUTION = 1920;
void onInkCoverageCalculated();
bool isInkCoverageCalculated() const;
pdf::PDFInkMapper m_inkMapper;
const pdf::PDFDocument* m_document;
pdf::PDFWidget* m_widget;
InkCoverageStatisticsModel* m_model;
QFuture<InkCoverageResults> m_future;
QFutureWatcher<InkCoverageResults>* m_futureWatcher;
};
class InkCoverageStatisticsModel : public QAbstractItemModel
{
Q_OBJECT
private:
using BaseClass = QAbstractItemModel;
public:
explicit InkCoverageStatisticsModel(QObject* parent);
enum EStandardColumn
{
StandardColumnPageIndex,
LastStandardColumn
};
enum EChannelColumn
{
ChannelColumnColorant,
ChannelColumnCoverageArea,
ChannelColumnCoverageRatio,
LastChannelColumn
};
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void setInkCoverageResults(InkCoverageResults inkCoverageResults);
int getChannelCount() const;
EChannelColumn getChannelColumn(int index) const;
int getChannelIndex(int index) const;
private:
InkCoverageResults m_inkCoverageResults;
};
} // namespace pdfplugin
#endif // INKCOVERAGEDIALOG_H

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InkCoverageDialog</class>
<widget class="QDialog" name="InkCoverageDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>551</height>
</rect>
</property>
<property name="windowTitle">
<string>Ink Coverage Calculator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="coverageLabel">
<property name="text">
<string>Ink Coverage</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="coverageTableView">
<attribute name="horizontalHeaderMinimumSectionSize">
<number>40</number>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InkCoverageDialog</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>InkCoverageDialog</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>

View File

@@ -0,0 +1,429 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#include "outputpreviewdialog.h"
#include "ui_outputpreviewdialog.h"
#include "pdfcms.h"
#include "pdfrenderer.h"
#include "pdfwidgetutils.h"
#include "pdfdrawspacecontroller.h"
#include <QCloseEvent>
#include <QColorDialog>
#include <QtConcurrent/QtConcurrent>
namespace pdfplugin
{
OutputPreviewDialog::OutputPreviewDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent) :
QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
ui(new Ui::OutputPreviewDialog),
m_inkMapper(widget->getCMSManager(), document),
m_inkMapperForRendering(widget->getCMSManager(), document),
m_document(document),
m_widget(widget),
m_needUpdateImage(false),
m_outputPreviewWidget(new OutputPreviewWidget(this)),
m_futureWatcher(nullptr)
{
ui->setupUi(this);
ui->pageIndexScrollBar->setMinimum(1);
ui->pageIndexScrollBar->setValue(1);
ui->pageIndexScrollBar->setMaximum(int(document->getCatalog()->getPageCount()));
ui->frameViewLayout->insertWidget(0, m_outputPreviewWidget);
ui->displayModeComboBox->addItem(tr("Separations"), OutputPreviewWidget::Separations);
ui->displayModeComboBox->addItem(tr("Color Warnings | Ink Coverage"), OutputPreviewWidget::ColorWarningInkCoverage);
ui->displayModeComboBox->addItem(tr("Color Warnings | Rich Black"), OutputPreviewWidget::ColorWarningRichBlack);
ui->displayModeComboBox->addItem(tr("Ink Coverage"), OutputPreviewWidget::InkCoverage);
ui->displayModeComboBox->addItem(tr("Shape Channel"), OutputPreviewWidget::ShapeChannel);
ui->displayModeComboBox->addItem(tr("Opacity Channel"), OutputPreviewWidget::OpacityChannel);
ui->displayModeComboBox->setCurrentIndex(0);
m_outputPreviewWidget->setInkMapper(&m_inkMapper);
ui->inksTreeWidget->setMinimumHeight(pdf::PDFWidgetUtils::scaleDPI_y(ui->inksTreeWidget, 150));
m_inkMapper.createSpotColors(ui->simulateSeparationsCheckBox->isChecked());
connect(ui->simulateSeparationsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::onSimulateSeparationsChecked);
connect(ui->simulatePaperColorCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::onSimulatePaperColorChecked);
connect(ui->redPaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
connect(ui->greenPaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
connect(ui->bluePaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
connect(ui->pageIndexScrollBar, &QScrollBar::valueChanged, this, &OutputPreviewDialog::updatePageImage);
connect(ui->displayImagesCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
connect(ui->displayShadingCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
connect(ui->displayTextCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
connect(ui->displayTilingPatternsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
connect(ui->displayVectorGraphicsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
connect(ui->inksTreeWidget->model(), &QAbstractItemModel::dataChanged, this, &OutputPreviewDialog::onInksChanged);
connect(ui->alarmColorButton, &QPushButton::clicked, this, &OutputPreviewDialog::onAlarmColorButtonClicked);
connect(ui->displayModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &OutputPreviewDialog::onDisplayModeChanged);
connect(ui->inkCoverageLimitEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onInkCoverageLimitChanged);
connect(ui->richBlackLimitEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onRichBlackLimtiChanged);
updatePageImage();
updateInks();
updatePaperColorWidgets();
updateAlarmColorButtonIcon();
onDisplayModeChanged();
onInkCoverageLimitChanged(ui->inkCoverageLimitEdit->value());
onRichBlackLimtiChanged(ui->richBlackLimitEdit->value());
pdf::PDFWidgetUtils::style(this);
}
OutputPreviewDialog::~OutputPreviewDialog()
{
delete ui;
}
void OutputPreviewDialog::resizeEvent(QResizeEvent* event)
{
QDialog::resizeEvent(event);
updatePageImage();
}
void OutputPreviewDialog::closeEvent(QCloseEvent* event)
{
if (!isRenderingDone())
{
event->ignore();
}
}
void OutputPreviewDialog::showEvent(QShowEvent* event)
{
Q_UNUSED(event);
updatePageImage();
}
void OutputPreviewDialog::updateInks()
{
ui->inksTreeWidget->setUpdatesEnabled(false);
ui->inksTreeWidget->clear();
QTreeWidgetItem* processColorsRoot = new QTreeWidgetItem(ui->inksTreeWidget, QStringList(tr("Process Inks")));
QTreeWidgetItem* spotColorsRoot = new QTreeWidgetItem(ui->inksTreeWidget, QStringList(tr("Spot Inks")));
processColorsRoot->setFlags(processColorsRoot->flags() | Qt::ItemIsUserCheckable);
processColorsRoot->setCheckState(0, Qt::Checked);
spotColorsRoot->setFlags(spotColorsRoot->flags() | Qt::ItemIsUserCheckable);
spotColorsRoot->setCheckState(0, Qt::Checked);
QSize iconSize = pdf::PDFWidgetUtils::scaleDPI(ui->inksTreeWidget, QSize(16, 16));
ui->inksTreeWidget->setIconSize(iconSize);
ui->inksTreeWidget->setRootIsDecorated(true);
int colorIndex = 0;
std::vector<pdf::PDFInkMapper::ColorInfo> separations = m_inkMapper.getSeparations(4);
for (const auto& colorInfo : separations)
{
QTreeWidgetItem* item = nullptr;
if (!colorInfo.isSpot)
{
// Process color (ink)
item = new QTreeWidgetItem(processColorsRoot, QStringList(colorInfo.textName));
}
else
{
// Spot color (ink)
item = new QTreeWidgetItem(spotColorsRoot, QStringList(colorInfo.textName));
}
if (colorInfo.color.isValid())
{
QPixmap icon(iconSize);
icon.fill(colorInfo.color);
item->setIcon(0, QIcon(icon));
}
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(0, Qt::Checked);
item->setData(0, Qt::UserRole, colorIndex++);
}
if (processColorsRoot->childCount() == 0)
{
delete processColorsRoot;
}
if (spotColorsRoot->childCount() == 0)
{
delete spotColorsRoot;
}
ui->inksTreeWidget->expandAll();
ui->inksTreeWidget->setUpdatesEnabled(true);
}
void OutputPreviewDialog::updatePaperColorWidgets()
{
const bool isPaperColorEnabled = ui->simulatePaperColorCheckBox->isChecked();
ui->redPaperColorEdit->setEnabled(isPaperColorEnabled);
ui->greenPaperColorEdit->setEnabled(isPaperColorEnabled);
ui->bluePaperColorEdit->setEnabled(isPaperColorEnabled);
if (!isPaperColorEnabled)
{
ui->redPaperColorEdit->setValue(1.0);
ui->greenPaperColorEdit->setValue(1.0);
ui->bluePaperColorEdit->setValue(1.0);
}
}
void OutputPreviewDialog::updateAlarmColorButtonIcon()
{
QSize iconSize = ui->alarmColorButton->iconSize();
QPixmap pixmap(iconSize);
pixmap.fill(m_outputPreviewWidget->getAlarmColor());
ui->alarmColorButton->setIcon(QIcon(pixmap));
}
void OutputPreviewDialog::onPaperColorChanged()
{
const bool isPaperColorEnabled = ui->simulatePaperColorCheckBox->isChecked();
if (isPaperColorEnabled)
{
updatePageImage();
}
}
void OutputPreviewDialog::onAlarmColorButtonClicked()
{
QColorDialog colorDialog(m_outputPreviewWidget->getAlarmColor(), this);
if (colorDialog.exec() == QColorDialog::Accepted)
{
m_outputPreviewWidget->setAlarmColor(colorDialog.currentColor());
updateAlarmColorButtonIcon();
}
}
void OutputPreviewDialog::onSimulateSeparationsChecked(bool checked)
{
m_inkMapper.setSpotColorsActive(checked);
updateInks();
updatePageImage();
}
void OutputPreviewDialog::onSimulatePaperColorChecked(bool checked)
{
Q_UNUSED(checked);
updatePaperColorWidgets();
updatePageImage();
}
void OutputPreviewDialog::onDisplayModeChanged()
{
m_outputPreviewWidget->setDisplayMode(OutputPreviewWidget::DisplayMode(ui->displayModeComboBox->currentData().toInt()));
}
void OutputPreviewDialog::onInksChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
{
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
if (roles.contains(Qt::CheckStateRole))
{
updatePageImage();
}
}
void OutputPreviewDialog::onInkCoverageLimitChanged(double value)
{
m_outputPreviewWidget->setInkCoverageLimit(value / 100.0);
}
void OutputPreviewDialog::onRichBlackLimtiChanged(double value)
{
m_outputPreviewWidget->setRichBlackLimit(value / 100.0);
}
void OutputPreviewDialog::updatePageImage()
{
if (!isRenderingDone())
{
m_needUpdateImage = true;
return;
}
m_needUpdateImage = false;
const pdf::PDFPage* page = m_document->getCatalog()->getPage(ui->pageIndexScrollBar->value() - 1);
if (!page)
{
m_outputPreviewWidget->clear();
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
pdf::PDFRGB paperColor = pdf::PDFRGB{ 1.0f, 1.0f, 1.0f };
// Active color mask
uint32_t activeColorMask = pdf::PDFPixelFormat::getAllColorsMask();
const int itemCount = ui->inksTreeWidget->topLevelItemCount();
for (int i = 0; i < itemCount; ++i)
{
QTreeWidgetItem* rootItem = ui->inksTreeWidget->topLevelItem(i);
const bool isRootItemChecked = rootItem->data(0, Qt::CheckStateRole).toInt() == Qt::Checked;
const int childCount = rootItem->childCount();
for (int j = 0; j < childCount; ++j)
{
QTreeWidgetItem* childItem = rootItem->child(j);
const bool isChecked = childItem->data(0, Qt::CheckStateRole).toInt() == Qt::Checked;
const bool isEnabled = isRootItemChecked && isChecked;
if (!isEnabled)
{
uint32_t colorIndex = childItem->data(0, Qt::UserRole).toInt();
uint32_t colorFlags = 1 << colorIndex;
activeColorMask = activeColorMask & ~colorFlags;
}
}
}
// Paper color
if (ui->simulatePaperColorCheckBox)
{
paperColor[0] = ui->redPaperColorEdit->value();
paperColor[1] = ui->greenPaperColorEdit->value();
paperColor[2] = ui->bluePaperColorEdit->value();
}
pdf::PDFTransparencyRendererSettings::Flags flags = pdf::PDFTransparencyRendererSettings::None;
flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayImages, ui->displayImagesCheckBox->isChecked());
flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayText, ui->displayTextCheckBox->isChecked());
flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayVectorGraphics, ui->displayVectorGraphicsCheckBox->isChecked());
flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayShadings, ui->displayShadingCheckBox->isChecked());
flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayTilingPatterns, ui->displayTilingPatternsCheckBox->isChecked());
flags.setFlag(pdf::PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
m_inkMapperForRendering = m_inkMapper;
QSize renderSize = m_outputPreviewWidget->getPageImageSizeHint();
auto renderImage = [this, page, renderSize, paperColor, activeColorMask, flags]() -> RenderedImage
{
return renderPage(page, renderSize, paperColor, activeColorMask, flags);
};
m_future = QtConcurrent::run(renderImage);
m_futureWatcher = new QFutureWatcher<RenderedImage>();
connect(m_futureWatcher, &QFutureWatcher<RenderedImage>::finished, this, &OutputPreviewDialog::onPageImageRendered);
m_futureWatcher->setFuture(m_future);
}
OutputPreviewDialog::RenderedImage OutputPreviewDialog::renderPage(const pdf::PDFPage* page,
QSize renderSize,
pdf::PDFRGB paperColor,
uint32_t activeColorMask,
pdf::PDFTransparencyRendererSettings::Flags additionalFlags)
{
RenderedImage result;
QRectF pageRect = page->getRotatedMediaBox();
QSizeF pageSize = pageRect.size();
pageSize.scale(renderSize.width(), renderSize.height(), Qt::KeepAspectRatio);
QSize imageSize = pageSize.toSize();
if (!imageSize.isValid())
{
return result;
}
pdf::PDFTransparencyRendererSettings settings;
settings.flags = additionalFlags;
// Jakub Melka: debug is very slow, use multithreading
#ifdef QT_DEBUG
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::MultithreadedPathSampler, true);
#endif
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::ActiveColorMask, activeColorMask != pdf::PDFPixelFormat::getAllColorsMask());
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SeparationSimulation, m_inkMapperForRendering.getActiveSpotColorCount() > 0);
settings.activeColorMask = activeColorMask;
QTransform pagePointToDevicePoint = pdf::PDFRenderer::createPagePointToDevicePointMatrix(page, QRect(QPoint(0, 0), imageSize));
pdf::PDFDrawWidgetProxy* proxy = m_widget->getDrawWidgetProxy();
pdf::PDFCMSPointer cms = proxy->getCMSManager()->getCurrentCMS();
pdf::PDFTransparencyRenderer renderer(page, m_document, proxy->getFontCache(), cms.data(), proxy->getOptionalContentActivity(),
&m_inkMapperForRendering, settings, pagePointToDevicePoint);
renderer.beginPaint(imageSize);
result.errors = renderer.processContents();
renderer.endPaint();
QImage image = renderer.toImage(false, true, paperColor);
result.image = qMove(image);
result.originalProcessImage = renderer.getOriginalProcessBitmap();
result.pageSize = page->getRotatedMediaBoxMM().size();
return result;
}
void OutputPreviewDialog::onPageImageRendered()
{
QApplication::restoreOverrideCursor();
if (m_future.isFinished())
{
RenderedImage result = m_future.result();
m_future = QFuture<RenderedImage>();
m_futureWatcher->deleteLater();
m_futureWatcher = nullptr;
m_outputPreviewWidget->setPageImage(qMove(result.image), qMove(result.originalProcessImage), result.pageSize);
if (m_needUpdateImage)
{
updatePageImage();
}
}
}
bool OutputPreviewDialog::isRenderingDone() const
{
return !(m_futureWatcher && m_futureWatcher->isRunning());
}
void OutputPreviewDialog::accept()
{
if (!isRenderingDone())
{
return;
}
QDialog::accept();
}
void OutputPreviewDialog::reject()
{
if (!isRenderingDone())
{
return;
}
QDialog::reject();
}
} // namespace pdfplugin

View File

@@ -0,0 +1,98 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#ifndef OUTPUTPREVIEWDIALOG_H
#define OUTPUTPREVIEWDIALOG_H
#include "pdfdocument.h"
#include "pdfdrawwidget.h"
#include "pdftransparencyrenderer.h"
#include "outputpreviewwidget.h"
#include <QDialog>
#include <QFuture>
#include <QFutureWatcher>
namespace Ui
{
class OutputPreviewDialog;
}
namespace pdfplugin
{
class OutputPreviewDialog : public QDialog
{
Q_OBJECT
public:
explicit OutputPreviewDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent);
virtual ~OutputPreviewDialog() override;
virtual void resizeEvent(QResizeEvent* event) override;
virtual void closeEvent(QCloseEvent* event) override;
virtual void showEvent(QShowEvent* event) override;
virtual void accept() override;
virtual void reject() override;
private:
void updateInks();
void updatePaperColorWidgets();
void updateAlarmColorButtonIcon();
void onPaperColorChanged();
void onAlarmColorButtonClicked();
void onSimulateSeparationsChecked(bool checked);
void onSimulatePaperColorChecked(bool checked);
void onDisplayModeChanged();
void onInksChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
void onInkCoverageLimitChanged(double value);
void onRichBlackLimtiChanged(double value);
struct RenderedImage
{
QImage image;
pdf::PDFFloatBitmapWithColorSpace originalProcessImage;
QSizeF pageSize;
QList<pdf::PDFRenderError> errors;
};
void updatePageImage();
void onPageImageRendered();
RenderedImage renderPage(const pdf::PDFPage* page,
QSize renderSize,
pdf::PDFRGB paperColor,
uint32_t activeColorMask,
pdf::PDFTransparencyRendererSettings::Flags additionalFlags);
bool isRenderingDone() const;
Ui::OutputPreviewDialog* ui;
pdf::PDFInkMapper m_inkMapper;
pdf::PDFInkMapper m_inkMapperForRendering;
const pdf::PDFDocument* m_document;
pdf::PDFWidget* m_widget;
bool m_needUpdateImage;
OutputPreviewWidget* m_outputPreviewWidget;
QFuture<RenderedImage> m_future;
QFutureWatcher<RenderedImage>* m_futureWatcher;
};
} // namespace pdf
#endif // OUTPUTPREVIEWDIALOG_H

View File

@@ -0,0 +1,351 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OutputPreviewDialog</class>
<widget class="QDialog" name="OutputPreviewDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>955</width>
<height>686</height>
</rect>
</property>
<property name="windowTitle">
<string>Output Preview</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,1">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="frameViewLayout">
<property name="spacing">
<number>0</number>
</property>
<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="QScrollBar" name="pageIndexScrollBar">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="settingsGroupBox">
<property name="title">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QLabel" name="redLabel">
<property name="text">
<string>Red</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="blueLabel">
<property name="text">
<string>Blue</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="greenLabel">
<property name="text">
<string>Green</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="simulateSeparationsCheckBox">
<property name="text">
<string>Simulate separations</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="simulatePaperColorCheckBox">
<property name="text">
<string>Simulate paper color</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="redPaperColorEdit">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="greenPaperColorEdit">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QDoubleSpinBox" name="bluePaperColorEdit">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="displayGroupBox">
<property name="title">
<string>Display</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="displayImagesCheckBox">
<property name="text">
<string>Images</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="displayTextCheckBox">
<property name="text">
<string>Text</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="displayVectorGraphicsCheckBox">
<property name="text">
<string>Vector graphics</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="displayShadingCheckBox">
<property name="text">
<string>Shading patterns</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="displayTilingPatternsCheckBox">
<property name="text">
<string>Tiling patterns</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="displayModeComboBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="colorWarningsGroupBox">
<property name="title">
<string>Color Warnings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0">
<widget class="QLabel" name="alarmColorLabel">
<property name="text">
<string>Alarm color</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="inkCoverageLimitEdit">
<property name="suffix">
<string> %</string>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>10.000000000000000</double>
</property>
<property name="value">
<double>300.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="coverageLimitLabel">
<property name="text">
<string>Coverage limit</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="alarmColorButton">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="richBlackLimitEdit">
<property name="suffix">
<string> %</string>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="richBlackLabel">
<property name="text">
<string>Rich black limit</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="inksGroupBox">
<property name="title">
<string>Inks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeWidget" name="inksTreeWidget">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<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>OutputPreviewDialog</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>OutputPreviewDialog</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>

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#include "outputpreviewplugin.h"
#include "outputpreviewdialog.h"
#include "inkcoveragedialog.h"
#include "pdfdrawwidget.h"
#include <QAction>
namespace pdfplugin
{
OutputPreviewPlugin::OutputPreviewPlugin() :
pdf::PDFPlugin(nullptr),
m_outputPreviewAction(nullptr),
m_inkCoverageAction(nullptr)
{
}
void OutputPreviewPlugin::setWidget(pdf::PDFWidget* widget)
{
Q_ASSERT(!m_widget);
BaseClass::setWidget(widget);
m_outputPreviewAction = new QAction(QIcon(":/pdfplugins/outputpreview/preview.svg"), tr("Output Preview"), this);
m_outputPreviewAction->setObjectName("actionOutputPreview_OutputPreview");
m_inkCoverageAction = new QAction(QIcon(":/pdfplugins/outputpreview/ink-coverage.svg"), tr("Ink Coverage"), this);
m_inkCoverageAction->setObjectName("actionOutputPreview_InkCoverage");
connect(m_outputPreviewAction, &QAction::triggered, this, &OutputPreviewPlugin::onOutputPreviewTriggered);
connect(m_inkCoverageAction, &QAction::triggered, this, &OutputPreviewPlugin::onInkCoverageTriggered);
updateActions();
}
void OutputPreviewPlugin::setDocument(const pdf::PDFModifiedDocument& document)
{
BaseClass::setDocument(document);
if (document.hasReset())
{
updateActions();
}
}
std::vector<QAction*> OutputPreviewPlugin::getActions() const
{
return { m_outputPreviewAction, m_inkCoverageAction };
}
void OutputPreviewPlugin::onOutputPreviewTriggered()
{
OutputPreviewDialog dialog(m_document, m_widget, m_widget);
dialog.exec();
}
void OutputPreviewPlugin::onInkCoverageTriggered()
{
InkCoverageDialog dialog(m_document, m_widget, m_widget);
dialog.exec();
}
void OutputPreviewPlugin::updateActions()
{
m_outputPreviewAction->setEnabled(m_widget && m_document);
m_inkCoverageAction->setEnabled(m_widget && m_document);
}
}

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#ifndef OUTPUTPREVIEWPLUGIN_H
#define OUTPUTPREVIEWPLUGIN_H
#include "pdfplugin.h"
#include <QObject>
namespace pdfplugin
{
class OutputPreviewPlugin : public pdf::PDFPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "PDF4QT.OutputPreviewPlugin" FILE "OutputPreviewPlugin.json")
private:
using BaseClass = pdf::PDFPlugin;
public:
OutputPreviewPlugin();
virtual void setWidget(pdf::PDFWidget* widget) override;
virtual void setDocument(const pdf::PDFModifiedDocument& document) override;
virtual std::vector<QAction*> getActions() const override;
private:
void onOutputPreviewTriggered();
void onInkCoverageTriggered();
void updateActions();
QAction* m_outputPreviewAction;
QAction* m_inkCoverageAction;
};
} // namespace pdfplugin
#endif // OUTPUTPREVIEWPLUGIN_H

View File

@@ -0,0 +1,946 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#include "outputpreviewwidget.h"
#include "pdfwidgetutils.h"
#include <QPainter>
#include <QMouseEvent>
#include <QFontMetrics>
namespace pdfplugin
{
OutputPreviewWidget::OutputPreviewWidget(QWidget* parent) :
BaseClass(parent),
m_inkMapper(nullptr),
m_displayMode(Separations),
m_alarmColor(Qt::red),
m_inkCoverageLimit(3.0),
m_richBlackLimit(1.0)
{
setMouseTracking(true);
}
QSize OutputPreviewWidget::sizeHint() const
{
return pdf::PDFWidgetUtils::scaleDPI(this, QSize(500, 300));
}
QSize OutputPreviewWidget::minimumSizeHint() const
{
return pdf::PDFWidgetUtils::scaleDPI(this, QSize(400, 300));
}
void OutputPreviewWidget::clear()
{
m_pageImage = QImage();
m_originalProcessBitmap = pdf::PDFFloatBitmapWithColorSpace();
m_pageSizeMM = QSizeF();
m_infoBoxItems.clear();
m_imagePointUnderCursor = std::nullopt;
m_inkCoverageMM.dirty();
m_alarmCoverageImage.dirty();
m_alarmRichBlackImage.dirty();
m_inkCoverageImage.dirty();
m_shapeMask.dirty();
m_opacityMask.dirty();
update();
}
void OutputPreviewWidget::setPageImage(QImage image, pdf::PDFFloatBitmapWithColorSpace originalProcessBitmap, QSizeF pageSizeMM)
{
m_pageImage = qMove(image);
m_originalProcessBitmap = qMove(originalProcessBitmap);
m_pageSizeMM = pageSizeMM;
if (m_imagePointUnderCursor.has_value())
{
QPoint point = m_imagePointUnderCursor.value();
if (point.x() >= image.width() || point.y() >= image.height())
{
m_imagePointUnderCursor = std::nullopt;
}
}
m_inkCoverageMM.dirty();
m_alarmCoverageImage.dirty();
m_alarmRichBlackImage.dirty();
m_inkCoverageImage.dirty();
m_shapeMask.dirty();
m_opacityMask.dirty();
buildInfoBoxItems();
update();
}
void OutputPreviewWidget::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
Q_UNUSED(event);
QRect rect = this->rect();
painter.fillRect(rect, Qt::gray);
QRect contentRect = getContentRect();
QRect pageImageRect = getPageImageRect(contentRect);
if (pageImageRect.isValid())
{
painter.save();
painter.setClipRect(pageImageRect, Qt::IntersectClip);
switch (m_displayMode)
{
case Separations:
{
if (!m_pageImage.isNull())
{
painter.translate(0, (pageImageRect.height() - m_pageImage.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), m_pageImage);
}
break;
}
case ColorWarningInkCoverage:
{
const AlarmImageInfo& alarmImage = getAlarmCoverageImage();
if (!alarmImage.image.isNull())
{
painter.translate(0, (pageImageRect.height() - alarmImage.image.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), alarmImage.image);
}
break;
}
case ColorWarningRichBlack:
{
const AlarmImageInfo& image = getAlarmRichBlackImage();
if (!image.image.isNull())
{
painter.translate(0, (pageImageRect.height() - image.image.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), image.image);
}
break;
}
case InkCoverage:
{
const InkCoverageInfo& image = getInkCoverageInfo();
if (!image.image.isNull())
{
painter.translate(0, (pageImageRect.height() - image.image.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), image.image);
}
break;
}
case ShapeChannel:
{
const QImage& image = getShapeImage();
if (!image.isNull())
{
painter.translate(0, (pageImageRect.height() - image.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), image);
}
break;
}
case OpacityChannel:
{
const QImage& image = getOpacityImage();
if (!image.isNull())
{
painter.translate(0, (pageImageRect.height() - image.height()) / 2);
painter.drawImage(pageImageRect.topLeft(), image);
}
break;
}
default:
Q_ASSERT(false);
}
painter.restore();
}
if (!m_infoBoxItems.empty())
{
painter.save();
int infoBoxWidth = getInfoBoxWidth();
int itemHorizontalMargin = getInfoBoxContentHorizontalMargin();
QRect infoBoxRect = contentRect;
infoBoxRect.setLeft(infoBoxRect.right() - infoBoxWidth);
painter.setPen(Qt::black);
painter.setBrush(QBrush(Qt::white));
painter.drawRect(infoBoxRect);
painter.setClipRect(infoBoxRect, Qt::IntersectClip);
painter.setBrush(Qt::NoBrush);
QFontMetrics fontMetrics(painter.font(), painter.device());
QRect rowRect = infoBoxRect;
rowRect.setHeight(fontMetrics.lineSpacing());
for (const auto& infoBoxItem : m_infoBoxItems)
{
switch (infoBoxItem.style)
{
case pdfplugin::OutputPreviewWidget::Header:
{
painter.save();
QFont font = painter.font();
font.setBold(true);
painter.setFont(font);
painter.drawText(rowRect, Qt::AlignCenter | Qt::TextSingleLine, infoBoxItem.caption);
painter.restore();
break;
}
case pdfplugin::OutputPreviewWidget::Separator:
break;
case pdfplugin::OutputPreviewWidget::ColoredItem:
{
QRect cellRect = rowRect.marginsRemoved(QMargins(itemHorizontalMargin, 0, itemHorizontalMargin, 0));
if (infoBoxItem.color.isValid())
{
QRect ellipseRect = cellRect;
ellipseRect.setWidth(ellipseRect.height());
cellRect.setLeft(ellipseRect.right() + 1);
painter.save();
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(infoBoxItem.color));
painter.drawEllipse(ellipseRect);
painter.restore();
}
painter.drawText(cellRect, Qt::AlignVCenter | Qt::AlignLeft | Qt::TextSingleLine, infoBoxItem.caption);
painter.drawText(cellRect, Qt::AlignVCenter | Qt::AlignRight | Qt::TextSingleLine, infoBoxItem.value);
break;
}
case pdfplugin::OutputPreviewWidget::ColorOnly:
{
QRect cellRect = rowRect.marginsRemoved(QMargins(itemHorizontalMargin, 0, itemHorizontalMargin, 0));
QPoint center = cellRect.center();
cellRect.setWidth(cellRect.width() / 4);
cellRect.moveCenter(center);
painter.fillRect(cellRect, infoBoxItem.color);
break;
}
default:
Q_ASSERT(false);
break;
}
rowRect.translate(0, rowRect.height());
}
rowRect.translate(0, rowRect.height());
painter.restore();
if (m_displayMode == InkCoverage)
{
const InkCoverageInfo& info = getInkCoverageInfo();
if (info.colorScale.isValid())
{
painter.save();
int rowHeight = rowRect.height();
QRect colorScaleRect = rowRect;
colorScaleRect.setBottom(contentRect.bottom());
const int maxRows = colorScaleRect.height() / rowHeight;
int rows = maxRows;
if (rows > 6)
{
painter.save();
QFont font = painter.font();
font.setBold(true);
painter.setFont(font);
painter.drawText(rowRect, Qt::AlignCenter | Qt::TextSingleLine, tr("Distribution"));
rowRect.translate(0, rowRect.height());
colorScaleRect.setTop(rowRect.top());
painter.restore();
--rows;
const pdf::PDFColorScale& colorScale = info.colorScale;
pdf::PDFLinearInterpolation<qreal> interpolation(0, rows - 1, colorScale.getMax(), colorScale.getMin());
QRect colorRect = colorScaleRect;
colorRect.setLeft(colorScaleRect.left() + colorScaleRect.width() / 3);
colorRect.setWidth(colorScaleRect.width() / 3);
colorRect.setHeight(rowHeight);
colorRect.translate(0, rowHeight / 2);
QRect textRect = rowRect;
textRect.setLeft(colorRect.right() + colorRect.height() / 2);
QLocale locale;
qreal colorScaleTop = colorRect.top();
qreal colorScaleBottom = colorRect.bottom();
for (int i = 0; i < rows; ++i)
{
if (i < rows - 1)
{
QLinearGradient gradient(0, colorRect.top(), 0, colorRect.bottom());
gradient.setColorAt(0, colorScale.map(interpolation(i)));
gradient.setColorAt(1, colorScale.map(interpolation(i + 1)));
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(gradient));
painter.drawRect(colorRect);
colorScaleBottom = colorRect.bottom();
}
pdf::PDFReal value = interpolation(i) * 100.0;
QPointF point2 = (textRect.topLeft() + textRect.bottomLeft()) * 0.5;
point2.rx() -= rowHeight / 4;
QPointF point1 = point2;
point1.rx() -= rowHeight / 4;
painter.setPen(Qt::black);
painter.drawLine(point1, point2);
painter.drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft | Qt::TextSingleLine, QString("%1 %").arg(locale.toString(value, 'f', 2)));
colorRect.translate(0, colorRect.height());
textRect.translate(0, textRect.height());
}
if (m_imagePointUnderCursor.has_value())
{
pdf::PDFLinearInterpolation<qreal> inverseInterpolation(colorScale.getMax(), colorScale.getMin(), colorScaleTop, colorScaleBottom);
pdf::PDFColorComponent coverage = m_originalProcessBitmap.getPixelInkCoverage(m_imagePointUnderCursor->x(), m_imagePointUnderCursor->y());
qreal yCoordinate = inverseInterpolation(coverage);
const int triangleRight = colorRect.left();
const int triangleLeft = triangleRight - colorRect.height();
const int halfHeight = (triangleRight - triangleLeft) * 0.5;
QPolygonF triangle;
triangle << QPointF(triangleLeft, yCoordinate - halfHeight);
triangle << QPointF(triangleRight, yCoordinate);
triangle << QPointF(triangleLeft, yCoordinate + halfHeight);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(Qt::red));
painter.drawConvexPolygon(triangle);
QString textCoverage = QString("%1 %").arg(locale.toString(coverage * 100.0, 'f', 2));
const int textRight = triangleLeft - rowHeight / 4;
const int textWidth = painter.fontMetrics().horizontalAdvance(textCoverage);
const int textStart = textRight - textWidth;
QRect currentTextRect(textStart, yCoordinate - halfHeight, textWidth + 1, rowHeight);
painter.setPen(Qt::black);
painter.drawText(currentTextRect, Qt::AlignVCenter | Qt::AlignLeft | Qt::TextSingleLine, textCoverage);
}
}
painter.restore();
}
}
}
}
QMargins OutputPreviewWidget::getDrawMargins() const
{
const int horizontalMargin = pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
const int verticalMargin = pdf::PDFWidgetUtils::scaleDPI_y(this, 5);
return QMargins(horizontalMargin, verticalMargin, horizontalMargin, verticalMargin);
}
QRect OutputPreviewWidget::getContentRect() const
{
QRect rect = this->rect();
QRect contentRect = rect.marginsRemoved(getDrawMargins());
return contentRect;
}
QRect OutputPreviewWidget::getPageImageRect(QRect contentRect) const
{
int infoBoxWidth = getInfoBoxWidth();
if (infoBoxWidth > 0)
{
infoBoxWidth += pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
}
contentRect.setRight(contentRect.right() - infoBoxWidth);
return contentRect;
}
int OutputPreviewWidget::getInfoBoxWidth() const
{
if (m_infoBoxItems.empty())
{
return 0;
}
return pdf::PDFWidgetUtils::scaleDPI_x(this, 200);
}
int OutputPreviewWidget::getInfoBoxContentHorizontalMargin() const
{
return pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
}
void OutputPreviewWidget::buildInfoBoxItems()
{
m_infoBoxItems.clear();
QColor sampleColor;
switch (m_displayMode)
{
case Separations:
case ColorWarningInkCoverage:
case ColorWarningRichBlack:
case InkCoverage:
{
if (m_originalProcessBitmap.getWidth() > 0 && m_originalProcessBitmap.getHeight() > 0)
{
const pdf::PDFPixelFormat pixelFormat = m_originalProcessBitmap.getPixelFormat();
std::vector<pdf::PDFInkMapper::ColorInfo> separations = m_inkMapper->getSeparations(pixelFormat.getProcessColorChannelCount(), true);
QStringList colorValues;
colorValues.reserve(pixelFormat.getColorChannelCount());
Q_ASSERT(pixelFormat.getColorChannelCount() == separations.size());
std::vector<QColor> inkColors;
if (m_imagePointUnderCursor.has_value())
{
QPoint point = m_imagePointUnderCursor.value();
Q_ASSERT(point.x() >= 0);
Q_ASSERT(point.x() < m_originalProcessBitmap.getWidth());
Q_ASSERT(point.y() >= 0);
Q_ASSERT(point.y() < m_originalProcessBitmap.getHeight());
pdf::PDFColorBuffer buffer = m_originalProcessBitmap.getPixel(point.x(), point.y());
for (int i = 0; i < pixelFormat.getColorChannelCount(); ++i)
{
const pdf::PDFColorComponent color = buffer[i] * 100.0f;
const int percent = qRound(color);
colorValues << QString("%1 %").arg(percent);
QColor inkColor = separations[i].color;
if (inkColor.isValid())
{
inkColor.setAlphaF(buffer[i]);
inkColors.push_back(inkColor);
}
}
Q_ASSERT(point.x() >= 0);
Q_ASSERT(point.x() < m_pageImage.width());
Q_ASSERT(point.y() >= 0);
Q_ASSERT(point.y() < m_pageImage.height());
sampleColor = m_pageImage.pixelColor(point);
}
else
{
for (int i = 0; i < pixelFormat.getColorChannelCount(); ++i)
{
colorValues << QString();
}
}
// Count process/spot inks
int processInks = 0;
int spotInks = 0;
for (const auto& colorInfo : separations)
{
if (!colorInfo.isSpot)
{
++processInks;
}
else
{
++spotInks;
}
}
int colorValueIndex = 0;
if (processInks > 0)
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Process Inks"));
for (const auto& colorInfo : separations)
{
if (colorInfo.isSpot)
{
continue;
}
addInfoBoxColoredItem(colorInfo.color, colorInfo.textName, colorValues[colorValueIndex++]);
}
}
if (spotInks > 0)
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Spot Inks"));
for (const auto& colorInfo : separations)
{
if (!colorInfo.isSpot)
{
continue;
}
addInfoBoxColoredItem(colorInfo.color, colorInfo.textName, colorValues[colorValueIndex++]);
}
}
}
break;
}
case ShapeChannel:
case OpacityChannel:
break;
default:
Q_ASSERT(false);
break;
}
if (m_displayMode == ColorWarningInkCoverage)
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Warning | Ink Coverage"));
QLocale locale;
const auto& alarmImage = getAlarmCoverageImage();
addInfoBoxColoredItem(Qt::green, tr("OK"), QString("%1 mm²").arg(locale.toString(alarmImage.areaValid, 'f', 2)));
addInfoBoxColoredItem(Qt::red, tr("Failure"), QString("%1 mm²").arg(locale.toString(alarmImage.areaInvalid, 'f', 2)));
}
if (m_displayMode == ColorWarningRichBlack)
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Warning | Rich Black"));
QLocale locale;
const auto& alarmImage = getAlarmRichBlackImage();
addInfoBoxColoredItem(Qt::green, tr("OK"), QString("%1 mm²").arg(locale.toString(alarmImage.areaValid, 'f', 2)));
addInfoBoxColoredItem(Qt::red, tr("Failure"), QString("%1 mm²").arg(locale.toString(alarmImage.areaInvalid, 'f', 2)));
}
if (m_displayMode == Separations || m_displayMode == InkCoverage)
{
if (m_originalProcessBitmap.getWidth() > 0 && m_originalProcessBitmap.getHeight() > 0)
{
const pdf::PDFPixelFormat pixelFormat = m_originalProcessBitmap.getPixelFormat();
std::vector<pdf::PDFInkMapper::ColorInfo> separations = m_inkMapper->getSeparations(pixelFormat.getProcessColorChannelCount(), true);
const std::vector<pdf::PDFColorComponent>& inkCoverage = getInkCoverage();
if (!inkCoverage.empty() && inkCoverage.size() == separations.size())
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Ink Coverage"));
QLocale locale;
for (size_t i = 0; i < inkCoverage.size(); ++i)
{
const pdf::PDFColorComponent area = inkCoverage[i];
const QColor separationColor = separations[i].color;
const QString& name = separations[i].textName;
addInfoBoxColoredItem(separationColor, name, QString("%1 mm²").arg(locale.toString(area, 'f', 2)));
}
}
}
}
if (m_displayMode == ShapeChannel || m_displayMode == OpacityChannel)
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Shape/Opacity"));
}
if (sampleColor.isValid())
{
addInfoBoxSeparator();
addInfoBoxHeader(tr("Sample Color"));
addInfoBoxColoredRect(sampleColor);
}
}
void OutputPreviewWidget::addInfoBoxHeader(QString caption)
{
m_infoBoxItems.push_back(InfoBoxItem(Header, QColor(), caption, QString()));
}
void OutputPreviewWidget::addInfoBoxSeparator()
{
if (!m_infoBoxItems.empty())
{
m_infoBoxItems.push_back(InfoBoxItem(Separator, QColor(), QString(), QString()));
}
}
void OutputPreviewWidget::addInfoBoxColoredItem(QColor color, QString caption, QString value)
{
m_infoBoxItems.push_back(InfoBoxItem(ColoredItem, color, caption, value));
}
void OutputPreviewWidget::addInfoBoxColoredRect(QColor color)
{
m_infoBoxItems.push_back(InfoBoxItem(ColorOnly, color, QString(), QString()));
}
const std::vector<pdf::PDFColorComponent>& OutputPreviewWidget::getInkCoverage() const
{
return m_inkCoverageMM.get(this, &OutputPreviewWidget::getInkCoverageImpl);
}
const OutputPreviewWidget::AlarmImageInfo& OutputPreviewWidget::getAlarmCoverageImage() const
{
return m_alarmCoverageImage.get(this, &OutputPreviewWidget::getAlarmCoverageImageImpl);
}
const OutputPreviewWidget::AlarmImageInfo& OutputPreviewWidget::getAlarmRichBlackImage() const
{
return m_alarmRichBlackImage.get(this, &OutputPreviewWidget::getAlarmRichBlackImageImpl);
}
const OutputPreviewWidget::InkCoverageInfo& OutputPreviewWidget::getInkCoverageInfo() const
{
return m_inkCoverageImage.get(this, &OutputPreviewWidget::getInkCoverageInfoImpl);
}
const QImage& OutputPreviewWidget::getShapeImage() const
{
return m_shapeMask.get(this, &OutputPreviewWidget::getShapeImageImpl);
}
const QImage& OutputPreviewWidget::getOpacityImage() const
{
return m_opacityMask.get(this, &OutputPreviewWidget::getOpacityImageImpl);
}
std::vector<pdf::PDFColorComponent> OutputPreviewWidget::getInkCoverageImpl() const
{
std::vector<pdf::PDFColorComponent> result;
if (m_originalProcessBitmap.getWidth() > 0 && m_originalProcessBitmap.getHeight() > 0)
{
pdf::PDFPixelFormat pixelFormat = m_originalProcessBitmap.getPixelFormat();
pdf::PDFColorComponent totalArea = m_pageSizeMM.width() * m_pageSizeMM.height();
pdf::PDFColorComponent pixelArea = totalArea / pdf::PDFColorComponent(m_originalProcessBitmap.getWidth() * m_originalProcessBitmap.getHeight());
const uint8_t colorChannelCount = pixelFormat.getColorChannelCount();
result.resize(colorChannelCount, 0.0f);
for (size_t y = 0; y < m_originalProcessBitmap.getHeight(); ++y)
{
for (size_t x = 0; x < m_originalProcessBitmap.getWidth(); ++x)
{
const pdf::PDFConstColorBuffer buffer = m_originalProcessBitmap.getPixel(x, y);
const pdf::PDFColorComponent alpha = pixelFormat.hasOpacityChannel() ? buffer[pixelFormat.getOpacityChannelIndex()] : 1.0f;
for (uint8_t i = 0; i < colorChannelCount; ++i)
{
result[i] += buffer[i] * alpha;
}
}
}
for (uint8_t i = 0; i < colorChannelCount; ++i)
{
result[i] *= pixelArea;
}
}
return result;
}
OutputPreviewWidget::AlarmImageInfo OutputPreviewWidget::getAlarmCoverageImageImpl() const
{
AlarmImageInfo alarmImage;
alarmImage.image = m_pageImage;
alarmImage.areaValid = 0.0f;
alarmImage.areaInvalid = 0.0f;
const int width = alarmImage.image.width();
const int height = alarmImage.image.height();
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
pdf::PDFColorComponent inkCoverage = m_originalProcessBitmap.getPixelInkCoverage(x, y);
if (inkCoverage > m_inkCoverageLimit)
{
alarmImage.areaInvalid += 1.0f;
alarmImage.image.setPixelColor(x, y, m_alarmColor);
}
else if (!qFuzzyIsNull(inkCoverage))
{
alarmImage.areaValid += 1.0f;
}
}
}
if (width > 0 && height > 0)
{
const pdf::PDFColorComponent factor = m_pageSizeMM.width() * m_pageSizeMM.height() / (pdf::PDFColorComponent(width) * pdf::PDFColorComponent(height));
alarmImage.areaValid *= factor;
alarmImage.areaInvalid *= factor;
}
return alarmImage;
}
OutputPreviewWidget::AlarmImageInfo OutputPreviewWidget::getAlarmRichBlackImageImpl() const
{
AlarmImageInfo alarmImage;
alarmImage.image = m_pageImage;
alarmImage.areaValid = 0.0f;
alarmImage.areaInvalid = 0.0f;
const pdf::PDFPixelFormat pixelFormat = m_originalProcessBitmap.getPixelFormat();
if (pixelFormat.getProcessColorChannelCount() == 4)
{
const int width = alarmImage.image.width();
const int height = alarmImage.image.height();
const uint8_t blackChannelIndex = pixelFormat.getProcessColorChannelIndexStart() + 3;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
pdf::PDFConstColorBuffer buffer = m_originalProcessBitmap.getPixel(x, y);
pdf::PDFColorComponent blackInk = buffer[blackChannelIndex];
pdf::PDFColorComponent inkCoverage = m_originalProcessBitmap.getPixelInkCoverage(x, y);
pdf::PDFColorComponent inkCoverageWithoutBlack = inkCoverage - blackInk;
if (blackInk > m_richBlackLimit && !qFuzzyIsNull(inkCoverageWithoutBlack))
{
alarmImage.areaInvalid += 1.0f;
alarmImage.image.setPixelColor(x, y, m_alarmColor);
}
else if (!qFuzzyIsNull(inkCoverage))
{
alarmImage.areaValid += 1.0f;
}
}
}
if (width > 0 && height > 0)
{
const pdf::PDFColorComponent factor = m_pageSizeMM.width() * m_pageSizeMM.height() / (pdf::PDFColorComponent(width) * pdf::PDFColorComponent(height));
alarmImage.areaValid *= factor;
alarmImage.areaInvalid *= factor;
}
}
return alarmImage;
}
OutputPreviewWidget::InkCoverageInfo OutputPreviewWidget::getInkCoverageInfoImpl() const
{
InkCoverageInfo coverageInfo;
coverageInfo.minValue = 0.0f;
coverageInfo.maxValue = 1.0f;
pdf::PDFFloatBitmap inkCoverageBitmap = m_originalProcessBitmap.getInkCoverageBitmap();
int width = int(inkCoverageBitmap.getWidth());
int height = int(inkCoverageBitmap.getHeight());
if (width > 0 && height > 0)
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
pdf::PDFColorBuffer buffer = inkCoverageBitmap.getPixel(x, y);
const pdf::PDFColorComponent coverage = buffer[0];
coverageInfo.maxValue = qMax(coverage, coverageInfo.maxValue);
}
}
coverageInfo.colorScale = pdf::PDFColorScale(coverageInfo.minValue, coverageInfo.maxValue);
coverageInfo.image = QImage(width, height, QImage::Format_RGBX8888);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
pdf::PDFColorBuffer buffer = inkCoverageBitmap.getPixel(x, y);
const pdf::PDFColorComponent coverage = buffer[0];
coverageInfo.image.setPixelColor(x, y, coverageInfo.colorScale.map(coverage));
}
}
}
return coverageInfo;
}
QImage OutputPreviewWidget::getShapeImageImpl() const
{
if (!m_originalProcessBitmap.getPixelFormat().hasShapeChannel())
{
return QImage();
}
return m_originalProcessBitmap.getChannelImage(m_originalProcessBitmap.getPixelFormat().getShapeChannelIndex());
}
QImage OutputPreviewWidget::getOpacityImageImpl() const
{
if (!m_originalProcessBitmap.getPixelFormat().hasOpacityChannel())
{
return QImage();
}
return m_originalProcessBitmap.getChannelImage(m_originalProcessBitmap.getPixelFormat().getOpacityChannelIndex());
}
pdf::PDFColorComponent OutputPreviewWidget::getRichBlackLimit() const
{
return m_richBlackLimit;
}
void OutputPreviewWidget::setRichBlackLimit(pdf::PDFColorComponent richBlackLimit)
{
if (m_richBlackLimit != richBlackLimit)
{
m_richBlackLimit = richBlackLimit;
m_alarmRichBlackImage.dirty();
buildInfoBoxItems();
update();
}
}
pdf::PDFColorComponent OutputPreviewWidget::getInkCoverageLimit() const
{
return m_inkCoverageLimit;
}
void OutputPreviewWidget::setInkCoverageLimit(pdf::PDFColorComponent inkCoverageLimit)
{
if (m_inkCoverageLimit != inkCoverageLimit)
{
m_inkCoverageLimit = inkCoverageLimit;
m_alarmCoverageImage.dirty();
buildInfoBoxItems();
update();
}
}
OutputPreviewWidget::DisplayMode OutputPreviewWidget::getDisplayMode() const
{
return m_displayMode;
}
void OutputPreviewWidget::setDisplayMode(const DisplayMode& displayMode)
{
if (m_displayMode != displayMode)
{
m_displayMode = displayMode;
buildInfoBoxItems();
update();
}
}
QColor OutputPreviewWidget::getAlarmColor() const
{
return m_alarmColor;
}
void OutputPreviewWidget::setAlarmColor(const QColor& alarmColor)
{
if (m_alarmColor != alarmColor)
{
m_alarmColor = alarmColor;
m_alarmCoverageImage.dirty();
m_alarmRichBlackImage.dirty();
update();
}
}
const pdf::PDFInkMapper* OutputPreviewWidget::getInkMapper() const
{
return m_inkMapper;
}
void OutputPreviewWidget::setInkMapper(const pdf::PDFInkMapper* inkMapper)
{
m_inkMapper = inkMapper;
}
QSize OutputPreviewWidget::getPageImageSizeHint() const
{
return getPageImageRect(getContentRect()).size();
}
void OutputPreviewWidget::mouseMoveEvent(QMouseEvent* event)
{
m_imagePointUnderCursor = std::nullopt;
if (m_pageImage.isNull())
{
// Nothing to do...
return;
}
QPoint position = event->pos();
QRect rect = getPageImageRect(getContentRect());
if (rect.contains(position))
{
int verticalImageOffset = (rect.height() - m_pageImage.height()) / 2;
QPoint imagePoint = position - rect.topLeft() - QPoint(0, verticalImageOffset);
if (imagePoint.x() >= 0 && imagePoint.x() < m_pageImage.width() &&
imagePoint.y() >= 0 && imagePoint.y() < m_pageImage.height())
{
m_imagePointUnderCursor = imagePoint;
}
}
buildInfoBoxItems();
update();
}
} // pdfplugin

View File

@@ -0,0 +1,169 @@
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
#ifndef OUTPUTPREVIEWWIDGET_H
#define OUTPUTPREVIEWWIDGET_H
#include "pdftransparencyrenderer.h"
#include <QWidget>
namespace pdfplugin
{
class OutputPreviewWidget : public QWidget
{
Q_OBJECT
private:
using BaseClass = QWidget;
public:
explicit OutputPreviewWidget(QWidget* parent);
enum DisplayMode
{
Separations,
ColorWarningInkCoverage,
ColorWarningRichBlack,
InkCoverage,
ShapeChannel,
OpacityChannel
};
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
/// Clears all widget contents
void clear();
/// Set active image
void setPageImage(QImage image, pdf::PDFFloatBitmapWithColorSpace originalProcessBitmap, QSizeF pageSizeMM);
const pdf::PDFInkMapper* getInkMapper() const;
void setInkMapper(const pdf::PDFInkMapper* inkMapper);
/// Returns page image size hint (ideal size of page image)
QSize getPageImageSizeHint() const;
QColor getAlarmColor() const;
void setAlarmColor(const QColor& alarmColor);
DisplayMode getDisplayMode() const;
void setDisplayMode(const DisplayMode& displayMode);
pdf::PDFColorComponent getInkCoverageLimit() const;
void setInkCoverageLimit(pdf::PDFColorComponent inkCoverageLimit);
pdf::PDFColorComponent getRichBlackLimit() const;
void setRichBlackLimit(pdf::PDFColorComponent richBlackLimit);
protected:
virtual void paintEvent(QPaintEvent* event) override;
virtual void mouseMoveEvent(QMouseEvent* event) override;
private:
QMargins getDrawMargins() const;
QRect getContentRect() const;
QRect getPageImageRect(QRect contentRect) const;
int getInfoBoxWidth() const;
int getInfoBoxContentHorizontalMargin() const;
void buildInfoBoxItems();
void addInfoBoxHeader(QString caption);
void addInfoBoxSeparator();
void addInfoBoxColoredItem(QColor color, QString caption, QString value);
void addInfoBoxColoredRect(QColor color);
struct AlarmImageInfo
{
QImage image;
pdf::PDFColorComponent areaValid = 0.0f;
pdf::PDFColorComponent areaInvalid = 0.0f;
};
struct InkCoverageInfo
{
QImage image;
pdf::PDFColorComponent minValue = 0.0f;
pdf::PDFColorComponent maxValue = 0.0f;
pdf::PDFColorScale colorScale;
};
const std::vector<pdf::PDFColorComponent>& getInkCoverage() const;
const AlarmImageInfo& getAlarmCoverageImage() const;
const AlarmImageInfo& getAlarmRichBlackImage() const;
const InkCoverageInfo& getInkCoverageInfo() const;
const QImage& getShapeImage() const;
const QImage& getOpacityImage() const;
std::vector<pdf::PDFColorComponent> getInkCoverageImpl() const;
AlarmImageInfo getAlarmCoverageImageImpl() const;
AlarmImageInfo getAlarmRichBlackImageImpl() const;
InkCoverageInfo getInkCoverageInfoImpl() const;
QImage getShapeImageImpl() const;
QImage getOpacityImageImpl() const;
enum InfoBoxStyle
{
Header,
Separator,
ColoredItem,
ColorOnly
};
struct InfoBoxItem
{
InfoBoxItem(InfoBoxStyle style, QColor color, QString caption, QString value) :
style(style),
color(color),
caption(caption),
value(value)
{
}
InfoBoxStyle style = InfoBoxStyle::Separator;
QColor color;
QString caption;
QString value;
};
const pdf::PDFInkMapper* m_inkMapper;
DisplayMode m_displayMode;
std::vector<InfoBoxItem> m_infoBoxItems;
QColor m_alarmColor;
std::optional<QPoint> m_imagePointUnderCursor;
pdf::PDFColorComponent m_inkCoverageLimit;
pdf::PDFColorComponent m_richBlackLimit;
mutable pdf::PDFCachedItem<std::vector<pdf::PDFColorComponent>> m_inkCoverageMM;
mutable pdf::PDFCachedItem<AlarmImageInfo> m_alarmCoverageImage;
mutable pdf::PDFCachedItem<AlarmImageInfo> m_alarmRichBlackImage;
mutable pdf::PDFCachedItem<InkCoverageInfo> m_inkCoverageImage;
mutable pdf::PDFCachedItem<QImage> m_opacityMask;
mutable pdf::PDFCachedItem<QImage> m_shapeMask;
QImage m_pageImage;
pdf::PDFFloatBitmapWithColorSpace m_originalProcessBitmap;
QSizeF m_pageSizeMM;
};
} // pdfplugin
#endif // OUTPUTPREVIEWWIDGET_H

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#292D32" d="M18.862,11.846c-0.113-0.228-0.31-0.298-0.567-0.293
c-0.555,0.01-1.109,0.003-1.666,0.004c-0.05,0-0.098,0-0.142,0c-0.405-2.289-1.743-3.631-4.043-4.042c0-0.035,0-0.082,0-0.13
c0-0.56-0.007-1.12,0.005-1.68c0.003-0.234-0.054-0.418-0.237-0.534c-0.042-0.024-0.22-0.11-0.433,0.005
c-0.176,0.116-0.233,0.298-0.229,0.529c0.012,0.555,0.005,1.111,0.005,1.667c0,0.048,0,0.098,0,0.142
c-2.29,0.405-3.631,1.743-4.043,4.043c-0.032,0-0.08,0-0.128,0c-0.56,0-1.12,0.006-1.68-0.004c-0.242-0.005-0.43,0.058-0.544,0.254
c-0.021,0.04-0.088,0.194,0.006,0.395c0.002,0.005,0.002,0.009,0.005,0.014c0.115,0.182,0.299,0.239,0.533,0.234
c0.555-0.011,1.111-0.002,1.667-0.002c0.049,0,0.098,0,0.142,0c0.405,2.289,1.743,3.63,4.043,4.042c0,0.032,0,0.081,0,0.128
c0,0.561,0.007,1.121-0.005,1.68c-0.005,0.259,0.066,0.455,0.296,0.567c0.138,0.033,0.271,0.012,0.288,0.009
c0.244-0.11,0.319-0.312,0.314-0.576c-0.012-0.555-0.005-1.11-0.005-1.666c0-0.05,0-0.097,0-0.142
c2.291-0.406,3.632-1.743,4.043-4.042c0.033,0,0.081,0,0.128,0c0.561,0,1.121-0.009,1.68,0.002
c0.255,0.005,0.451-0.063,0.563-0.285c0.007-0.016,0.025-0.064,0.026-0.146c0-0.001,0-0.003,0-0.005
C18.884,11.967,18.878,11.911,18.862,11.846z M13.604,12.123c0.053,0.175,0.17,0.284,0.35,0.311
c0.099,0.016,0.197,0.013,0.296,0.013c0.43,0.002,0.859,0.001,1.291,0.001c-0.055,1.322-1.309,2.927-3.089,3.11
c-0.003-0.049-0.007-0.096-0.007-0.144c-0.001-0.439-0.001-0.878-0.001-1.317c0-0.138-0.021-0.269-0.125-0.371
c-0.124-0.124-0.27-0.17-0.441-0.119c-0.176,0.052-0.283,0.17-0.311,0.35c-0.015,0.098-0.012,0.197-0.012,0.295
c-0.001,0.431,0,0.86,0,1.291c-1.324-0.054-2.928-1.31-3.111-3.088c0.048-0.003,0.096-0.007,0.143-0.007
c0.438-0.001,0.878-0.001,1.318,0c0.137,0,0.268-0.022,0.371-0.126c0.124-0.124,0.168-0.271,0.119-0.442
c-0.053-0.175-0.17-0.283-0.352-0.311c-0.096-0.015-0.196-0.012-0.294-0.012c-0.43-0.002-0.859,0-1.29,0
c0.053-1.324,1.309-2.929,3.087-3.111c0.002,0.048,0.007,0.095,0.007,0.142c0.001,0.439,0.001,0.878,0.001,1.317
c-0.001,0.139,0.021,0.269,0.123,0.372c0.125,0.124,0.271,0.169,0.443,0.119c0.174-0.052,0.283-0.17,0.311-0.351
c0.013-0.096,0.012-0.196,0.012-0.294c0.001-0.43,0-0.86,0-1.3c1.505,0.125,2.944,1.448,3.112,3.098
c-0.048,0.002-0.096,0.007-0.144,0.007c-0.439,0.001-0.877,0.001-1.316,0.001c-0.139-0.001-0.269,0.021-0.372,0.124
C13.6,11.806,13.554,11.953,13.604,12.123z"/>
<g>
<path fill="#292D32" d="M22.076,17.182c0.128-0.621,0.194-1.346,0.204-2.216V9.031c-0.012-0.89-0.077-1.593-0.205-2.21
c-0.252-1.393-0.758-2.461-1.549-3.271c-0.59-0.618-1.389-1.092-2.377-1.404c-0.906-0.281-2.021-0.425-3.311-0.425l-5.68,0.002
c-5.145,0-7.439,2.293-7.439,7.438v5.689c0,5.139,2.295,7.43,7.439,7.43h5.681c1.291,0,2.403-0.145,3.312-0.424
c0.98-0.306,1.761-0.762,2.379-1.397C21.315,19.65,21.821,18.58,22.076,17.182z M18.219,20.134
c-0.643,0.284-1.444,0.446-2.452,0.496c-0.303,0.021-0.604,0.031-0.926,0.031H9.16c-4.353,0-5.821-1.466-5.821-5.812v-5.69
c0-4.323,1.441-5.79,5.715-5.811l0.107-0.021h5.68c0.33,0,0.645,0.011,0.938,0.03c1.016,0.062,1.818,0.228,2.453,0.509
c1.194,0.507,1.902,1.432,2.228,2.911c0.131,0.631,0.196,1.367,0.205,2.252v5.937c-0.009,0.889-0.074,1.624-0.205,2.254
C20.134,18.702,19.422,19.628,18.219,20.134z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB