From 91cab34664685e99c3baf50dd5a313ee965fda49 Mon Sep 17 00:00:00 2001 From: renbin Date: Mon, 13 Mar 2023 11:12:15 +0800 Subject: [PATCH 01/12] feat: Add permission config and watermark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加授权控制,解析命令行传入base64编码json格式字符串。 控制看图编辑、复制、打印权限,屏蔽删除、重命名设置。 设置看图阅读水印以及打印水印。 执行各项操作时上报操作信息。 Log: 添加授权控制及水印. Influence: 编辑 复制 打印 Change-Id: Ib69e34972b67d7982d3bc03b0bb296e9389782aa --- .tx/config | 10 + .tx/deepin.conf | 1 + libimageviewer/imageengine.cpp | 3 + libimageviewer/imageengine.h | 11 +- libimageviewer/imageviewer.cpp | 20 +- libimageviewer/service/permissionconfig.cpp | 531 ++++++++++++++++++ libimageviewer/service/permissionconfig.h | 106 ++++ libimageviewer/translations/libimageviewer.ts | 8 + .../translations/libimageviewer_es.ts | 10 +- .../translations/libimageviewer_zh_CN.ts | 8 + .../viewpanel/contents/bottomtoolbar.cpp | 45 +- .../viewpanel/scen/imagegraphicsview.cpp | 15 +- libimageviewer/viewpanel/viewpanel.cpp | 54 +- libimageviewer/viewpanel/viewpanel.h | 2 +- libimageviewer/widgets/printhelper.cpp | 58 +- 15 files changed, 848 insertions(+), 34 deletions(-) create mode 100644 .tx/config create mode 100644 libimageviewer/service/permissionconfig.cpp create mode 100644 libimageviewer/service/permissionconfig.h diff --git a/.tx/config b/.tx/config new file mode 100644 index 00000000..65d4ebb1 --- /dev/null +++ b/.tx/config @@ -0,0 +1,10 @@ +[main] +host = https://www.transifex.com +minimum_perc = 80 +mode = developer + +[o:linuxdeepin:p:libimage-viewer:r:libimage-viewer] +file_filter = src/libimageviewer/translations/libimageviewer_.ts +source_file = src/libimageviewer/translations/libimageviewer.ts +source_lang = en +type = QT diff --git a/.tx/deepin.conf b/.tx/deepin.conf index 0660d16a..c230b4f8 100644 --- a/.tx/deepin.conf +++ b/.tx/deepin.conf @@ -1 +1,2 @@ [transifex] +branch = m20 diff --git a/libimageviewer/imageengine.cpp b/libimageviewer/imageengine.cpp index d49a6a20..491ba0fb 100644 --- a/libimageviewer/imageengine.cpp +++ b/libimageviewer/imageengine.cpp @@ -15,6 +15,7 @@ #include "service/commonservice.h" #include "unionimage/imgoperate.h" #include "unionimage/unionimage.h" +#include "service/permissionconfig.h" #include "service/imagedataservice.h" class ImageEnginePrivate { @@ -32,6 +33,8 @@ class ImageEnginePrivate ImageEnginePrivate::ImageEnginePrivate(ImageEngine *parent): q_ptr(parent) { Q_Q(ImageEngine); + // 关联授权操作通知信号 + QObject::connect(PermissionConfig::instance(), &PermissionConfig::authoriseNotify, q, &ImageEngine::sigAuthoriseNotify); QThread *workerThread = new QThread(q_ptr); m_worker = new LibImgOperate(workerThread); diff --git a/libimageviewer/imageengine.h b/libimageviewer/imageengine.h index 7ad4f750..c00fb072 100644 --- a/libimageviewer/imageengine.h +++ b/libimageviewer/imageengine.h @@ -30,7 +30,6 @@ class IMAGEVIEWERSHARED_EXPORT ImageEngine : public QObject //是否是可选转的图片 bool isRotatable(const QString &path); - //根据文件路径制作md5 QString makeMD5(const QString &path); @@ -53,14 +52,14 @@ class IMAGEVIEWERSHARED_EXPORT ImageEngine : public QObject void sigGetAlbumName(const QString &path); //添加到已有相册/新建相册 void sigAddToAlbum(bool isNew, const QString &album, const QString &path); - void sigAddToAlbumWithUID(bool isNew, int UID, const QString &path); //采用UID方案的添加至相册 + void sigAddToAlbumWithUID(bool isNew, int UID, const QString &path); //采用UID方案的添加至相册 //收藏/取消收藏 void sigAddOrRemoveToFav(const QString &path, bool isAdd); //导出 void sigExport(const QString &path); //从自定义相册中移除 void sigRemoveFromCustom(const QString &path, const QString &album); - void sigRemoveFromCustomWithUID(const QString &path, int UID); //采用UID方案的从相册中删除 + void sigRemoveFromCustomWithUID(const QString &path, int UID); //采用UID方案的从相册中删除 //退出幻灯片 void exitSlideShow(); //按下ESC键 @@ -68,12 +67,14 @@ class IMAGEVIEWERSHARED_EXPORT ImageEngine : public QObject //通知旋转 void sigRotatePic(const QString &path); -private: + // 授权操作通知信号 + void sigAuthoriseNotify(const QJsonObject &data); +private: static ImageEngine *m_ImageEngine; QScopedPointer d_ptr; Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), ImageEngine) }; -#endif // IMAGEENGINE_H +#endif // IMAGEENGINE_H diff --git a/libimageviewer/imageviewer.cpp b/libimageviewer/imageviewer.cpp index cb50d4e1..0fbacd46 100644 --- a/libimageviewer/imageviewer.cpp +++ b/libimageviewer/imageviewer.cpp @@ -16,11 +16,16 @@ #include "imageengine.h" #include "viewpanel/viewpanel.h" +#include "service/permissionconfig.h" #include "service/commonservice.h" #include "service/mtpfileproxy.h" #include "unionimage/imageutils.h" #include "unionimage/baseutils.h" +#ifndef DISABLE_WATERMARK +#include +#endif + #define PLUGINTRANSPATH "/usr/share/libimageviewer/translations" class ImageViewerPrivate { @@ -36,8 +41,10 @@ class ImageViewerPrivate ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar, ImageViewer *parent) : q_ptr(parent) { - QDir dir(PLUGINTRANSPATH); + // 在界面前初始化授权配置 + PermissionConfig::instance()->initFromArguments(); + QDir dir(PLUGINTRANSPATH); if (dir.exists()) { QDirIterator qmIt(PLUGINTRANSPATH, QStringList() << QString("*%1.qm").arg(QLocale::system().name()), QDir::Files); @@ -64,7 +71,7 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer } } } -// } + Q_Q(ImageViewer); m_imgViewerType = imgViewerType; //记录当前展示模式 @@ -77,6 +84,15 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer q->setLayout(layout); m_panel = new LibViewPanel(customTopToolbar, q); layout->addWidget(m_panel); + +#ifndef DISABLE_WATERMARK + // 设置看图水印,目前仅在主要展示区域显示 + if (PermissionConfig::instance()->isValid()) { + auto data = PermissionConfig::instance()->readWaterMarkData(); + DWaterMarkHelper::instance()->setData(data); + DWaterMarkHelper::instance()->registerWidget(m_panel); + } +#endif } ImageViewer::ImageViewer(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar, QWidget *parent) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp new file mode 100644 index 00000000..16fea476 --- /dev/null +++ b/libimageviewer/service/permissionconfig.cpp @@ -0,0 +1,531 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "permissionconfig.h" +#include "imageengine.h" + +#include +#include +#include +#include + +const QString g_KeyTid = "tid"; +const QString g_KeyOperate = "operate"; +const QString g_KeyFilePath = "filePath"; +const QString g_KeyRemaining = "remainingPrintCount"; + +/** + @class AuthoriseConfig + @brief 授权控制类,提供操作授权和水印配置 + @details 授权控制主要包括编辑、拷贝、删除、保存等操作,水印包括阅读水印及打印水印。 + 配置信息通过命令行参数获取,当授权控制开启时,进行主要操作将自动发送通知信息。 + @note 此类非线程安全 + */ + +/** + @brief 构造函数,构造时即初始化配置 + */ +PermissionConfig::PermissionConfig(QObject *parent) + : QObject(parent) +{ +#ifdef DISABLE_WATERMARK + qWarning() << qPrintable("Current version is not support read watermark"); +#endif +} + +/** + @brief 析构函数 + */ +PermissionConfig::~PermissionConfig() {} + +/** + @return 返回权限控制单实例 + */ +PermissionConfig *PermissionConfig::instance() +{ + static PermissionConfig config; + return &config; +} + +/** + @return 是否允许进行权限控制,未加载授权配置时返回 false + */ +bool PermissionConfig::isValid() const +{ + return valid; +} + +/** + @return 返回当前是否为权限控制目标图片 + */ +bool PermissionConfig::isCurrentIsTargetImage() const +{ + return isValid() && currentImagePath == targetImagePath; +} + +/** + @return 是否允许编辑图片,无授权控制时默认返回 true + */ +bool PermissionConfig::isEditable(const QString &fileName) const +{ + if (checkAuthInvalid(fileName)) { + return true; + } + + return authFlags.testFlag(EnableEdit); +} + +/** + @return 是否允许复制图片,无授权控制时默认返回 true + */ +bool PermissionConfig::isCopyable(const QString &fileName) const +{ + if (checkAuthInvalid(fileName)) { + return true; + } + + return authFlags.testFlag(EnableCopy); +} + +/** + @return 是否允许删除图片,无授权控制时默认返回 true + */ +bool PermissionConfig::isDeletable() const +{ + if (checkAuthInvalid()) { + return true; + } + + return authFlags.testFlag(EnableDelete); +} + +/** + @return 是否允许重命名图片,无授权控制时默认返回 true + */ +bool PermissionConfig::isRenamable() const +{ + if (checkAuthInvalid()) { + return true; + } + + return authFlags.testFlag(EnableRename); +} + +/** + @return 是否允许切换图片,无授权控制时默认返回 true + */ +bool PermissionConfig::isSwitchable() const +{ + if (checkAuthInvalid()) { + return true; + } + + return authFlags.testFlag(EnableSwitch); +} + +/** + @return + @note -1 表示无限制;0 表示无打印次数;1~表示剩余可打印次数 + */ +bool PermissionConfig::isPrintable(const QString &fileName) const +{ + if (checkAuthInvalid(fileName)) { + return true; + } + + return !!printLimitCount; +} + +/** + @brief 触发打开文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerOpen(const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + if (NotOpen != status) { + return; + } + status = Open; + + QJsonObject data{{g_KeyTid, TidOpen}, {g_KeyOperate, "open"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 触发编辑文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerEdit(const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + QJsonObject data{{g_KeyTid, TidEdit}, {g_KeyOperate, "edit"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 触发拷贝文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerCopy(const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + QJsonObject data{{g_KeyTid, TidCopy}, {g_KeyOperate, "copy"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 触发删除文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerDelete(const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + QJsonObject data{{g_KeyTid, TidDelete}, {g_KeyOperate, "delete"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 触发重命名文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerRename(const QString &fileName) +{ + if (checkAuthInvalid()) { + return; + } + + QJsonObject data{{g_KeyTid, TidRename}, {g_KeyOperate, "rename"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 触发打印文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerPrint(const QString &fileName) +{ + if (checkAuthInvalid()) { + return; + } + + // 减少打印计数 + reduceOnePrintCount(); + QJsonObject data{{g_KeyTid, TidPrint}, {g_KeyOperate, "print"}, {g_KeyFilePath, fileName}, {g_KeyRemaining, printCount()}}; + + triggerNotify(data); +} + +/** + @brief 触发关闭文件 \a fileName ,若为限权文件,向外发送权限通知信号 + */ +void PermissionConfig::triggerClose(const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + if (Open != status) { + return; + } + status = Close; + + QJsonObject data{{"tid", TidClose}, {g_KeyOperate, "close"}, {g_KeyFilePath, fileName}}; + + triggerNotify(data); +} + +/** + @brief 返回当前剩余的打印次数 + @sa `isPrintable` + */ +int PermissionConfig::printCount() const +{ + return printLimitCount; +} + +/** + @brief 返回是否打印无限制 + */ +bool PermissionConfig::isUnlimitPrint() const +{ + if (checkAuthInvalid()) { + return true; + } + return -1 == printLimitCount; +} + +/** + @brief 减少一次打印计数并发送打印计数变更信号 `printCountChanged` + */ +void PermissionConfig::reduceOnePrintCount() +{ + if (printLimitCount > 0) { + printLimitCount--; + Q_EMIT printCountChanged(); + } else { + qWarning() << "Escape print authorise check!"; + } +} + +/** + @brief 触发权限操作通知,将向外发送Json数据,通过DBus广播 + */ +void PermissionConfig::triggerNotify(const QJsonObject &data) +{ + enum ReportMode { Broadcast = 1, Report = 2, ReportAndBroadcast = Broadcast | Report }; + QJsonObject sendData; + sendData.insert("policy", QJsonObject{{"reportMode", ReportAndBroadcast}}); + sendData.insert("info", data); + + Q_EMIT authoriseNotify(sendData); +} + +/** + @brief 设置当前处理的文件路径为 \a fileName + */ +void PermissionConfig::setCurrentImagePath(const QString &fileName) +{ + currentImagePath = fileName; +} + +/** + @return 返回当前控制的图片路径 + */ +QString PermissionConfig::targetImage() const +{ + return targetImagePath; +} + +#ifndef DISABLE_WATERMARK + +/** + @return 返回从配置中读取的阅读水印配置,用于图片展示时显示 + */ +WaterMarkData PermissionConfig::readWaterMarkData() const +{ + return readWaterMark; +} + +/** + @return 返回从配置中读取的打印水印配置,用于图片打印预览及打印时显示 + */ +WaterMarkData PermissionConfig::printWaterMarkData() const +{ + return printWaterMark; +} + +#endif + +/** + @brief 从命令行参数中取得授权配置 + @note 命令行参数示例: + deepin-image-viewer --config=[Base64 code data] /path/to/file + */ +void PermissionConfig::initFromArguments() +{ + if (valid) { + return; + } + + QString configParam = parseConfigOption(); + if (!configParam.isEmpty()) { + // 获取带权限控制的文件路径 + QStringList arguments = qApp->arguments(); + for (const QString &arg : arguments) { + QFileInfo info(arg); + QString filePath = info.absoluteFilePath(); + if (ImageEngine::instance()->isImage(filePath) || info.suffix() == "dsps") { + targetImagePath = filePath; + break; + } + } + + if (targetImagePath.isEmpty()) { + qWarning() << qPrintable("Authorise config with no target image path.") << arguments; + return; + } + + QByteArray jsonData = QByteArray::fromBase64(configParam.toUtf8()); + qInfo() << QString("Parse authorise config, data: %1").arg(QString(jsonData)); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error); + + if (!doc.isNull()) { + QJsonObject root = doc.object(); + initAuthorise(root.value("permission").toObject()); + initReadWaterMark(root.value("readWatermark").toObject()); + initPrintWaterMark(root.value("printWatermark").toObject()); + + valid = !targetImagePath.isEmpty(); + if (valid) { + // 首次触发打开图片 + triggerOpen(targetImagePath); + } + + qInfo() << qPrintable("Current Enable permission") << authFlags); + } else { + qWarning() + << QString("Parse authorise config error at pos: %1, details: %2").arg(error.offset).arg(error.errorString()); + } + } else { + qWarning() << qPrintable("Parse authorise config is empty."); + } +} + +/** + @return 解析命令行参数并返回设置的权限和水印配置,为设置则返回空 + */ +QString PermissionConfig::parseConfigOption() +{ + if (!qApp) { + qWarning() << qPrintable("Must init authorise config after QApplication initialized"); + return {}; + } + + const QString option("--config"); + const QLatin1Char assignChar('='); + enum Status { FindOption, FindAssign, FindParam }; + Status status = FindOption; + + QStringList arguments = qApp->arguments(); + for (const QString &arg : arguments) { + switch (status) { + case FindOption: + if (arg.simplified().startsWith(option)) { + int index = arg.indexOf(assignChar); + if (index < 0) { + status = FindAssign; + continue; + } else if (index == arg.length() - 1) { + status = FindParam; + continue; + } else { + return arg.mid(index + 1); + } + } + break; + case FindAssign: + if (arg.simplified().startsWith(assignChar)) { + if (arg.length() > 1) { + return arg.mid(1); + } else { + status = FindParam; + continue; + } + } else { + return {}; + } + break; + case FindParam: + return arg; + } + } + + return {}; +} + +/** + @brief 从 Json 配置 \a param 中取得授权信息 + */ +void PermissionConfig::initAuthorise(const QJsonObject ¶m) +{ + if (param.isEmpty()) { + qInfo() << qPrintable("Authorise config not contains authorise data."); + return; + } + + // 屏蔽 delete / rename ,默认无此功能 + authFlags.setFlag(EnableEdit, param.value("edit").toBool(false)); + authFlags.setFlag(EnableCopy, param.value("copy").toBool(false)); + // 默认允许切换图片,包括快捷键及 Ctrl + O + authFlags.setFlag(EnableSwitch, true); + + printLimitCount = param.value("printCount").toInt(0); +} + +/** + @brief 从 Json 配置 \a param 中取得阅读水印信息 + */ +void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) +{ + if (param.isEmpty()) { + qInfo() << qPrintable("Authorise config not contains read watermark data."); + return; + } + +#ifndef DISABLE_WATERMARK + readWaterMark.type = WaterMarkType::Text; + readWaterMark.font.setFamily(param.value("font").toString()); + readWaterMark.font.setPixelSize(param.value("fontSize").toInt()); + + QString colorName = param.value("color").toString(); + if (!colorName.startsWith('#')) { + colorName.prepend('#'); + } + readWaterMark.color.setNamedColor(colorName); + readWaterMark.opacity = param.value("opacity").toDouble() / 255; + readWaterMark.layout = param.value("layout").toInt() ? WaterMarkLayout::Tiled : WaterMarkLayout::Center; + readWaterMark.rotation = param.value("angle").toDouble(); + readWaterMark.lineSpacing = param.value("rowSpacing").toInt(); + readWaterMark.spacing = param.value("columnSpacing").toInt(); + readWaterMark.text = param.value("text").toString(); +#endif +} + +/** + @brief 从 Json 配置 \a param 中取得打印水印信息 + */ +void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) +{ + if (param.isEmpty()) { + qInfo() << qPrintable("Authorise config not contains print watermark data."); + return; + } + +#ifndef DISABLE_WATERMARK + printWaterMark.type = WaterMarkType::Text; + printWaterMark.font.setFamily(param.value("font").toString()); + printWaterMark.font.setPixelSize(param.value("fontSize").toInt()); + + QString colorName = param.value("color").toString(); + if (!colorName.startsWith('#')) { + colorName.prepend('#'); + } + printWaterMark.color.setNamedColor(colorName); + printWaterMark.opacity = param.value("opacity").toDouble() / 255; + printWaterMark.layout = param.value("layout").toInt() ? WaterMarkLayout::Tiled : WaterMarkLayout::Center; + printWaterMark.rotation = param.value("angle").toDouble(); + printWaterMark.lineSpacing = param.value("rowSpacing").toInt(); + printWaterMark.spacing = param.value("columnSpacing").toInt(); + printWaterMark.text = param.value("text").toString(); +#endif +} + +/** + @return 返回文件 \a fileName 是否需要被校验权限 + */ +bool PermissionConfig::checkAuthInvalid(const QString &fileName) const +{ + if (!isValid()) { + return true; + } + if (fileName.isEmpty()) { + return currentImagePath != targetImagePath; + } + + return fileName != targetImagePath; +} diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h new file mode 100644 index 00000000..7fc13c2b --- /dev/null +++ b/libimageviewer/service/permissionconfig.h @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef PERMISSIONCONFIG_H +#define PERMISSIONCONFIG_H + +#include +#include + +// #define DISABLE_WATERMARK + +#ifndef DISABLE_WATERMARK +#include +#include + +DWIDGET_USE_NAMESPACE +#endif + +class PermissionConfig : public QObject +{ + Q_OBJECT + Q_PROPERTY(int printCount READ printCount NOTIFY printCountChanged) + + explicit PermissionConfig(QObject *parent = nullptr); + ~PermissionConfig() override; + +public: + static PermissionConfig *instance(); + bool isValid() const; + bool isCurrentIsTargetImage() const; + + enum Authorise { + NoAuth = 0, + EnableEdit = 0x1, + EnableCopy = 0x2, + EnableDelete = 0x4, + EnableRename = 0x8, + EnableSwitch = 0x10, + }; + Q_DECLARE_FLAGS(Authorises, Authorise) + + bool isEditable(const QString &fileName = QString()) const; + bool isCopyable(const QString &fileName = QString()) const; + bool isDeletable() const; + bool isRenamable() const; + bool isSwitchable() const; + bool isPrintable(const QString &fileName = QString()) const; + + void triggerOpen(const QString &fileName); + void triggerEdit(const QString &fileName); + void triggerCopy(const QString &fileName); + void triggerDelete(const QString &fileName); + void triggerRename(const QString &fileName); + void triggerPrint(const QString &fileName); + void triggerClose(const QString &fileName); + Q_SIGNAL void authoriseNotify(const QJsonObject &data); + + int printCount() const; + bool isUnlimitPrint() const; + Q_SIGNAL void printCountChanged(); + + void setCurrentImagePath(const QString &fileName); + QString targetImage() const; + +#ifndef DISABLE_WATERMARK + WaterMarkData readWaterMarkData() const; + WaterMarkData printWaterMarkData() const; +#endif + + void initFromArguments(); + +private: + QString parseConfigOption(); + void initAuthorise(const QJsonObject ¶m); + void initReadWaterMark(const QJsonObject ¶m); + void initPrintWaterMark(const QJsonObject ¶m); + bool checkAuthInvalid(const QString &fileName = QString()) const; + void reduceOnePrintCount(); + void triggerNotify(const QJsonObject &data); + +private: + enum TidType { + TidOpen = 1000200050, + TidEdit = 1000200051, + TidCopy = 1000200052, + TidPrint = 1000200053, + TidClose = 1000200054, + TidDelete = 1000200055, + TidRename = 1000200056, + }; + enum Status { NotOpen, Open, Close }; + + QString currentImagePath; // 当前展示的图片文件路径 + QString targetImagePath; // 权限控制指向的文件路径 + bool valid = false; + int printLimitCount = -1; + Status status = NotOpen; // 被控制权限图片的状态 + Authorises authFlags = NoAuth; +#ifndef DISABLE_WATERMARK + WaterMarkData readWaterMark; + WaterMarkData printWaterMark; +#endif +}; + +#endif // PERMISSIONCONFIG_H diff --git a/libimageviewer/translations/libimageviewer.ts b/libimageviewer/translations/libimageviewer.ts index b70f2631..50d4ab9c 100644 --- a/libimageviewer/translations/libimageviewer.ts +++ b/libimageviewer/translations/libimageviewer.ts @@ -311,6 +311,14 @@ Photo info Photo info + + (Disabled) + (Disabled) + + + (Remaining %1 times) + (Remaining %1 times) + Colorize pictures diff --git a/libimageviewer/translations/libimageviewer_es.ts b/libimageviewer/translations/libimageviewer_es.ts index 18d6ef95..e9ee589f 100644 --- a/libimageviewer/translations/libimageviewer_es.ts +++ b/libimageviewer/translations/libimageviewer_es.ts @@ -240,7 +240,15 @@ Photo info - + Photo info + + + (Disabled) + (Disabled) + + + (Remaining %1 times) + (Remaining %1 times) diff --git a/libimageviewer/translations/libimageviewer_zh_CN.ts b/libimageviewer/translations/libimageviewer_zh_CN.ts index 57aa5aa7..788d29c9 100644 --- a/libimageviewer/translations/libimageviewer_zh_CN.ts +++ b/libimageviewer/translations/libimageviewer_zh_CN.ts @@ -319,6 +319,14 @@ Photo info 照片信息 + + (Disabled) + (已禁用) + + + (Remaining %1 times) + (剩余 %1 次) + Retry 重 试 diff --git a/libimageviewer/viewpanel/contents/bottomtoolbar.cpp b/libimageviewer/viewpanel/contents/bottomtoolbar.cpp index 852a14ff..1037d03c 100644 --- a/libimageviewer/viewpanel/contents/bottomtoolbar.cpp +++ b/libimageviewer/viewpanel/contents/bottomtoolbar.cpp @@ -25,6 +25,7 @@ #include "unionimage/baseutils.h" #include "unionimage/imageutils.h" #include "unionimage/unionimage.h" +#include "service/permissionconfig.h" #include "service/commonservice.h" #include "imageengine.h" #include "image-viewer_global.h" @@ -173,8 +174,10 @@ void LibBottomToolbar::setRotateBtnClicked(const bool &bRet) void LibBottomToolbar::setPictureDoBtnClicked(const bool &bRet) { + bool enableCopy = PermissionConfig::instance()->isCopyable(); + if (m_ocrIsExists && m_ocrBtn) { - m_ocrBtn->setEnabled(bRet); + m_ocrBtn->setEnabled(bRet && enableCopy); } if (m_adaptImageBtn) { m_adaptImageBtn->setEnabled(bRet); @@ -314,11 +317,12 @@ void LibBottomToolbar::deleteImage() if (m_imgListWidget->getCurrentCount() == 0) { m_preButton->setEnabled(false); } -// qDebug() << m_imgListWidget->getCurrentPath(); -// qDebug() << m_imgListWidget->getCurrentCount(); + emit removed(); //删除数据库图片 -// setIsConnectDel(true); + m_imgListWidget->moveCenterWidget(); + + PermissionConfig::instance()->triggerDelete(path); } } @@ -477,23 +481,39 @@ void LibBottomToolbar::slotOpenImage(int index, QString path) //BUG#93143 QFileInfo info(path); + PermissionConfig::instance()->setCurrentImagePath(info.absoluteFilePath()); + m_trashBtn->setVisible(!PermissionConfig::instance()->isCurrentIsTargetImage()); + + bool isCopyable = PermissionConfig::instance()->isCopyable(); + if (m_ocrIsExists) { + if (isCopyable) { + m_ocrBtn->setToolTip(QObject::tr("Extract text")); + } else { + m_ocrBtn->setToolTip(QObject::tr("Extract text") + QObject::tr("(Disabled)")); + } + } + //左转右转的控制不在这里 if (!info.isFile() || !info.exists()) { m_adaptImageBtn->setEnabled(false); m_adaptImageBtn->setChecked(false); m_adaptScreenBtn->setEnabled(false); - m_trashBtn->setEnabled(false); + m_trashBtn->setEnabled(false); if (m_ocrIsExists) { m_ocrBtn->setEnabled(false); } } else { m_adaptImageBtn->setEnabled(true); m_adaptScreenBtn->setEnabled(true); - m_trashBtn->setEnabled(true); + if (!PermissionConfig::instance()->isEditable()) { + return; + } + + m_trashBtn->setEnabled(true); if (m_ocrIsExists) { - m_ocrBtn->setEnabled(true); + m_ocrBtn->setEnabled(isCopyable); } } } @@ -791,6 +811,17 @@ void LibBottomToolbar::initUI() m_trashBtn->setIconSize(QSize(36, 36)); m_trashBtn->setToolTip(QObject::tr("Delete")); hb->addWidget(m_trashBtn); + + if (PermissionConfig::instance()->isValid() && m_imgListWidget) { + auto authIns = PermissionConfig::instance(); + + if (m_ocrBtn) { + m_ocrBtn->setEnabled(authIns->isEditable()); + } + bool isDeletable = authIns->isDeletable(); + m_trashBtn->setVisible(isDeletable); + m_trashBtn->setEnabled(isDeletable); + } } void LibBottomToolbar::initConnection() diff --git a/libimageviewer/viewpanel/scen/imagegraphicsview.cpp b/libimageviewer/viewpanel/scen/imagegraphicsview.cpp index c12cfc8c..64ebe1a3 100644 --- a/libimageviewer/viewpanel/scen/imagegraphicsview.cpp +++ b/libimageviewer/viewpanel/scen/imagegraphicsview.cpp @@ -38,6 +38,7 @@ #include "imageengine.h" #include "service/mtpfileproxy.h" #include "service/aimodelservice.h" +#include "service/permissionconfig.h" #include #include @@ -656,10 +657,7 @@ qreal LibImageGraphicsView::imageRelativeScale() const qreal LibImageGraphicsView::windowRelativeScale() const { QRectF bf = sceneRect(); - qDebug()< 1.0 * bf.width() / (bf.height())) { // if (1.0 * width() / (height() - TITLEBAR_HEIGHT * 2) > 1.0 * bf.width() / (bf.height() - TITLEBAR_HEIGHT * 2)) { @@ -965,6 +963,12 @@ void LibImageGraphicsView::slotRotatePixCurrent() pathType != imageViewerSpace::PathTypeSAFEBOX && //保险箱 pathType != imageViewerSpace::PathTypeRECYCLEBIN) { //回收站 + // 图片信息不一定同步,需做二次判断 + if (!PermissionConfig::instance()->isEditable(m_path)) { + m_rotateAngel = 0; + return; + } + disconnect(m_imgFileWatcher, &QFileSystemWatcher::fileChanged, this, &LibImageGraphicsView::onImgFileChanged); Libutils::image::rotate(m_path, m_rotateAngel); @@ -980,6 +984,9 @@ void LibImageGraphicsView::slotRotatePixCurrent() connect(m_imgFileWatcher, &QFileSystemWatcher::fileChanged, this, &LibImageGraphicsView::onImgFileChanged); }); m_rotateAngel = 0; + + // 通知授权控制图片编辑完成 + PermissionConfig::instance()->triggerEdit(m_path); } } } diff --git a/libimageviewer/viewpanel/viewpanel.cpp b/libimageviewer/viewpanel/viewpanel.cpp index 9dc90904..b99325f6 100644 --- a/libimageviewer/viewpanel/viewpanel.cpp +++ b/libimageviewer/viewpanel/viewpanel.cpp @@ -35,6 +35,7 @@ #include "widgets/renamedialog.h" #include "service/ocrinterface.h" #include "slideshow/slideshowpanel.h" +#include "service/permissionconfig.h" #include "service/configsetter.h" #include "service/imagedataservice.h" #include "service/mtpfileproxy.h" @@ -151,6 +152,9 @@ LibViewPanel::LibViewPanel(AbstractTopToolbar *customToolbar, QWidget *parent) LibViewPanel::~LibViewPanel() { + // 析构时通知图片窗口关闭 + PermissionConfig::instance()->triggerClose(m_currentPath); + // 清空图像缓存目录 Libutils::image::clearCacheImageFolder(); @@ -189,6 +193,15 @@ void LibViewPanel::loadImage(const QString &path, QStringList paths) m_dirWatcher->removePaths(m_dirWatcher->directories()); m_dirWatcher->addPath(QFileInfo(path).dir().path()); + + // 判断是否授权状态更新 + QFileInfo targetImageInfo(PermissionConfig::instance()->targetImage()); + if (info.absoluteDir() != targetImageInfo.absoluteDir()) { + if (!paths.contains(targetImageInfo.absoluteFilePath())) { + // 不含授权文件,提示文件关闭 + PermissionConfig::instance()->triggerClose(PermissionConfig::instance()->targetImage()); + } + } } void LibViewPanel::initConnect() @@ -468,8 +481,8 @@ void LibViewPanel::updateMenuContent(const QString &path) QFileInfo info(currentPath); - bool isReadable = info.isReadable() ; //是否可读 - // 判断文件是否可写和文件目录是否可写 + bool isReadable = info.isReadable(); //是否可读 + //判断文件是否可写和文件目录是否可写 bool isWritable = info.isWritable() && QFileInfo(info.dir(), info.dir().path()).isWritable(); //是否可写 bool isRotatable = ImageEngine::instance()->isRotatable(currentPath);//是否可旋转 imageViewerSpace::PathType pathType; // 路径类型 @@ -482,6 +495,10 @@ void LibViewPanel::updateMenuContent(const QString &path) imageViewerSpace::ImageType imageType = LibUnionImage_NameSpace::getImageType(currentPath);//图片类型 + auto authIns = PermissionConfig::instance(); + bool isEditable = authIns->isEditable(currentPath); + bool isCopyable = authIns->isCopyable(currentPath); + //判断是否是损坏图片 setCurrentWidget(currentPath); @@ -513,15 +530,23 @@ void LibViewPanel::updateMenuContent(const QString &path) appendAction(IdFullScreen, QObject::tr("Fullscreen"), ss("Fullscreen", "F11")); } - appendAction(IdPrint, QObject::tr("Print"), ss("Print", "Ctrl+P")); + if (authIns->isCurrentIsTargetImage() && !authIns->isUnlimitPrint()) { + appendAction(IdPrint, QObject::tr("Print") + QObject::tr("(Remaining %1 times)").arg(authIns->printCount()), + ss("Print", "Ctrl+P"), authIns->isPrintable()); + } else { + appendAction(IdPrint, QObject::tr("Print"), ss("Print", "Ctrl+P")); + } //ocr按钮,是否是动态图,todo DIconButton *OcrButton = m_bottomToolbar->getBottomtoolbarButton(imageViewerSpace::ButtonTypeOcr); if (OcrButton != nullptr) { - if (imageViewerSpace::ImageTypeDynamic != imageType && isPic && isReadable) { + if (imageViewerSpace::ImageTypeDynamic != imageType && isPic && isReadable && isCopyable) { appendAction(IdOcr, QObject::tr("Extract text"), ss("Extract text", "Alt+O")); OcrButton->setEnabled(true); } else { + if (!isCopyable) { + appendAction(IdOcr, QObject::tr("Extract text") + QObject::tr("(Disabled)"), ss("Extract text", "Alt+O"), false); + } OcrButton->setEnabled(false); } } @@ -587,11 +612,15 @@ void LibViewPanel::updateMenuContent(const QString &path) // 添加AI模型选项,仅处理静态图 addAIMenu(); - if (isAlbum && isReadable && !enhanceImage) { + if (isAlbum && isReadable && !enhanceImage && isCopyable) { appendAction(IdExport, tr("Export"), ss("Export", "Ctrl+E")); //导出 } if (isReadable) { - appendAction(IdCopy, QObject::tr("Copy"), ss("Copy", "Ctrl+C")); + if (isCopyable) { + appendAction(IdCopy, QObject::tr("Copy"), ss("Copy", "Ctrl+C")); + } else { + appendAction(IdCopy, QObject::tr("Copy") + QObject::tr("(Disabled)"), ss("Copy", "Ctrl+C"), false); + } } //如果程序有可读可写的权限,才能重命名,todo @@ -599,7 +628,7 @@ void LibViewPanel::updateMenuContent(const QString &path) if (isReadable && isWritable && imageViewerSpace::PathTypeMTP != pathType && imageViewerSpace::PathTypePTP != pathType && - imageViewerSpace::PathTypeAPPLE != pathType && !isAlbum) { + imageViewerSpace::PathTypeAPPLE != pathType && !isAlbum && authIns->isRenamable()) { appendAction(IdRename, QObject::tr("Rename"), ss("Rename", "F2")); } @@ -611,7 +640,7 @@ void LibViewPanel::updateMenuContent(const QString &path) imageViewerSpace::PathTypeRECYCLEBIN != pathType && imageViewerSpace::PathTypeMTP != pathType && imageViewerSpace::PathTypePTP != pathType && - isWritable && isReadable) || (isAlbum && isWritable)) { + isWritable && isReadable && authIns->isDeletable()) || (isAlbum && isWritable)) { if (isAlbum) { appendAction(IdMoveToTrash, QObject::tr("Delete"), ss("Throw to trash", "")); } else { @@ -761,7 +790,7 @@ void LibViewPanel::showNormal() }); } -QAction *LibViewPanel::appendAction(int id, const QString &text, const QString &shortcut) +QAction *LibViewPanel::appendAction(int id, const QString &text, const QString &shortcut, bool enable) { if (m_menu && m_menuItemDisplaySwitch.test(static_cast(id))) { QAction *ac = new QAction(m_menu); @@ -769,6 +798,7 @@ QAction *LibViewPanel::appendAction(int id, const QString &text, const QString & ac->setText(text); ac->setProperty("MenuID", id); ac->setShortcut(QKeySequence(shortcut)); + ac->setEnabled(enable); m_menu->addAction(ac); return ac; @@ -1454,6 +1484,9 @@ bool LibViewPanel::slotOcrPicture() QString path = m_bottomToolbar->getCurrentItemInfo().path; //图片过大,会导致崩溃,超过4K,智能裁剪 if (m_ocrInterface != nullptr && m_view != nullptr) { + // 提示授权控制拷贝图片 + PermissionConfig::instance()->triggerCopy(path); + QImage image = m_view->image(); if (image.width() > 2500) { image = image.scaledToWidth(2500, Qt::SmoothTransformation); @@ -1746,6 +1779,8 @@ void LibViewPanel::onMenuItemClicked(QAction *action) m_bottomToolbar->setCurrentPath(filepath); openImg(0, filepath); } + + PermissionConfig::instance()->triggerRename(oldPath); } } if (m_dirWatcher) { @@ -1769,6 +1804,7 @@ void LibViewPanel::onMenuItemClicked(QAction *action) Libutils::base::copyImageToClipboard(QStringList(m_bottomToolbar->getCurrentItemInfo().path)); } + PermissionConfig::instance()->triggerCopy(m_bottomToolbar->getCurrentItemInfo().path); break; } case IdMoveToTrash: { diff --git a/libimageviewer/viewpanel/viewpanel.h b/libimageviewer/viewpanel/viewpanel.h index da0a88ec..aa5faa9b 100644 --- a/libimageviewer/viewpanel/viewpanel.h +++ b/libimageviewer/viewpanel/viewpanel.h @@ -81,7 +81,7 @@ class LibViewPanel : public QFrame * @param text 按钮名称 * @param shortcut 按钮快捷键 */ - QAction *appendAction(int id, const QString &text, const QString &shortcut = ""); + QAction *appendAction(int id, const QString &text, const QString &shortcut = "", bool enable = true); //设置壁纸 void setWallpaper(const QImage &img); diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 54f105fe..f794c061 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "printhelper.h" +#include "service/permissionconfig.h" #include #include @@ -15,6 +16,7 @@ #include #include +#include #ifdef USE_UNIONIMAGE #include "printhelper.h" @@ -97,12 +99,15 @@ static QAction *hookToolBarActionIcons(QToolBar *bar, QAction **pageSetupAction */ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) { + if (!PermissionConfig::instance()->isPrintable()) { + return; + } + m_re->m_paths.clear(); m_re->m_imgs.clear(); m_re->m_paths = paths; QStringList tempExsitPaths;//保存存在的图片路径 - QImage imgTemp; for (const QString &path : paths) { QString errMsg; QImageReader imgReadreder(path); @@ -120,10 +125,10 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) } } tempExsitPaths << paths; - } + //看图采用同步,因为只有一张图片,传入父指针 - DPrintPreviewDialog printDialog2(parent); + DPrintPreviewDialog printDialog2(parent); #if (DTK_VERSION_MAJOR > 5 \ || (DTK_VERSION_MAJOR >=5 && DTK_VERSION_MINOR > 4) \ || (DTK_VERSION_MAJOR >= 5 && DTK_VERSION_MINOR >= 4 && DTK_VERSION_PATCH >= 10))//5.4.4暂时没有合入 @@ -134,15 +139,58 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) printDialog2.setDocName(QFileInfo(tempExsitPaths.at(0)).absoluteFilePath()); } } + +#ifndef DISABLE_WATERMARK + // 更新打印水印设置 + if (PermissionConfig::instance()->isValid()) { + auto data = PermissionConfig::instance()->printWaterMarkData(); + + DPrintPreviewSettingInfo *baseInfo = printDialog2.createDialogSettingInfo(DPrintPreviewWatermarkInfo::PS_Watermark); + if (baseInfo) { + DPrintPreviewWatermarkInfo *info = dynamic_cast(baseInfo); + if (info) { + info->opened = true; + info->angle = static_cast(data.rotation); + info->transparency = static_cast(data.opacity * 255); + QFontMetrics fm(data.font); + QSize textSize = fm.size(Qt::TextSingleLine, data.text); + info->rowSpacing = (data.lineSpacing + textSize.height()) / textSize.height(); + info->columnSpacing = (data.spacing + textSize.width()) / textSize.width(); + info->layout = data.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : DPrintPreviewWatermarkInfo::Tiled; + info->currentWatermarkType = (data.type == WaterMarkType::Text) ? DPrintPreviewWatermarkInfo::TextWatermark : DPrintPreviewWatermarkInfo::ImageWatermark; + info->textType = DPrintPreviewWatermarkInfo::Custom; + info->customText = data.text; + info->textColor = data.color; + info->fontList.append(data.font.family()); + info->size = int(data.font.pixelSize() / 20.0f * 100); + printDialog2.updateDialogSettingInfo(info); + } else { + qWarning() << qPrintable("Can't convert DPrintPreviewDialog watermark info.") << baseInfo->type(); + } + + delete baseInfo; + } else { + qWarning() << qPrintable("Can't get DPrintPreviewDialog watermark info."); + } + } +#endif + #endif connect(&printDialog2, SIGNAL(paintRequested(DPrinter *)), m_re, SLOT(paintRequestSync(DPrinter *))); #ifndef USE_TEST - printDialog2.exec(); + int ret = printDialog2.exec(); #else - printDialog2.show(); + int ret = printDialog2.show(); #endif + + if (QDialog::Accepted == ret) { + if (!tempExsitPaths.isEmpty()) { + PermissionConfig::instance()->triggerPrint(tempExsitPaths.first()); + } + } + m_re->m_paths.clear(); m_re->m_imgs.clear(); } From 5b8124304166543a675ec3d46390a49823a72f21 Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 14 Mar 2023 10:40:00 +0800 Subject: [PATCH 02/12] fix: a build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复一处构建错误 Log: 修复一处构建错误 Change-Id: Iaacf5a012961df011950cfa889e831e9fad95674 --- libimageviewer/service/permissionconfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 16fea476..17f7d038 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -376,7 +376,7 @@ void PermissionConfig::initFromArguments() triggerOpen(targetImagePath); } - qInfo() << qPrintable("Current Enable permission") << authFlags); + qInfo() << qPrintable("Current Enable permission") << authFlags; } else { qWarning() << QString("Parse authorise config error at pos: %1, details: %2").arg(error.offset).arg(error.errorString()); From 7023df9cc19e1a280f72890007ca5f8d38984ec8 Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 14 Mar 2023 13:55:18 +0800 Subject: [PATCH 03/12] fix: Printed watermark is diff from the read one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTK中打印水印和阅读水印实现方式不同,调整设置的打印水印参数, 使得二者显示效果一致。无打印水印配置时不进行设置。 Log: 解决打印水印和阅读水印显示效果不一致问题 Task: https://pms.uniontech.com/story-view-27783.html Influence: 打印水印 Change-Id: Ia2806eec9490e1ab25f67e68acaa0ac63db6dd04 --- libimageviewer/imageviewer.cpp | 2 +- libimageviewer/service/permissionconfig.cpp | 22 ++++++++++++++++++++- libimageviewer/service/permissionconfig.h | 7 ++++++- libimageviewer/viewpanel/viewpanel.cpp | 8 ++++++++ libimageviewer/widgets/printhelper.cpp | 17 +++++++++++----- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/libimageviewer/imageviewer.cpp b/libimageviewer/imageviewer.cpp index 0fbacd46..fc586b78 100644 --- a/libimageviewer/imageviewer.cpp +++ b/libimageviewer/imageviewer.cpp @@ -87,7 +87,7 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer #ifndef DISABLE_WATERMARK // 设置看图水印,目前仅在主要展示区域显示 - if (PermissionConfig::instance()->isValid()) { + if (PermissionConfig::instance()->hasReadWaterMark()) { auto data = PermissionConfig::instance()->readWaterMarkData(); DWaterMarkHelper::instance()->setData(data); DWaterMarkHelper::instance()->registerWidget(m_panel); diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 17f7d038..cc8fccfb 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -125,7 +125,7 @@ bool PermissionConfig::isSwitchable() const } /** - @return + @return 是否允许打印图片,无授权控制时默认返回 true @note -1 表示无限制;0 表示无打印次数;1~表示剩余可打印次数 */ bool PermissionConfig::isPrintable(const QString &fileName) const @@ -137,6 +137,22 @@ bool PermissionConfig::isPrintable(const QString &fileName) const return !!printLimitCount; } +/** + @return 是否存在阅读水印 + */ +bool PermissionConfig::hasReadWaterMark() const +{ + return authFlags.testFlag(EnableReadWaterMark); +} + +/** + @return 是否存在打印水印 + */ +bool PermissionConfig::hasPrintWaterMark() const +{ + return authFlags.testFlag(EnablePrintWaterMark); +} + /** @brief 触发打开文件 \a fileName ,若为限权文件,向外发送权限通知信号 */ @@ -483,6 +499,8 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) readWaterMark.lineSpacing = param.value("rowSpacing").toInt(); readWaterMark.spacing = param.value("columnSpacing").toInt(); readWaterMark.text = param.value("text").toString(); + + authFlags.setFlag(EnableReadWaterMark, true); #endif } @@ -512,6 +530,8 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) printWaterMark.lineSpacing = param.value("rowSpacing").toInt(); printWaterMark.spacing = param.value("columnSpacing").toInt(); printWaterMark.text = param.value("text").toString(); + + authFlags.setFlag(EnablePrintWaterMark, true); #endif } diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index 7fc13c2b..5d018b90 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -37,6 +37,9 @@ class PermissionConfig : public QObject EnableDelete = 0x4, EnableRename = 0x8, EnableSwitch = 0x10, + + EnableReadWaterMark = 0x1000, + EnablePrintWaterMark = 0x2000, }; Q_DECLARE_FLAGS(Authorises, Authorise) @@ -46,6 +49,8 @@ class PermissionConfig : public QObject bool isRenamable() const; bool isSwitchable() const; bool isPrintable(const QString &fileName = QString()) const; + bool hasReadWaterMark() const; + bool hasPrintWaterMark() const; void triggerOpen(const QString &fileName); void triggerEdit(const QString &fileName); @@ -94,7 +99,7 @@ class PermissionConfig : public QObject QString currentImagePath; // 当前展示的图片文件路径 QString targetImagePath; // 权限控制指向的文件路径 bool valid = false; - int printLimitCount = -1; + int printLimitCount = 0; Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; #ifndef DISABLE_WATERMARK diff --git a/libimageviewer/viewpanel/viewpanel.cpp b/libimageviewer/viewpanel/viewpanel.cpp index b99325f6..e6d8e4e6 100644 --- a/libimageviewer/viewpanel/viewpanel.cpp +++ b/libimageviewer/viewpanel/viewpanel.cpp @@ -152,6 +152,10 @@ LibViewPanel::LibViewPanel(AbstractTopToolbar *customToolbar, QWidget *parent) LibViewPanel::~LibViewPanel() { + // 关闭前保存旋转状态 + if (m_view) { + m_view->slotRotatePixCurrent(); + } // 析构时通知图片窗口关闭 PermissionConfig::instance()->triggerClose(m_currentPath); @@ -198,6 +202,10 @@ void LibViewPanel::loadImage(const QString &path, QStringList paths) QFileInfo targetImageInfo(PermissionConfig::instance()->targetImage()); if (info.absoluteDir() != targetImageInfo.absoluteDir()) { if (!paths.contains(targetImageInfo.absoluteFilePath())) { + // 关闭前保存旋转状态 + if (m_view) { + m_view->slotRotatePixCurrent(); + } // 不含授权文件,提示文件关闭 PermissionConfig::instance()->triggerClose(PermissionConfig::instance()->targetImage()); } diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index f794c061..0b3589f5 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -142,27 +142,34 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) #ifndef DISABLE_WATERMARK // 更新打印水印设置 - if (PermissionConfig::instance()->isValid()) { + if (PermissionConfig::instance()->hasPrintWaterMark()) { auto data = PermissionConfig::instance()->printWaterMarkData(); DPrintPreviewSettingInfo *baseInfo = printDialog2.createDialogSettingInfo(DPrintPreviewWatermarkInfo::PS_Watermark); if (baseInfo) { DPrintPreviewWatermarkInfo *info = dynamic_cast(baseInfo); if (info) { + // 打印水印实现方式同阅读水印略有不同,调整参数以使得效果一致。 info->opened = true; info->angle = static_cast(data.rotation); - info->transparency = static_cast(data.opacity * 255); + info->transparency = static_cast(data.opacity * 100); QFontMetrics fm(data.font); QSize textSize = fm.size(Qt::TextSingleLine, data.text); - info->rowSpacing = (data.lineSpacing + textSize.height()) / textSize.height(); - info->columnSpacing = (data.spacing + textSize.width()) / textSize.width(); + if (textSize.height() > 0) { + info->rowSpacing = qreal(data.lineSpacing + textSize.height()) / textSize.height(); + } + if (textSize.width() > 0) { + info->columnSpacing = qreal(data.spacing + textSize.width()) / textSize.width(); + } info->layout = data.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : DPrintPreviewWatermarkInfo::Tiled; info->currentWatermarkType = (data.type == WaterMarkType::Text) ? DPrintPreviewWatermarkInfo::TextWatermark : DPrintPreviewWatermarkInfo::ImageWatermark; info->textType = DPrintPreviewWatermarkInfo::Custom; info->customText = data.text; info->textColor = data.color; info->fontList.append(data.font.family()); - info->size = int(data.font.pixelSize() / 20.0f * 100); + static const float sc_defaultFontSize = 65.0f; + // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 + info->size = int(data.font.pixelSize() / sc_defaultFontSize * 100); printDialog2.updateDialogSettingInfo(info); } else { qWarning() << qPrintable("Can't convert DPrintPreviewDialog watermark info.") << baseInfo->type(); From 5f0944094b7c63be94192474cf3976d5fd09ae7a Mon Sep 17 00:00:00 2001 From: renbin Date: Fri, 17 Mar 2023 10:47:03 +0800 Subject: [PATCH 04/12] fix: Printing without watermark problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 打印水印设置被包版本判断宏屏蔽,需在DTK5.4.4及以上剩下。 此版本通过libdtkcore5判断,测试及编译环境dtkcore版本为5.2.2, 导致功能被屏蔽。 修改水印设置,将其移除版本宏判断,定制分支一定存在水印功能。 Log: 修复无打印水印问题 Bug: https://pms.uniontech.com/bug-view-191587.html Influence: 打印水印 Change-Id: Ib6f39e314cb59f60faa0094860f248a72aac6a66 --- libimageviewer/widgets/printhelper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 0b3589f5..03854eca 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -139,8 +139,10 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) printDialog2.setDocName(QFileInfo(tempExsitPaths.at(0)).absoluteFilePath()); } } +#endif #ifndef DISABLE_WATERMARK + // 定制分支,水印功能不依赖DTK版本 // 更新打印水印设置 if (PermissionConfig::instance()->hasPrintWaterMark()) { auto data = PermissionConfig::instance()->printWaterMarkData(); @@ -182,7 +184,6 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) } #endif -#endif connect(&printDialog2, SIGNAL(paintRequested(DPrinter *)), m_re, SLOT(paintRequestSync(DPrinter *))); From 3c43acb2daa4c02515fc91ad0902d1eaa418eb83 Mon Sep 17 00:00:00 2001 From: renbin Date: Mon, 20 Mar 2023 09:14:19 +0800 Subject: [PATCH 05/12] fix: Clipboard image appears blank after copying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 图片复制使用GNOME拷贝格式,在PGV系统剪贴板中未显示图片, 修改为同时拷贝图片源数据,以保留原始图片信息。 Log: 修复复制图片剪贴板显示空白图片的问题 Bug: https://pms.uniontech.com/bug-view-191601.html Influence: 图片复制 Change-Id: Id7360e9b33b54961e7ef58241a3835cdb5ffeb7d --- libimageviewer/unionimage/baseutils.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libimageviewer/unionimage/baseutils.cpp b/libimageviewer/unionimage/baseutils.cpp index ea6bc8f0..3676535d 100755 --- a/libimageviewer/unionimage/baseutils.cpp +++ b/libimageviewer/unionimage/baseutils.cpp @@ -182,6 +182,10 @@ void showInFileManager(const QString &path) void copyImageToClipboard(const QStringList &paths) { + if (paths.isEmpty()) { + return; + } + // Get clipboard // QClipboard *cb = QApplication::clipboard(); QClipboard *cb = qApp->clipboard(); @@ -210,13 +214,14 @@ void copyImageToClipboard(const QStringList &paths) gnomeFormat.remove(gnomeFormat.length() - 1, 1); newMimeData->setData("x-special/gnome-copied-files", gnomeFormat); - // Copy Image Date -// QImage img(paths.first()); -// Q_ASSERT(!img.isNull()); -// newMimeData->setImageData(img); + // Copy Image Data + QImage img; + QString errMsg; + if (LibUnionImage_NameSpace::loadStaticImageFromFile(paths.first(), img, errMsg)) { + newMimeData->setImageData(img); + } // Set the mimedata -// cb->setMimeData(newMimeData); cb->setMimeData(newMimeData, QClipboard::Clipboard); } From 7f9d640fa9cb83f61641ab2028ec89a73a473878 Mon Sep 17 00:00:00 2001 From: renbin Date: Fri, 7 Jul 2023 11:08:02 +0800 Subject: [PATCH 06/12] feat: Disable set wallpaper while set permission config. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 设置权限控制时屏蔽"设为壁纸"选项; 2. 为兼容性修改功能判断宏. Log: 设置权限控制时不允许设置壁纸 Task: https://pms.uniontech.com/story-view-30217.html Influence: PermissionConfig Change-Id: Ifc3f4cbb3d7a849d88cc19bda49e14a17330aca8 --- libimageviewer/imageviewer.cpp | 5 +++-- libimageviewer/service/permissionconfig.cpp | 8 ++++---- libimageviewer/service/permissionconfig.h | 8 ++++---- libimageviewer/viewpanel/viewpanel.cpp | 6 ++++-- libimageviewer/widgets/printhelper.cpp | 3 ++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/libimageviewer/imageviewer.cpp b/libimageviewer/imageviewer.cpp index fc586b78..4704add7 100644 --- a/libimageviewer/imageviewer.cpp +++ b/libimageviewer/imageviewer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "imageengine.h" #include "viewpanel/viewpanel.h" @@ -22,7 +23,7 @@ #include "unionimage/imageutils.h" #include "unionimage/baseutils.h" -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper #include #endif @@ -85,7 +86,7 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer m_panel = new LibViewPanel(customTopToolbar, q); layout->addWidget(m_panel); -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper // 设置看图水印,目前仅在主要展示区域显示 if (PermissionConfig::instance()->hasReadWaterMark()) { auto data = PermissionConfig::instance()->readWaterMarkData(); diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index cc8fccfb..6bd217f3 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -29,7 +29,7 @@ const QString g_KeyRemaining = "remainingPrintCount"; PermissionConfig::PermissionConfig(QObject *parent) : QObject(parent) { -#ifdef DISABLE_WATERMARK +#ifndef DTKWIDGET_CLASS_DWaterMarkHelper qWarning() << qPrintable("Current version is not support read watermark"); #endif } @@ -325,7 +325,7 @@ QString PermissionConfig::targetImage() const return targetImagePath; } -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper /** @return 返回从配置中读取的阅读水印配置,用于图片展示时显示 @@ -483,7 +483,7 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) return; } -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper readWaterMark.type = WaterMarkType::Text; readWaterMark.font.setFamily(param.value("font").toString()); readWaterMark.font.setPixelSize(param.value("fontSize").toInt()); @@ -514,7 +514,7 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) return; } -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper printWaterMark.type = WaterMarkType::Text; printWaterMark.font.setFamily(param.value("font").toString()); printWaterMark.font.setPixelSize(param.value("fontSize").toInt()); diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index 5d018b90..d2c7d668 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -8,9 +8,9 @@ #include #include -// #define DISABLE_WATERMARK +#include -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper #include #include @@ -68,7 +68,7 @@ class PermissionConfig : public QObject void setCurrentImagePath(const QString &fileName); QString targetImage() const; -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMarkData() const; WaterMarkData printWaterMarkData() const; #endif @@ -102,7 +102,7 @@ class PermissionConfig : public QObject int printLimitCount = 0; Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMark; WaterMarkData printWaterMark; #endif diff --git a/libimageviewer/viewpanel/viewpanel.cpp b/libimageviewer/viewpanel/viewpanel.cpp index e6d8e4e6..327157a3 100644 --- a/libimageviewer/viewpanel/viewpanel.cpp +++ b/libimageviewer/viewpanel/viewpanel.cpp @@ -697,12 +697,14 @@ void LibViewPanel::updateMenuContent(const QString &path) } - //需要判断图片是否支持设置壁纸,若不支持则置灰设置壁纸菜单项 - if (isPic) { + // 需要判断图片是否支持设置壁纸,若不支持则置灰设置壁纸菜单项 + // 当配置权限控制时,屏蔽设置壁纸接口 + if (isPic && !authIns->isValid()) { QAction *ac = appendAction(IdSetAsWallpaper, QObject::tr("Set as wallpaper"), ss("Set as wallpaper", "Ctrl+F9")); if (ac) ac->setEnabled(Libutils::image::imageSupportWallPaper(ItemInfo.path)); } + if (isReadable) { appendAction(IdDisplayInFileManager, QObject::tr("Display in file manager"), ss("Display in file manager", "Alt+D")); diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 03854eca..907801c2 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -17,6 +17,7 @@ #include #include +#include #ifdef USE_UNIONIMAGE #include "printhelper.h" @@ -141,7 +142,7 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) } #endif -#ifndef DISABLE_WATERMARK +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper // 定制分支,水印功能不依赖DTK版本 // 更新打印水印设置 if (PermissionConfig::instance()->hasPrintWaterMark()) { From 8da32401abb739d3f18494fc06ace225edb536f0 Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 22 Aug 2023 11:28:59 +0800 Subject: [PATCH 07/12] feat: Permission and activate process with DBus. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 更新权限控制处理,并允许通过DBus接口唤醒已启动进程; 2. 更新权限控制单元测试. Log: Update permission config and activate process with DBus. Task: https://pms.uniontech.com/story-view-30217.html Influence: PermissionConfig UnitTest Change-Id: Ibc5e8bd990bb910012cbab47bf596de5529348c2 --- CMakeLists.txt | 2 +- libimageviewer/imageviewer.cpp | 18 +- libimageviewer/service/permissionconfig.cpp | 418 +++++++++--------- libimageviewer/service/permissionconfig.h | 60 +-- .../viewpanel/contents/bottomtoolbar.cpp | 12 +- .../viewpanel/scen/imagegraphicsview.cpp | 6 +- libimageviewer/viewpanel/viewpanel.cpp | 57 ++- libimageviewer/widgets/printhelper.cpp | 3 +- tests/CMakeLists.txt | 2 + tests/test_permissionconfig.cpp | 210 +++++++++ 10 files changed, 519 insertions(+), 269 deletions(-) create mode 100644 tests/test_permissionconfig.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7901c7dd..99db348e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ message(STATUS ${CMAKE_CXX_FLAGS}) option(DOTEST "option for test" OFF) -if(DOTEST) +if(DOTEST OR CMAKE_BUILD_TYPE STREQUAL "Debug") add_subdirectory(tests) endif() diff --git a/libimageviewer/imageviewer.cpp b/libimageviewer/imageviewer.cpp index 4704add7..9e65f383 100644 --- a/libimageviewer/imageviewer.cpp +++ b/libimageviewer/imageviewer.cpp @@ -43,7 +43,11 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer : q_ptr(parent) { // 在界面前初始化授权配置 - PermissionConfig::instance()->initFromArguments(); + if (!qApp) { + qWarning() << qPrintable("Must init authorise config after QApplication initialized!"); + } else { + PermissionConfig::instance()->initFromArguments(qApp->arguments()); + } QDir dir(PLUGINTRANSPATH); if (dir.exists()) { @@ -92,6 +96,18 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer auto data = PermissionConfig::instance()->readWaterMarkData(); DWaterMarkHelper::instance()->setData(data); DWaterMarkHelper::instance()->registerWidget(m_panel); + + // 仅权限控制图片单独展示 + QObject::connect(PermissionConfig::instance(), &PermissionConfig::currentImagePathChanged, q_ptr, [ this ](const QString &, bool isTargetImage){ + if (!PermissionConfig::instance()->isValid()) { + return; + } + + DWaterMarkWidget *mark = m_panel->findChild(); + if (mark) { + mark->setVisible(isTargetImage); + } + }); } #endif } diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 6bd217f3..7b780eea 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -6,8 +6,13 @@ #include "imageengine.h" #include +#include #include #include +#include +#include +#include +#include #include const QString g_KeyTid = "tid"; @@ -15,6 +20,39 @@ const QString g_KeyOperate = "operate"; const QString g_KeyFilePath = "filePath"; const QString g_KeyRemaining = "remainingPrintCount"; +/** + @brief 通过dbus接口从任务栏激活窗口 +*/ +bool activateWindowFromDock(quintptr winId) +{ + bool bRet = false; + // 优先采用V23接口,低版本DDE还未提供区分V23/V20接口 + QDBusInterface dockDbusInterfaceV23( + "org.deepin.dde.daemon.Dock1", "/org/deepin/dde/daemon/Dock1", "org.deepin.dde.daemon.Dock1"); + if (dockDbusInterfaceV23.isValid()) { + QDBusReply reply = dockDbusInterfaceV23.call("ActivateWindow", winId); + if (!reply.isValid()) { + qWarning() << qPrintable("Call v23 org.deepin.dde.daemon.Dock1 failed") << reply.error(); + } else { + return true; + } + } + + QDBusInterface dockDbusInterfaceV20( + "com.deepin.dde.daemon.Dock", "/com/deepin/dde/daemon/Dock", "com.deepin.dde.daemon.Dock"); + if (dockDbusInterfaceV20.isValid() && !bRet) { + QDBusReply reply = dockDbusInterfaceV20.call("ActivateWindow", winId); + if (!reply.isValid()) { + qWarning() << qPrintable("Call v20 com.deepin.dde.daemon.Dock failed") << reply.error(); + bRet = false; + } else { + return true; + } + } + + return bRet; +} + /** @class AuthoriseConfig @brief 授权控制类,提供操作授权和水印配置 @@ -64,66 +102,6 @@ bool PermissionConfig::isCurrentIsTargetImage() const return isValid() && currentImagePath == targetImagePath; } -/** - @return 是否允许编辑图片,无授权控制时默认返回 true - */ -bool PermissionConfig::isEditable(const QString &fileName) const -{ - if (checkAuthInvalid(fileName)) { - return true; - } - - return authFlags.testFlag(EnableEdit); -} - -/** - @return 是否允许复制图片,无授权控制时默认返回 true - */ -bool PermissionConfig::isCopyable(const QString &fileName) const -{ - if (checkAuthInvalid(fileName)) { - return true; - } - - return authFlags.testFlag(EnableCopy); -} - -/** - @return 是否允许删除图片,无授权控制时默认返回 true - */ -bool PermissionConfig::isDeletable() const -{ - if (checkAuthInvalid()) { - return true; - } - - return authFlags.testFlag(EnableDelete); -} - -/** - @return 是否允许重命名图片,无授权控制时默认返回 true - */ -bool PermissionConfig::isRenamable() const -{ - if (checkAuthInvalid()) { - return true; - } - - return authFlags.testFlag(EnableRename); -} - -/** - @return 是否允许切换图片,无授权控制时默认返回 true - */ -bool PermissionConfig::isSwitchable() const -{ - if (checkAuthInvalid()) { - return true; - } - - return authFlags.testFlag(EnableSwitch); -} - /** @return 是否允许打印图片,无授权控制时默认返回 true @note -1 表示无限制;0 表示无打印次数;1~表示剩余可打印次数 @@ -153,87 +131,12 @@ bool PermissionConfig::hasPrintWaterMark() const return authFlags.testFlag(EnablePrintWaterMark); } -/** - @brief 触发打开文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerOpen(const QString &fileName) -{ - if (checkAuthInvalid(fileName)) { - return; - } - - if (NotOpen != status) { - return; - } - status = Open; - - QJsonObject data{{g_KeyTid, TidOpen}, {g_KeyOperate, "open"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - -/** - @brief 触发编辑文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerEdit(const QString &fileName) -{ - if (checkAuthInvalid(fileName)) { - return; - } - - QJsonObject data{{g_KeyTid, TidEdit}, {g_KeyOperate, "edit"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - -/** - @brief 触发拷贝文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerCopy(const QString &fileName) -{ - if (checkAuthInvalid(fileName)) { - return; - } - - QJsonObject data{{g_KeyTid, TidCopy}, {g_KeyOperate, "copy"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - -/** - @brief 触发删除文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerDelete(const QString &fileName) -{ - if (checkAuthInvalid(fileName)) { - return; - } - - QJsonObject data{{g_KeyTid, TidDelete}, {g_KeyOperate, "delete"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - -/** - @brief 触发重命名文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerRename(const QString &fileName) -{ - if (checkAuthInvalid()) { - return; - } - - QJsonObject data{{g_KeyTid, TidRename}, {g_KeyOperate, "rename"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - /** @brief 触发打印文件 \a fileName ,若为限权文件,向外发送权限通知信号 */ void PermissionConfig::triggerPrint(const QString &fileName) { - if (checkAuthInvalid()) { + if (checkAuthInvalid(fileName)) { return; } @@ -244,25 +147,6 @@ void PermissionConfig::triggerPrint(const QString &fileName) triggerNotify(data); } -/** - @brief 触发关闭文件 \a fileName ,若为限权文件,向外发送权限通知信号 - */ -void PermissionConfig::triggerClose(const QString &fileName) -{ - if (checkAuthInvalid(fileName)) { - return; - } - - if (Open != status) { - return; - } - status = Close; - - QJsonObject data{{"tid", TidClose}, {g_KeyOperate, "close"}, {g_KeyFilePath, fileName}}; - - triggerNotify(data); -} - /** @brief 返回当前剩余的打印次数 @sa `isPrintable` @@ -292,8 +176,67 @@ void PermissionConfig::reduceOnePrintCount() printLimitCount--; Q_EMIT printCountChanged(); } else { - qWarning() << "Escape print authorise check!"; + qWarning() << qPrintable("Escape print authorise check!"); + } +} + +/** + @brief 用于操作文件 \a fileName 触发不同类型 \a tid 动作,根据不同动作构造不同的 Json 通知数据, + 并通过信号向外发送。 +*/ +void PermissionConfig::triggerAction(TidType tid, const QString &fileName) +{ + if (checkAuthInvalid(fileName)) { + return; + } + + QString optName; + switch (tid) { + case TidOpen: + if (NotOpen != status) { + return; + } + status = Open; + optName = "open"; + break; + case TidClose: + if (Open != status) { + return; + } + status = Close; + // Note: 授权文件关闭后(关闭窗口或打开其他图片),权限控制无效化 + valid = false; + + optName = "close"; + break; + case TidEdit: + optName = "edit"; + break; + case TidCopy: + optName = "copy"; + break; + case TidDelete: + optName = "delete"; + break; + case TidPrint: + // 打印操作略有不同,单独处理 + triggerPrint(fileName); + return; + default: + /* + 当前未捕获的操作将不会处理,直接返回 + case TidSwitch: + optName = "pictureSwitch"; + break; + case TidSetWallpaper: + optName = "setWallpaper"; + break; + */ + return; } + + QJsonObject data{{g_KeyTid, tid}, {g_KeyOperate, optName}, {g_KeyFilePath, fileName}}; + triggerNotify(data); } /** @@ -314,7 +257,14 @@ void PermissionConfig::triggerNotify(const QJsonObject &data) */ void PermissionConfig::setCurrentImagePath(const QString &fileName) { + if (!valid) { + return; + } + currentImagePath = fileName; + + // 通知当前展示的图片变更 + Q_EMIT currentImagePathChanged(fileName, bool(currentImagePath == targetImagePath)); } /** @@ -343,24 +293,46 @@ WaterMarkData PermissionConfig::printWaterMarkData() const return printWaterMark; } -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper + +/** + @brief 接收来自外部的调起窗口指令,当 \a pid 和当前进程pid一致时,激活主窗口 +*/ +void PermissionConfig::activateProcess(qint64 pid) +{ + qInfo() << QString("Receive DBus activate process, current pid: %1, request pid %2").arg(qApp->applicationPid()).arg(pid); + + if (pid == qApp->applicationPid()) { + QWindowList winList = qApp->topLevelWindows(); + if (winList.isEmpty()) { + return; + } + + // 看图默认一个窗口,通过 DBus 接口激活窗口(防止缩小在任务栏) + auto mainWin = winList.first(); + if (!Q_LIKELY(activateWindowFromDock(mainWin->winId()))) { + mainWin->requestActivate(); + } + } +} /** - @brief 从命令行参数中取得授权配置 + @brief 从命令行参数 \a arguments 中取得授权配置 @note 命令行参数示例: deepin-image-viewer --config=[Base64 code data] /path/to/file */ -void PermissionConfig::initFromArguments() +void PermissionConfig::initFromArguments(const QStringList &arguments) { if (valid) { return; } - QString configParam = parseConfigOption(); - if (!configParam.isEmpty()) { + QString configParam; + QStringList imageList; + bool ret = parseConfigOption(arguments, configParam, imageList); + if (ret) { // 获取带权限控制的文件路径 - QStringList arguments = qApp->arguments(); - for (const QString &arg : arguments) { + for (const QString &arg : imageList) { QFileInfo info(arg); QString filePath = info.absoluteFilePath(); if (ImageEngine::instance()->isImage(filePath) || info.suffix() == "dsps") { @@ -370,10 +342,9 @@ void PermissionConfig::initFromArguments() } if (targetImagePath.isEmpty()) { - qWarning() << qPrintable("Authorise config with no target image path.") << arguments; + qWarning() << qPrintable("Authorise config with no target image path."); return; } - QByteArray jsonData = QByteArray::fromBase64(configParam.toUtf8()); qInfo() << QString("Parse authorise config, data: %1").arg(QString(jsonData)); @@ -386,72 +357,71 @@ void PermissionConfig::initFromArguments() initReadWaterMark(root.value("readWatermark").toObject()); initPrintWaterMark(root.value("printWatermark").toObject()); - valid = !targetImagePath.isEmpty(); - if (valid) { - // 首次触发打开图片 - triggerOpen(targetImagePath); - } - qInfo() << qPrintable("Current Enable permission") << authFlags; } else { qWarning() << QString("Parse authorise config error at pos: %1, details: %2").arg(error.offset).arg(error.errorString()); } + + // 只要传入参数、图片即认为有效,无论参数是否正常解析 + valid = true; + // 首次触发打开图片 + triggerAction(TidOpen, targetImagePath); } else { qWarning() << qPrintable("Parse authorise config is empty."); } + + if (valid) { + // 传入权限控制参数时,绑定DBus唤醒信号 + QDBusConnection connection = QDBusConnection::sessionBus(); + bool ret = connection.connect( + "com.wps.cryptfs", "/com/wps/cryptfs", "cryptfs.method.Type", "activateProcess", this, SLOT(activateProcess(qint64))); + if (!ret) { + qWarning() << qPrintable("DBus connect activateProcess failed!"); + } else { + qInfo() << qPrintable("DBus connect activateProcess success!"); + } + } } /** @return 解析命令行参数并返回设置的权限和水印配置,为设置则返回空 */ -QString PermissionConfig::parseConfigOption() -{ - if (!qApp) { - qWarning() << qPrintable("Must init authorise config after QApplication initialized"); - return {}; +bool PermissionConfig::parseConfigOption(const QStringList &arguments, QString &configParam, QStringList &imageList) const +{ + // 避开以 --config=* 开头的特殊图片 + QStringList specialList; + QStringList argumentsList = arguments; + QString prefix("--config="); + for (auto itr = argumentsList.begin(); itr != argumentsList.end();) { + if ((*itr).startsWith(prefix) && (*itr).size() > prefix.size()) { + if (ImageEngine::instance()->isImage((*itr))) { + specialList.append(*itr); + itr = argumentsList.erase(itr); + continue; + } + } + + ++itr; } - const QString option("--config"); - const QLatin1Char assignChar('='); - enum Status { FindOption, FindAssign, FindParam }; - Status status = FindOption; - - QStringList arguments = qApp->arguments(); - for (const QString &arg : arguments) { - switch (status) { - case FindOption: - if (arg.simplified().startsWith(option)) { - int index = arg.indexOf(assignChar); - if (index < 0) { - status = FindAssign; - continue; - } else if (index == arg.length() - 1) { - status = FindParam; - continue; - } else { - return arg.mid(index + 1); - } - } - break; - case FindAssign: - if (arg.simplified().startsWith(assignChar)) { - if (arg.length() > 1) { - return arg.mid(1); - } else { - status = FindParam; - continue; - } - } else { - return {}; - } - break; - case FindParam: - return arg; - } + // 取得 --config 后的数据 + QCommandLineParser parser; + QCommandLineOption configOpt("config", "Permission config json(base64 encode).", "configvalue"); + parser.addOption(configOpt); + + if (!parser.parse(argumentsList)) { + qWarning() << QString("Can't parse arguments %1").arg(parser.errorText()); + return false; + } + + imageList = parser.positionalArguments() + specialList; + if (parser.isSet(configOpt)) { + configParam = parser.value(configOpt); + return true; } - return {}; + return false; } /** @@ -467,8 +437,8 @@ void PermissionConfig::initAuthorise(const QJsonObject ¶m) // 屏蔽 delete / rename ,默认无此功能 authFlags.setFlag(EnableEdit, param.value("edit").toBool(false)); authFlags.setFlag(EnableCopy, param.value("copy").toBool(false)); - // 默认允许切换图片,包括快捷键及 Ctrl + O - authFlags.setFlag(EnableSwitch, true); + authFlags.setFlag(EnableSwitch, param.value("pictureSwitch").toBool(false)); + authFlags.setFlag(EnableWallpaper, param.value("setWallpaper").toBool(false)); printLimitCount = param.value("printCount").toInt(0); } @@ -501,7 +471,7 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) readWaterMark.text = param.value("text").toString(); authFlags.setFlag(EnableReadWaterMark, true); -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper } /** @@ -532,7 +502,19 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) printWaterMark.text = param.value("text").toString(); authFlags.setFlag(EnablePrintWaterMark, true); -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper +} + +/** + @return 返回文件 \a fileName 是否符合权限标识 \a authFlag +*/ +bool PermissionConfig::checkAuthFlag(PermissionConfig::Authorise authFlag, const QString &fileName) const +{ + if (checkAuthInvalid(fileName)) { + return true; + } + + return authFlags.testFlag(authFlag); } /** diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index d2c7d668..398abbd1 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -15,7 +15,7 @@ #include DWIDGET_USE_NAMESPACE -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper class PermissionConfig : public QObject { @@ -32,33 +32,39 @@ class PermissionConfig : public QObject enum Authorise { NoAuth = 0, - EnableEdit = 0x1, + EnableEdit = 0x1, // 编辑权限,旋转图片是否允许写回文件 EnableCopy = 0x2, EnableDelete = 0x4, EnableRename = 0x8, - EnableSwitch = 0x10, + EnableSwitch = 0x10, // 是否允许切换文件 + EnableWallpaper = 0x20, EnableReadWaterMark = 0x1000, EnablePrintWaterMark = 0x2000, }; Q_DECLARE_FLAGS(Authorises, Authorise) - bool isEditable(const QString &fileName = QString()) const; - bool isCopyable(const QString &fileName = QString()) const; - bool isDeletable() const; - bool isRenamable() const; - bool isSwitchable() const; + // 权限/配置查询接口 + bool checkAuthFlag(Authorise authFlag, const QString &fileName = QString()) const; bool isPrintable(const QString &fileName = QString()) const; bool hasReadWaterMark() const; bool hasPrintWaterMark() const; - void triggerOpen(const QString &fileName); - void triggerEdit(const QString &fileName); - void triggerCopy(const QString &fileName); - void triggerDelete(const QString &fileName); - void triggerRename(const QString &fileName); + enum TidType { + TidOpen = 1000200050, + TidEdit = 1000200051, + TidCopy = 1000200052, + TidPrint = 1000200053, + TidClose = 1000200054, + TidSwitch = 1000200055, + TidSetWallpaper = 1000200056, + TidDelete = 1000200057, + TidRename = 1000200058, + }; + + // 动作触发通知接口 + void triggerAction(TidType tid, const QString &fileName); void triggerPrint(const QString &fileName); - void triggerClose(const QString &fileName); Q_SIGNAL void authoriseNotify(const QJsonObject &data); int printCount() const; @@ -66,46 +72,44 @@ class PermissionConfig : public QObject Q_SIGNAL void printCountChanged(); void setCurrentImagePath(const QString &fileName); + Q_SIGNAL void currentImagePathChanged(const QString &fileName, bool isTargetImage); QString targetImage() const; + // 阅读/打印水印 #ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMarkData() const; WaterMarkData printWaterMarkData() const; -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper - void initFromArguments(); + Q_SLOT void activateProcess(qint64 pid); + void initFromArguments(const QStringList &arguments); private: - QString parseConfigOption(); + // 解析配置 + bool parseConfigOption(const QStringList &arguments, QString &configParam, QStringList &imageList) const; void initAuthorise(const QJsonObject ¶m); void initReadWaterMark(const QJsonObject ¶m); void initPrintWaterMark(const QJsonObject ¶m); + bool checkAuthInvalid(const QString &fileName = QString()) const; void reduceOnePrintCount(); + void triggerNotify(const QJsonObject &data); private: - enum TidType { - TidOpen = 1000200050, - TidEdit = 1000200051, - TidCopy = 1000200052, - TidPrint = 1000200053, - TidClose = 1000200054, - TidDelete = 1000200055, - TidRename = 1000200056, - }; enum Status { NotOpen, Open, Close }; QString currentImagePath; // 当前展示的图片文件路径 QString targetImagePath; // 权限控制指向的文件路径 bool valid = false; - int printLimitCount = 0; + int printLimitCount = 0; // 打印记数,-1表示无限制 Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; + #ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMark; WaterMarkData printWaterMark; -#endif +#endif // DTKWIDGET_CLASS_DWaterMarkHelper }; #endif // PERMISSIONCONFIG_H diff --git a/libimageviewer/viewpanel/contents/bottomtoolbar.cpp b/libimageviewer/viewpanel/contents/bottomtoolbar.cpp index 1037d03c..80d9181b 100644 --- a/libimageviewer/viewpanel/contents/bottomtoolbar.cpp +++ b/libimageviewer/viewpanel/contents/bottomtoolbar.cpp @@ -174,7 +174,7 @@ void LibBottomToolbar::setRotateBtnClicked(const bool &bRet) void LibBottomToolbar::setPictureDoBtnClicked(const bool &bRet) { - bool enableCopy = PermissionConfig::instance()->isCopyable(); + bool enableCopy = PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableCopy); if (m_ocrIsExists && m_ocrBtn) { m_ocrBtn->setEnabled(bRet && enableCopy); @@ -322,7 +322,7 @@ void LibBottomToolbar::deleteImage() m_imgListWidget->moveCenterWidget(); - PermissionConfig::instance()->triggerDelete(path); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidDelete, path); } } @@ -484,7 +484,7 @@ void LibBottomToolbar::slotOpenImage(int index, QString path) PermissionConfig::instance()->setCurrentImagePath(info.absoluteFilePath()); m_trashBtn->setVisible(!PermissionConfig::instance()->isCurrentIsTargetImage()); - bool isCopyable = PermissionConfig::instance()->isCopyable(); + bool isCopyable = PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableCopy); if (m_ocrIsExists) { if (isCopyable) { m_ocrBtn->setToolTip(QObject::tr("Extract text")); @@ -507,7 +507,7 @@ void LibBottomToolbar::slotOpenImage(int index, QString path) m_adaptImageBtn->setEnabled(true); m_adaptScreenBtn->setEnabled(true); - if (!PermissionConfig::instance()->isEditable()) { + if (!PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableEdit)) { return; } @@ -816,9 +816,9 @@ void LibBottomToolbar::initUI() auto authIns = PermissionConfig::instance(); if (m_ocrBtn) { - m_ocrBtn->setEnabled(authIns->isEditable()); + m_ocrBtn->setEnabled(authIns->checkAuthFlag(PermissionConfig::EnableEdit)); } - bool isDeletable = authIns->isDeletable(); + bool isDeletable = authIns->checkAuthFlag(PermissionConfig::EnableDelete); m_trashBtn->setVisible(isDeletable); m_trashBtn->setEnabled(isDeletable); } diff --git a/libimageviewer/viewpanel/scen/imagegraphicsview.cpp b/libimageviewer/viewpanel/scen/imagegraphicsview.cpp index 64ebe1a3..5a2fd6ac 100644 --- a/libimageviewer/viewpanel/scen/imagegraphicsview.cpp +++ b/libimageviewer/viewpanel/scen/imagegraphicsview.cpp @@ -963,8 +963,8 @@ void LibImageGraphicsView::slotRotatePixCurrent() pathType != imageViewerSpace::PathTypeSAFEBOX && //保险箱 pathType != imageViewerSpace::PathTypeRECYCLEBIN) { //回收站 - // 图片信息不一定同步,需做二次判断 - if (!PermissionConfig::instance()->isEditable(m_path)) { + // 图片信息不一定同步,需做二次判断,根据权限配置是否回写文件 + if (!PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableEdit, m_path)) { m_rotateAngel = 0; return; } @@ -986,7 +986,7 @@ void LibImageGraphicsView::slotRotatePixCurrent() m_rotateAngel = 0; // 通知授权控制图片编辑完成 - PermissionConfig::instance()->triggerEdit(m_path); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidEdit, m_path); } } } diff --git a/libimageviewer/viewpanel/viewpanel.cpp b/libimageviewer/viewpanel/viewpanel.cpp index 327157a3..308bbd35 100644 --- a/libimageviewer/viewpanel/viewpanel.cpp +++ b/libimageviewer/viewpanel/viewpanel.cpp @@ -157,7 +157,7 @@ LibViewPanel::~LibViewPanel() m_view->slotRotatePixCurrent(); } // 析构时通知图片窗口关闭 - PermissionConfig::instance()->triggerClose(m_currentPath); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidClose, m_currentPath); // 清空图像缓存目录 Libutils::image::clearCacheImageFolder(); @@ -207,7 +207,8 @@ void LibViewPanel::loadImage(const QString &path, QStringList paths) m_view->slotRotatePixCurrent(); } // 不含授权文件,提示文件关闭 - PermissionConfig::instance()->triggerClose(PermissionConfig::instance()->targetImage()); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidClose, + PermissionConfig::instance()->targetImage()); } } } @@ -504,8 +505,8 @@ void LibViewPanel::updateMenuContent(const QString &path) imageViewerSpace::ImageType imageType = LibUnionImage_NameSpace::getImageType(currentPath);//图片类型 auto authIns = PermissionConfig::instance(); - bool isEditable = authIns->isEditable(currentPath); - bool isCopyable = authIns->isCopyable(currentPath); + bool isEditable = authIns->checkAuthFlag(PermissionConfig::EnableEdit, currentPath); + bool isCopyable = authIns->checkAuthFlag(PermissionConfig::EnableCopy, currentPath); //判断是否是损坏图片 setCurrentWidget(currentPath); @@ -636,7 +637,7 @@ void LibViewPanel::updateMenuContent(const QString &path) if (isReadable && isWritable && imageViewerSpace::PathTypeMTP != pathType && imageViewerSpace::PathTypePTP != pathType && - imageViewerSpace::PathTypeAPPLE != pathType && !isAlbum && authIns->isRenamable()) { + imageViewerSpace::PathTypeAPPLE != pathType && !isAlbum && authIns->checkAuthFlag(PermissionConfig::EnableRename)) { appendAction(IdRename, QObject::tr("Rename"), ss("Rename", "F2")); } @@ -648,7 +649,7 @@ void LibViewPanel::updateMenuContent(const QString &path) imageViewerSpace::PathTypeRECYCLEBIN != pathType && imageViewerSpace::PathTypeMTP != pathType && imageViewerSpace::PathTypePTP != pathType && - isWritable && isReadable && authIns->isDeletable()) || (isAlbum && isWritable)) { + isWritable && isReadable && authIns->checkAuthFlag(PermissionConfig::EnableDelete)) || (isAlbum && isWritable)) { if (isAlbum) { appendAction(IdMoveToTrash, QObject::tr("Delete"), ss("Throw to trash", "")); } else { @@ -699,7 +700,8 @@ void LibViewPanel::updateMenuContent(const QString &path) // 需要判断图片是否支持设置壁纸,若不支持则置灰设置壁纸菜单项 // 当配置权限控制时,屏蔽设置壁纸接口 - if (isPic && !authIns->isValid()) { + bool enableWallpaper = authIns->checkAuthFlag(PermissionConfig::EnableWallpaper, currentPath); + if (isPic && enableWallpaper) { QAction *ac = appendAction(IdSetAsWallpaper, QObject::tr("Set as wallpaper"), ss("Set as wallpaper", "Ctrl+F9")); if (ac) ac->setEnabled(Libutils::image::imageSupportWallPaper(ItemInfo.path)); @@ -927,6 +929,9 @@ void LibViewPanel::setWallpaper(const QString &imgPath) } QDBusMessage reply = interface.call(QStringLiteral("SetMonitorBackground"), screenname, path); qDebug() << "SettingWallpaper: replay" << reply.errorMessage(); + + // 通知触发设置壁纸动作(属于拷贝动作) + PermissionConfig::instance()->triggerAction(PermissionConfig::TidCopy, path); } else { qWarning() << "SettingWallpaper failed" << interface.lastError(); } @@ -950,9 +955,26 @@ bool LibViewPanel::startdragImage(const QStringList &paths, const QString &first if (image_list.isEmpty()) return false; + // 判断是否允许切换图片 (首次进入同样判断) + bool enableSwitch = PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableSwitch, image_list.first()); + if (!enableSwitch) { + if (image_list.first() != PermissionConfig::instance()->targetImage()) { + return false; + } + } + bool permissionValid = PermissionConfig::instance()->isValid(); + if (LibCommonService::instance()->getImgViewerType() == imageViewerSpace::ImgViewerTypeLocal) { QString path = image_list.first(); - if ((path.indexOf("smb-share:server=") != -1 || path.indexOf("mtp:host=") != -1 || path.indexOf("gphoto2:host=") != -1)) { + + // 判断是否允许切换图片,不切换图片时,将仅使用首张图片 + if (permissionValid && !enableSwitch) { + image_list.clear(); + if (ImageEngine::instance()->isImage(path)) { + image_list << path; + } + + } else if ((path.indexOf("smb-share:server=") != -1 || path.indexOf("mtp:host=") != -1 || path.indexOf("gphoto2:host=") != -1)) { image_list.clear(); //判断是否图片格式 if (ImageEngine::instance()->isImage(path)) { @@ -979,11 +1001,13 @@ bool LibViewPanel::startdragImage(const QStringList &paths, const QString &first } } } + if (image_list.count() > 0) { bRet = true; } else { bRet = false; } + //解决拖入非图片文件会出现崩溃 QString loadingPath = ""; if (image_list.contains(path)) { @@ -1263,6 +1287,12 @@ void LibViewPanel::setBottomToolBarButtonAlawysNotVisible(imageViewerSpace::Butt bool LibViewPanel::startChooseFileDialog() { + // 授权控制状态下,判断是否允许切换图片 + bool enableSwitch = PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableSwitch); + if (!enableSwitch) { + return false; + } + bool bRet = false; if (m_stack->currentWidget() != m_sliderPanel) { QString filter = tr("All images"); @@ -1495,7 +1525,7 @@ bool LibViewPanel::slotOcrPicture() //图片过大,会导致崩溃,超过4K,智能裁剪 if (m_ocrInterface != nullptr && m_view != nullptr) { // 提示授权控制拷贝图片 - PermissionConfig::instance()->triggerCopy(path); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidCopy, path); QImage image = m_view->image(); if (image.width() > 2500) { @@ -1790,7 +1820,7 @@ void LibViewPanel::onMenuItemClicked(QAction *action) openImg(0, filepath); } - PermissionConfig::instance()->triggerRename(oldPath); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidRename, oldPath); } } if (m_dirWatcher) { @@ -1814,7 +1844,7 @@ void LibViewPanel::onMenuItemClicked(QAction *action) Libutils::base::copyImageToClipboard(QStringList(m_bottomToolbar->getCurrentItemInfo().path)); } - PermissionConfig::instance()->triggerCopy(m_bottomToolbar->getCurrentItemInfo().path); + PermissionConfig::instance()->triggerAction(PermissionConfig::TidCopy, m_bottomToolbar->getCurrentItemInfo().path); break; } case IdMoveToTrash: { @@ -2187,6 +2217,11 @@ void LibViewPanel::dragEnterEvent(QDragEnterEvent *event) return; } + // 授权控制时判断是否允许拖拽图片进入 + if (!PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableSwitch)) { + return; + } + const QMimeData *mimeData = event->mimeData(); if (!pluginUtils::base::checkMimeData(mimeData)) { return; diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 907801c2..2da8f3d7 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -191,7 +191,8 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) #ifndef USE_TEST int ret = printDialog2.exec(); #else - int ret = printDialog2.show(); + printDialog2.show(); + int ret = QDialog::Accepted; #endif if (QDialog::Accepted == ret) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9310f57d..b5ec06f9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,8 @@ add_definitions( -DUSE_UNIONIMAGE ) option (USE_TEST "Use TESTS" ON) add_definitions( -DUSE_TEST ) +ADD_COMPILE_OPTIONS(-fno-access-control) + set(TARGET_NAME image-editor-test) set(CMD_NAME image-editor-test) set(CMAKE_AUTOMOC ON) diff --git a/tests/test_permissionconfig.cpp b/tests/test_permissionconfig.cpp new file mode 100644 index 00000000..e4ad2ed2 --- /dev/null +++ b/tests/test_permissionconfig.cpp @@ -0,0 +1,210 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "service/permissionconfig.h" +#include "imageengine.h" + +class UT_PermissionConfig : public ::testing::Test +{ +public: + void SetUp() override; + void TearDown() override; +}; + +void UT_PermissionConfig::SetUp() +{ + PermissionConfig::instance()->valid = true; + PermissionConfig::instance()->targetImagePath.clear(); + PermissionConfig::instance()->currentImagePath.clear(); +} + +void UT_PermissionConfig::TearDown() +{ + PermissionConfig::instance()->valid = false; +} + +static QString permission_config() +{ + QByteArray permission("{ \"permission\": { \"edit\": true, \"copy\": true, \"setWallpaper\": true, \"pictureSwitch\": true, \"printCount\": 2 } }"); + return permission.toBase64(); +} + +TEST_F(UT_PermissionConfig, parseConfigOption_NormalParam_Pass) +{ + QString configParam; + QStringList imageList; + QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; + QStringList tmpList {tempPath}; + QStringList arguments {qApp->arguments().first(), "--config=test", tempPath}; + + EXPECT_TRUE(PermissionConfig::instance()->parseConfigOption(arguments, configParam, imageList)); + EXPECT_EQ(configParam, QString("test")); + EXPECT_EQ(imageList, tmpList); + + QStringList arguments2 {qApp->arguments().first(), "--config", "test", tempPath}; + EXPECT_TRUE(PermissionConfig::instance()->parseConfigOption(arguments2, configParam, imageList)); + EXPECT_EQ(configParam, QString("test")); +} + +TEST_F(UT_PermissionConfig, parseConfigOption_NormalParam_Fail) +{ + QString configParam; + QStringList imageList; + QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; + QStringList arguments {qApp->arguments().first(), "config", "test", tempPath}; + + EXPECT_FALSE(PermissionConfig::instance()->parseConfigOption({}, configParam, imageList)); + EXPECT_TRUE(configParam.isEmpty()); + EXPECT_TRUE(imageList.isEmpty()); + + QStringList tmpList {"config", "test", tempPath}; + EXPECT_FALSE(PermissionConfig::instance()->parseConfigOption(arguments, configParam, imageList)); + EXPECT_TRUE(configParam.isEmpty()); + EXPECT_EQ(imageList, tmpList); +} + +TEST_F(UT_PermissionConfig, initFromArguments_NormalParam_Pass) +{ + PermissionConfig::instance()->valid = false; + PermissionConfig::instance()->authFlags = PermissionConfig::NoAuth; + + QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; + QStringList args; + args << qApp->arguments().first() + << QString("--config=%1").arg(permission_config()) + << tempPath; + + PermissionConfig::instance()->initFromArguments(args); + EXPECT_TRUE(PermissionConfig::instance()->isValid()); + + PermissionConfig::Authorises auth = PermissionConfig::Authorises( + PermissionConfig::EnableCopy | PermissionConfig::EnableEdit | PermissionConfig::EnableSwitch | PermissionConfig::EnableWallpaper); + EXPECT_EQ(PermissionConfig::instance()->authFlags, auth); + EXPECT_EQ(PermissionConfig::instance()->targetImagePath, tempPath); +} + +TEST_F(UT_PermissionConfig, initFromArguments_NormalParam_Fail) +{ + PermissionConfig::instance()->valid = false; + PermissionConfig::instance()->authFlags = PermissionConfig::NoAuth; + + PermissionConfig::instance()->initFromArguments({}); + EXPECT_FALSE(PermissionConfig::instance()->isValid()); + EXPECT_EQ(PermissionConfig::instance()->authFlags, PermissionConfig::NoAuth); + EXPECT_TRUE(PermissionConfig::instance()->targetImagePath.isEmpty()); +} + +TEST_F(UT_PermissionConfig, checkAuthFlag_NotInit_Pass) +{ + PermissionConfig::instance()->authFlags = PermissionConfig::Authorises(0b11000000111111); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableEdit)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableCopy)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableDelete)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableRename)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableSwitch)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableWallpaper)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableReadWaterMark)); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnablePrintWaterMark)); + EXPECT_TRUE(PermissionConfig::instance()->hasReadWaterMark()); + EXPECT_TRUE(PermissionConfig::instance()->hasPrintWaterMark()); + + PermissionConfig::instance()->currentImagePath = "1.png"; + PermissionConfig::instance()->targetImagePath = "1.png"; + EXPECT_FALSE(PermissionConfig::instance()->checkAuthInvalid()); + EXPECT_TRUE(PermissionConfig::instance()->checkAuthInvalid("2.png")); +} + +TEST_F(UT_PermissionConfig, checkAuthFlag_NotInit_Fail) +{ + EXPECT_TRUE(PermissionConfig::instance()->valid); + PermissionConfig::instance()->authFlags = PermissionConfig::NoAuth; + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableEdit)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableCopy)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableDelete)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableRename)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableSwitch)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableWallpaper)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnableReadWaterMark)); + EXPECT_FALSE(PermissionConfig::instance()->checkAuthFlag(PermissionConfig::EnablePrintWaterMark)); + EXPECT_FALSE(PermissionConfig::instance()->hasReadWaterMark()); + EXPECT_FALSE(PermissionConfig::instance()->hasPrintWaterMark()); +} + +TEST_F(UT_PermissionConfig, print_Count_Pass) +{ + QString tmpPath("/tmp/tmp.png"); + PermissionConfig::instance()->targetImagePath = tmpPath; + + PermissionConfig::instance()->printLimitCount = 1; + EXPECT_EQ(PermissionConfig::instance()->printLimitCount, 1); + EXPECT_TRUE(PermissionConfig::instance()->isPrintable(tmpPath)); + + PermissionConfig::instance()->triggerPrint(tmpPath); + EXPECT_EQ(PermissionConfig::instance()->printLimitCount, 0); + EXPECT_FALSE(PermissionConfig::instance()->isPrintable(tmpPath)); + + PermissionConfig::instance()->printLimitCount = -1; + EXPECT_TRUE(PermissionConfig::instance()->isPrintable(tmpPath)); + PermissionConfig::instance()->triggerPrint(tmpPath); + EXPECT_EQ(PermissionConfig::instance()->printLimitCount, -1); + EXPECT_TRUE(PermissionConfig::instance()->isUnlimitPrint()); +} + +TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass) +{ + // Default value + auto markData = PermissionConfig::instance()->readWaterMarkData(); + EXPECT_EQ(markData.type, WaterMarkType::None); + EXPECT_EQ(markData.opacity, 1); + EXPECT_EQ(markData.layout, WaterMarkLayout::Center); + EXPECT_TRUE(markData.text.isEmpty()); + + QJsonObject param {{"text", "test"}, {"opacity", 0}, {"layout", 1}}; + + PermissionConfig::instance()->initReadWaterMark(param); + markData = PermissionConfig::instance()->readWaterMarkData(); + EXPECT_EQ(markData.type, WaterMarkType::Text); + EXPECT_EQ(markData.opacity, 0); + EXPECT_EQ(markData.layout, WaterMarkLayout::Tiled); + EXPECT_EQ(markData.text, QString("test")); + + PermissionConfig::instance()->initPrintWaterMark(param); + markData = PermissionConfig::instance()->printWaterMarkData(); + EXPECT_EQ(markData.type, WaterMarkType::Text); +} + +TEST_F(UT_PermissionConfig, triggerAction_SignalNotify_Pass) +{ + QJsonObject notifyData; + QObject::connect(PermissionConfig::instance(), &PermissionConfig::authoriseNotify, [&](const QJsonObject ¬ify){ + notifyData = notify; + }); + + auto TriggerFunction = [&](PermissionConfig::TidType tid, const QString &operate){ + // PermissionConfig::targetImagePath is empty. + PermissionConfig::instance()->triggerAction(tid, ""); + // ReportMode::ReportAndBroadcast = 0b11 + auto policyData = notifyData.value("policy").toObject(); + EXPECT_EQ(policyData.value("reportMode").toInt(), 3); + + QJsonObject data = notifyData.value("info").toObject(); + EXPECT_EQ(data.value("tid").toInt(), tid); + EXPECT_EQ(data.value("operate").toString(), operate); + }; + + PermissionConfig::instance()->status = PermissionConfig::NotOpen; + + TriggerFunction(PermissionConfig::TidOpen, "open"); + TriggerFunction(PermissionConfig::TidEdit, "edit"); + TriggerFunction(PermissionConfig::TidCopy, "copy"); + // Close will set PermissionConfig::valid to false. + TriggerFunction(PermissionConfig::TidClose, "close"); + EXPECT_FALSE(PermissionConfig::instance()->isValid()); + EXPECT_EQ(PermissionConfig::instance()->status, PermissionConfig::Close); +} + + From cda645499530fdd2b197a6122ed8818a75254b46 Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 22 Aug 2023 16:39:27 +0800 Subject: [PATCH 08/12] fix: Use point size replace pixel size. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用PointSize替换PixelSize Log: Use point size replace pixel size. Influence: WaterMark Change-Id: I2940e62a948683bec845d4bfb1aef27a03ed7db7 --- libimageviewer/service/permissionconfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 7b780eea..71afecd7 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -456,7 +456,7 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) #ifdef DTKWIDGET_CLASS_DWaterMarkHelper readWaterMark.type = WaterMarkType::Text; readWaterMark.font.setFamily(param.value("font").toString()); - readWaterMark.font.setPixelSize(param.value("fontSize").toInt()); + readWaterMark.font.setPointSize(param.value("fontSize").toInt()); QString colorName = param.value("color").toString(); if (!colorName.startsWith('#')) { From 2ff623973f23489ef8213145aa5d9c8320d4d052 Mon Sep 17 00:00:00 2001 From: renbin Date: Wed, 30 Aug 2023 14:08:17 +0800 Subject: [PATCH 09/12] fix: Print watermark close by default. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 打印水印预览流程和 dde-watermark-plugin 存在冲突, watermark-plugin 未配置时会过滤打开水印设置。 修改应用打印水印预览流程,检测到 watermark-plugin 时,将水印设置设置为环境变量(JSON配置),使用 watermark-plugin 插件设置水印。 Log: 修复打印预览默认未打开水印问题. Bug: https://pms.uniontech.com/bug-view-216845.html Influence: WaterMark Change-Id: Ie037430109fb665a8925c2827b3a0f92278a86aa --- libimageviewer/service/permissionconfig.cpp | 143 +++++++++++++++++++- libimageviewer/service/permissionconfig.h | 3 + tests/test_permissionconfig.cpp | 37 +++++ 3 files changed, 178 insertions(+), 5 deletions(-) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 71afecd7..e1357002 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -8,17 +8,27 @@ #include #include #include +#include #include +#include +#include #include #include #include #include #include -const QString g_KeyTid = "tid"; -const QString g_KeyOperate = "operate"; -const QString g_KeyFilePath = "filePath"; -const QString g_KeyRemaining = "remainingPrintCount"; +#include + +static const QString g_KeyTid = "tid"; +static const QString g_KeyOperate = "operate"; +static const QString g_KeyFilePath = "filePath"; +static const QString g_KeyRemaining = "remainingPrintCount"; + +// 打印水印插件 +static const char *g_WaterMarkEnv = "DEEPIN_WATERMARK"; +static const QString g_WaterMarkPluginName = "WaterMarkFilter"; +static const QString g_WaterMarkPluginClass = "PrintPreviewSettingsPlugin"; /** @brief 通过dbus接口从任务栏激活窗口 @@ -125,10 +135,12 @@ bool PermissionConfig::hasReadWaterMark() const /** @return 是否存在打印水印 + 当环境中存在打印水印插件时,将不再手动设置打印水印,而是通过环境变量设置调整打印水印插件的数据 + @sa detectWaterMarkPluginExists() */ bool PermissionConfig::hasPrintWaterMark() const { - return authFlags.testFlag(EnablePrintWaterMark); + return !useWaterMarkPlugin && authFlags.testFlag(EnablePrintWaterMark); } /** @@ -363,6 +375,11 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) << QString("Parse authorise config error at pos: %1, details: %2").arg(error.offset).arg(error.errorString()); } + // 存在打印水印设置时,检测是否存在打印水印插件,若存在则通过设置环境变量调用打印插件而不是手动设置 + if (authFlags.testFlag(EnablePrintWaterMark)) { + detectWaterMarkPluginExists(); + } + // 只要传入参数、图片即认为有效,无论参数是否正常解析 valid = true; // 首次触发打开图片 @@ -505,6 +522,122 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) #endif // DTKWIDGET_CLASS_DWaterMarkHelper } +/** + @brief 检测当前系统环境中是否存在打印水印插件 + 通过路径下是否存在指定的插件文件决定,使用DPKG判断不一定准确, + 文件可能被移除。 + @sa initWaterMarkPluginEnvironment() + */ +void PermissionConfig::detectWaterMarkPluginExists() +{ + if (useWaterMarkPlugin) { + return; + } + + // 参考 dprintpreviewdialog.cpp loadPlugin() 加载过程判断插件是否允许加载 + // Note: 打印插件初始化在创建时加载完成,因此此处不尝试加载 +#if defined(Q_OS_LINUX) + QString pluginPath = QLatin1String("/usr/share/deepin/dtk/plugins/printsupport"); +#else + QString pluginPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/printsupport") + + QLatin1String("/plugins"); +#endif + + if (!QFileInfo(pluginPath).exists()) { + return; + } + + QDir pluginDir(pluginPath); + QStringList fileList = pluginDir.entryList({"*PrintPreviewSettings*.so"}, QDir::Files); + + bool detected = false; + for (const QString &pluginName : fileList) { + // 能否正常加载 + QPluginLoader loader(pluginDir.absoluteFilePath(pluginName)); + QString className = loader.metaData().value("className").toString(); + qInfo() << qPrintable("Detect print preview plugin metadata:") << pluginName << loader.metaData(); + + if (g_WaterMarkPluginClass == className) { + detected = true; + break; + } + } + + if (detected) { + if (initWaterMarkPluginEnvironment()) { + // 成功初始化打印插件环境变量 + useWaterMarkPlugin = true; + qInfo() << qPrintable("Using watermark plugin to print preview instead of manual set."); + } else { + qWarning() << qPrintable("Detect watermark print preview plugin but init failed!"); + } + } else { + qInfo() << qPrintable("Not detect watermark print preview plugin, current plugins:") << fileList; + } +} + +/** + @brief 初始化打印水印插件环境变量 + @note 打印插件在 loadPlugin() 初始化时读取环境变量,需要注意设置环境变量时间 + @sa detectWaterMarkPluginExists() + */ +bool PermissionConfig::initWaterMarkPluginEnvironment() +{ + QJsonObject envData; + envData.insert("angle", static_cast(printWaterMark.rotation)); + envData.insert("transparency", static_cast(printWaterMark.opacity * 100)); + QFontMetrics fm(printWaterMark.font); + QSize textSize = fm.size(Qt::TextSingleLine, printWaterMark.text); + if (textSize.height() > 0) { + envData.insert("rowSpacing", qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()); + } + if (textSize.width() > 0) { + envData.insert("columnSpacing", qreal(printWaterMark.spacing + textSize.width()) / textSize.width()); + } + + envData.insert("layout", + static_cast(printWaterMark.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : + DPrintPreviewWatermarkInfo::Tiled)); + // 仅文本水印 + envData.insert("watermarkType", static_cast(DPrintPreviewWatermarkInfo::TextWatermark)); + envData.insert("textType", static_cast(DPrintPreviewWatermarkInfo::Custom)); + envData.insert("customText", printWaterMark.text); + envData.insert("textColor", printWaterMark.color.name()); + // 兼容插件传参格式 + QJsonArray fontList; + fontList.append(printWaterMark.font.family()); + envData.insert("fontList", fontList); + static const float sc_defaultFontSize = 65.0f; + // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 + envData.insert("size", int(printWaterMark.font.pixelSize() / sc_defaultFontSize * 100)); + + QJsonDocument doc; + doc.setObject(envData); + QByteArray envParam = doc.toJson(QJsonDocument::Compact); + qInfo() << qPrintable(QString("Set print preview plugin environment %1 PARAM: ").arg(g_WaterMarkEnv)) << envParam; + + if (qputenv(g_WaterMarkEnv, envParam)) { + DWIDGET_USE_NAMESPACE + // 打印水印插件可能非默认插件,设置后手动初始化及配置打印 + if (g_WaterMarkPluginName != DPrintPreviewDialog::currentPlugin()) { + QStringList plugins = DPrintPreviewDialog::availablePlugins(); + if (plugins.contains(g_WaterMarkPluginName)) { + DPrintPreviewDialog::setCurrentPlugin(g_WaterMarkPluginName); + } else { + qWarning() << qPrintable(QString("Print preview plugin not contain %1, all plugins:").arg(g_WaterMarkPluginName)) + << plugins; + return false; + } + } + + } else { + qWarning() << qPrintable("Set watermark plugin environment variable failed!"); + return false; + } + + return true; +} + /** @return 返回文件 \a fileName 是否符合权限标识 \a authFlag */ diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index 398abbd1..ee96d86b 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -90,6 +90,8 @@ class PermissionConfig : public QObject void initAuthorise(const QJsonObject ¶m); void initReadWaterMark(const QJsonObject ¶m); void initPrintWaterMark(const QJsonObject ¶m); + void detectWaterMarkPluginExists(); + bool initWaterMarkPluginEnvironment(); bool checkAuthInvalid(const QString &fileName = QString()) const; void reduceOnePrintCount(); @@ -106,6 +108,7 @@ class PermissionConfig : public QObject Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; + bool useWaterMarkPlugin = false; // 是否使用水印插件 #ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMark; WaterMarkData printWaterMark; diff --git a/tests/test_permissionconfig.cpp b/tests/test_permissionconfig.cpp index e4ad2ed2..37b32e6f 100644 --- a/tests/test_permissionconfig.cpp +++ b/tests/test_permissionconfig.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "service/permissionconfig.h" #include "imageengine.h" @@ -18,6 +20,7 @@ class UT_PermissionConfig : public ::testing::Test void UT_PermissionConfig::SetUp() { PermissionConfig::instance()->valid = true; + PermissionConfig::instance()->useWaterMarkPlugin = false; PermissionConfig::instance()->targetImagePath.clear(); PermissionConfig::instance()->currentImagePath.clear(); } @@ -25,6 +28,7 @@ void UT_PermissionConfig::SetUp() void UT_PermissionConfig::TearDown() { PermissionConfig::instance()->valid = false; + PermissionConfig::instance()->useWaterMarkPlugin = false; } static QString permission_config() @@ -207,4 +211,37 @@ TEST_F(UT_PermissionConfig, triggerAction_SignalNotify_Pass) EXPECT_EQ(PermissionConfig::instance()->status, PermissionConfig::Close); } +TEST_F(UT_PermissionConfig, hasPrintWaterMark_usingWaterMark_Pass) +{ + PermissionConfig::instance()->authFlags = PermissionConfig::EnablePrintWaterMark; + EXPECT_TRUE(PermissionConfig::instance()->hasPrintWaterMark()); + + PermissionConfig::instance()->useWaterMarkPlugin = true; + EXPECT_FALSE(PermissionConfig::instance()->hasPrintWaterMark()); +} +TEST_F(UT_PermissionConfig, detectWaterMarkPluginExists_Pass) +{ + PermissionConfig::instance()->detectWaterMarkPluginExists(); + + QStringList plugins = DPrintPreviewDialog::availablePlugins(); + if (plugins.contains("WaterMarkFilter")) { + EXPECT_TRUE(PermissionConfig::instance()->useWaterMarkPlugin); + } else { + EXPECT_FALSE(PermissionConfig::instance()->useWaterMarkPlugin); + } +} + +TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckPlugin_Pass) +{ + DWIDGET_USE_NAMESPACE + QStringList plugins = DPrintPreviewDialog::availablePlugins(); + if (plugins.contains("WaterMarkFilter")) { + EXPECT_TRUE(PermissionConfig::instance()->initWaterMarkPluginEnvironment()); + } else { + EXPECT_FALSE(PermissionConfig::instance()->initWaterMarkPluginEnvironment()); + } + + QByteArray envData = qgetenv("DEEPIN_WATERMARK"); + EXPECT_FALSE(envData.isEmpty()); +} From 49de69f5da6c661f419dcb0a19e23d8b6fbd39c4 Mon Sep 17 00:00:00 2001 From: renbin Date: Mon, 23 Oct 2023 11:30:10 +0800 Subject: [PATCH 10/12] fix: Print watermark configuration error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复打印水印间距和设置值不同 2. 设置打印水印后屏蔽设置界面 Log: 修复打印水印设置问题 Bug: https://pms.uniontech.com/bug-view-224595.html Influence: PrintWaterMark Change-Id: I8b98a876e4b94ae24f2f21c84789404e672a76e2 --- libimageviewer/service/permissionconfig.cpp | 22 ++++++-- libimageviewer/service/permissionconfig.h | 3 +- libimageviewer/widgets/printhelper.cpp | 22 +++++--- tests/test_permissionconfig.cpp | 56 +++++++++++++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index e1357002..db5dbb3f 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -457,6 +457,9 @@ void PermissionConfig::initAuthorise(const QJsonObject ¶m) authFlags.setFlag(EnableSwitch, param.value("pictureSwitch").toBool(false)); authFlags.setFlag(EnableWallpaper, param.value("setWallpaper").toBool(false)); + // Internal + ignoreDevicePixelRatio = param.value("ignoreDevicePixelRatio").toBool(false); + printLimitCount = param.value("printCount").toInt(0); } @@ -487,6 +490,13 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) readWaterMark.spacing = param.value("columnSpacing").toInt(); readWaterMark.text = param.value("text").toString(); + qreal deviceRatio = qApp->devicePixelRatio(); + if (ignoreDevicePixelRatio && !qFuzzyCompare(1.0, deviceRatio) && deviceRatio > 0) { + readWaterMark.font.setPointSizeF(readWaterMark.font.pointSizeF() / deviceRatio); + readWaterMark.lineSpacing /= deviceRatio; + readWaterMark.spacing /= deviceRatio; + } + authFlags.setFlag(EnableReadWaterMark, true); #endif // DTKWIDGET_CLASS_DWaterMarkHelper } @@ -504,7 +514,7 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) #ifdef DTKWIDGET_CLASS_DWaterMarkHelper printWaterMark.type = WaterMarkType::Text; printWaterMark.font.setFamily(param.value("font").toString()); - printWaterMark.font.setPixelSize(param.value("fontSize").toInt()); + printWaterMark.font.setPointSize(param.value("fontSize").toInt()); QString colorName = param.value("color").toString(); if (!colorName.startsWith('#')) { @@ -584,15 +594,16 @@ void PermissionConfig::detectWaterMarkPluginExists() bool PermissionConfig::initWaterMarkPluginEnvironment() { QJsonObject envData; +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper envData.insert("angle", static_cast(printWaterMark.rotation)); envData.insert("transparency", static_cast(printWaterMark.opacity * 100)); QFontMetrics fm(printWaterMark.font); QSize textSize = fm.size(Qt::TextSingleLine, printWaterMark.text); if (textSize.height() > 0) { - envData.insert("rowSpacing", qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()); + envData.insert("rowSpacing", qMax(0.0, (qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0)); } if (textSize.width() > 0) { - envData.insert("columnSpacing", qreal(printWaterMark.spacing + textSize.width()) / textSize.width()); + envData.insert("columnSpacing", qMax(0.0, (qreal(printWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0)); } envData.insert("layout", @@ -607,9 +618,10 @@ bool PermissionConfig::initWaterMarkPluginEnvironment() QJsonArray fontList; fontList.append(printWaterMark.font.family()); envData.insert("fontList", fontList); - static const float sc_defaultFontSize = 65.0f; + static const qreal sc_defaultFontSize = 65.0; // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 - envData.insert("size", int(printWaterMark.font.pixelSize() / sc_defaultFontSize * 100)); + envData.insert("size", int(printWaterMark.font.pointSizeF() / sc_defaultFontSize * 100)); +#endif QJsonDocument doc; doc.setObject(envData); diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index ee96d86b..ac744390 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -108,7 +108,8 @@ class PermissionConfig : public QObject Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; - bool useWaterMarkPlugin = false; // 是否使用水印插件 + bool ignoreDevicePixelRatio = false; // 过滤设备显示比率,按照原生像素计算 + bool useWaterMarkPlugin = false; // 是否使用水印插件 #ifdef DTKWIDGET_CLASS_DWaterMarkHelper WaterMarkData readWaterMark; WaterMarkData printWaterMark; diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 2da8f3d7..23b3d80e 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -143,8 +143,7 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) #endif #ifdef DTKWIDGET_CLASS_DWaterMarkHelper - // 定制分支,水印功能不依赖DTK版本 - // 更新打印水印设置 + // 定制分支,水印功能不依赖DTK版本,更新打印水印设置 if (PermissionConfig::instance()->hasPrintWaterMark()) { auto data = PermissionConfig::instance()->printWaterMarkData(); @@ -159,10 +158,10 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) QFontMetrics fm(data.font); QSize textSize = fm.size(Qt::TextSingleLine, data.text); if (textSize.height() > 0) { - info->rowSpacing = qreal(data.lineSpacing + textSize.height()) / textSize.height(); + info->rowSpacing = qMax(0.0, (qreal(data.lineSpacing + textSize.height()) / textSize.height()) - 1.0); } if (textSize.width() > 0) { - info->columnSpacing = qreal(data.spacing + textSize.width()) / textSize.width(); + info->columnSpacing = qMax(0.0, (qreal(data.spacing + textSize.width()) / textSize.width()) - 1.0); } info->layout = data.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : DPrintPreviewWatermarkInfo::Tiled; info->currentWatermarkType = (data.type == WaterMarkType::Text) ? DPrintPreviewWatermarkInfo::TextWatermark : DPrintPreviewWatermarkInfo::ImageWatermark; @@ -170,9 +169,9 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) info->customText = data.text; info->textColor = data.color; info->fontList.append(data.font.family()); - static const float sc_defaultFontSize = 65.0f; + static const qreal sc_defaultFontSize = 65.0; // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 - info->size = int(data.font.pixelSize() / sc_defaultFontSize * 100); + info->size = int(data.font.pointSizeF() / sc_defaultFontSize * 100); printDialog2.updateDialogSettingInfo(info); } else { qWarning() << qPrintable("Can't convert DPrintPreviewDialog watermark info.") << baseInfo->type(); @@ -182,6 +181,16 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) } else { qWarning() << qPrintable("Can't get DPrintPreviewDialog watermark info."); } + + // 在不使用打印插件时,需要手动设置打印插件选项显示且不可编辑,在设置后调用 + // WaterMarkFrame 和 WaterMarkContentFrame 是DTK窗口中水印设置控件的 objectName + auto widgetList = printDialog2.findChildren("WaterMarkFrame"); + widgetList.append(printDialog2.findChildren("WaterMarkContentFrame")); + for (auto wid: widgetList) { + wid->setVisible(true); + wid->setEnabled(false); + } + } #endif @@ -248,4 +257,3 @@ void RequestedSlot::paintRequestSync(DPrinter *_printer) } painter.end(); } - diff --git a/tests/test_permissionconfig.cpp b/tests/test_permissionconfig.cpp index 37b32e6f..85f75fd8 100644 --- a/tests/test_permissionconfig.cpp +++ b/tests/test_permissionconfig.cpp @@ -5,11 +5,15 @@ #include #include +#include + #include #include "service/permissionconfig.h" #include "imageengine.h" +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper + class UT_PermissionConfig : public ::testing::Test { public: @@ -245,3 +249,55 @@ TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckPlugin_Pass) QByteArray envData = qgetenv("DEEPIN_WATERMARK"); EXPECT_FALSE(envData.isEmpty()); } + +TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckValue_Pass) +{ + static const qreal sc_defaultFontSize = 65.0; + // 验证打印水印设置值 + WaterMarkData &watermark = PermissionConfig::instance()->printWaterMark; + watermark.rotation = 45; + watermark.opacity = 0.3; + watermark.font.setPointSize(30); + watermark.text = "123"; + watermark.lineSpacing = 0; + watermark.spacing = 0; + watermark.color = "#FF00FF"; + watermark.layout = WaterMarkLayout::Center; + + // 强制设置环境变量 + PermissionConfig::instance()->initWaterMarkPluginEnvironment(); + + QByteArray envData = qgetenv("DEEPIN_WATERMARK"); + QJsonDocument doc = QJsonDocument::fromJson(envData); + QJsonObject root = doc.object(); + + EXPECT_EQ(watermark.rotation, root.value("angle").toDouble()); + EXPECT_EQ(watermark.opacity, root.value("transparency").toDouble() / 100); + EXPECT_EQ(watermark.text, root.value("customText").toString()); + EXPECT_EQ(watermark.color, root.value("textColor").toString()); + EXPECT_EQ(DPrintPreviewWatermarkInfo::TextWatermark, root.value("watermarkType").toInt()); + EXPECT_EQ(DPrintPreviewWatermarkInfo::Custom, root.value("textType").toInt()); + EXPECT_EQ(DPrintPreviewWatermarkInfo::Center, root.value("layout").toInt()); + + qreal realFontSize = (root.value("size").toDouble() * sc_defaultFontSize) / 100.0; + EXPECT_EQ(watermark.font.pointSize(), qFloor(realFontSize + 0.5)); + EXPECT_TRUE(qFuzzyCompare(0, root.value("rowSpacing").toDouble())); + EXPECT_TRUE(qFuzzyCompare(0, root.value("columnSpacing").toDouble())); + + // 计算间距 + watermark.lineSpacing = 100; + watermark.spacing = 60; + PermissionConfig::instance()->initWaterMarkPluginEnvironment(); + envData = qgetenv("DEEPIN_WATERMARK"); + doc = QJsonDocument::fromJson(envData); + root = doc.object(); + + QFontMetrics fm(watermark.font); + QSize textSize = fm.size(Qt::TextSingleLine, watermark.text); + qreal realRowSpacing = (qreal(watermark.lineSpacing + textSize.height()) / textSize.height()) - 1.0; + qreal realColumnSpacing = (qreal(watermark.spacing + textSize.width()) / textSize.width()) - 1.0; + EXPECT_TRUE(qFuzzyCompare(realRowSpacing, root.value("rowSpacing").toDouble())); + EXPECT_TRUE(qFuzzyCompare(realColumnSpacing, root.value("columnSpacing").toDouble())); +} + +#endif From 315b3ab2cc2b409d5665afb407fdeb596f21e39e Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 24 Oct 2023 11:17:00 +0800 Subject: [PATCH 11/12] fix: Print watermark spacing error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTK限制了打印水印间距,增加配置项以允许捕获 DTK水印设置,强制设置水印间距,默认false, 在需要时提供设置 `breakPrintSpacingLimit`。 Log: 修复打印水印间距问题 Influence: PrintWaterMark Change-Id: I3035a9735271f9a29b04023017744637c1ad76ea --- libimageviewer/service/permissionconfig.cpp | 93 ++++++++++++++++++++- libimageviewer/service/permissionconfig.h | 9 ++ libimageviewer/widgets/printhelper.cpp | 5 ++ tests/test_permissionconfig.cpp | 85 ++++++++++++++++++- 4 files changed, 190 insertions(+), 2 deletions(-) diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index db5dbb3f..8aa0fdb2 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -30,6 +30,12 @@ static const char *g_WaterMarkEnv = "DEEPIN_WATERMARK"; static const QString g_WaterMarkPluginName = "WaterMarkFilter"; static const QString g_WaterMarkPluginClass = "PrintPreviewSettingsPlugin"; +// 打破打印水印间距限制 +static const char *g_PrintRowSpacingProp = "_d_print_waterMarkRowSpacing"; +static const char *g_PrintColumnSpacingProp = "_d_print_waterMarkColumnSpacing"; +static const qreal g_PrintRowSpacingLimit = 10.0; +static const qreal g_PrintColumnSpacingLimit = 2.0; + /** @brief 通过dbus接口从任务栏激活窗口 */ @@ -264,6 +270,40 @@ void PermissionConfig::triggerNotify(const QJsonObject &data) Q_EMIT authoriseNotify(sendData); } +/** + @brief 过滤 \a watched (DPrintPreviewWidget)的 DynamicPropertyChange 事件, + 用于打破 DTK 对打印间距的限制,和设置值一致,仅在设置 breakPrintSpacingLimit 后有效 + @sa installFilterPrintDialog + */ +bool PermissionConfig::eventFilter(QObject *watched, QEvent *event) +{ + if (QEvent::DynamicPropertyChange == event->type() && watched) { + QDynamicPropertyChangeEvent *propEvent = dynamic_cast(event); + if (!propEvent) { + return false; + } + + auto checkFunc = [&](const char *prop, qreal value, qreal limit){ + if (QByteArray(prop) != propEvent->propertyName() + || (value <= limit)) { + return; + } + + qreal curValue = watched->property(prop).toDouble(); + if (!qFuzzyCompare(curValue, value)) { + // 强制设置间距和预期值一致 + watched->setProperty(prop, value); + } + }; + + // 检查 rowSpacing 和 columnSpacing, DynamicPropertyChange 只是事后通知,无需实际过滤 + checkFunc(g_PrintRowSpacingProp, printRowSpacing, g_PrintRowSpacingLimit); + checkFunc(g_PrintColumnSpacingProp, printColumnSpacing, g_PrintColumnSpacingLimit); + } + + return false; +} + /** @brief 设置当前处理的文件路径为 \a fileName */ @@ -401,6 +441,33 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) } } +/** + @brief 添加 \a dialog 的过滤器,这是用于解除 DTK 中对打印水印间距的限制 + 仅在设置 `breakPrintSpacingLimit` 权限后,且打印水印间距系数超过 DTK 限制时有效。 + 此函数时预留的,用于预防后期新增需求。 + */ +bool PermissionConfig::installFilterPrintDialog(DPrintPreviewDialog *dialog) +{ + if (!breakPrintSpacingLimit) { + return false; + } + + // 判断是否需要调整间距,若间距没有超过限制,则无需调整 + if (!(printRowSpacing > g_PrintRowSpacingLimit || printColumnSpacing > g_PrintColumnSpacingLimit)) { + return false; + } + + if (dialog) { + DPrintPreviewWidget *widget = dialog->findChild(); + if (widget) { + widget->installEventFilter(this); + return true; + } + } + + return false; +} + /** @return 解析命令行参数并返回设置的权限和水印配置,为设置则返回空 */ @@ -457,8 +524,15 @@ void PermissionConfig::initAuthorise(const QJsonObject ¶m) authFlags.setFlag(EnableSwitch, param.value("pictureSwitch").toBool(false)); authFlags.setFlag(EnableWallpaper, param.value("setWallpaper").toBool(false)); - // Internal + // Internal 内部使用,默认均为false ignoreDevicePixelRatio = param.value("ignoreDevicePixelRatio").toBool(false); + breakPrintSpacingLimit = param.value("breakPrintSpacingLimit").toBool(false); + if (ignoreDevicePixelRatio) { + qInfo() << qPrintable("Enable internal property: ignore device pixel ratio."); + } + if (breakPrintSpacingLimit) { + qInfo() << qPrintable("Enable internal property: break print spacing limit."); + } printLimitCount = param.value("printCount").toInt(0); } @@ -529,6 +603,23 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) printWaterMark.text = param.value("text").toString(); authFlags.setFlag(EnablePrintWaterMark, true); + + // 计算 DTK 打印水印的间距转换系数,限制提升到 10000.0 + if (!printWaterMark.text.isEmpty()) { + QFontMetrics fm(printWaterMark.font); + QSize textSize = fm.size(Qt::TextSingleLine, printWaterMark.text); + if (textSize.height() > 0) { + printRowSpacing = (qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0; + printRowSpacing = qBound(0.0, printRowSpacing, 10000.0); + } + if (textSize.width() > 0) { + printColumnSpacing = (qreal(printWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0; + printColumnSpacing = qBound(0.0, printColumnSpacing, 10000.0); + } + + qInfo() << QString("Print config spacing ratio row: %1 column: %2").arg(printRowSpacing).arg(printColumnSpacing); + } + #endif // DTKWIDGET_CLASS_DWaterMarkHelper } diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index ac744390..6a50e22f 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -12,6 +12,7 @@ #ifdef DTKWIDGET_CLASS_DWaterMarkHelper #include +#include #include DWIDGET_USE_NAMESPACE @@ -84,6 +85,8 @@ class PermissionConfig : public QObject Q_SLOT void activateProcess(qint64 pid); void initFromArguments(const QStringList &arguments); + bool installFilterPrintDialog(DPrintPreviewDialog *dialog); + private: // 解析配置 bool parseConfigOption(const QStringList &arguments, QString &configParam, QStringList &imageList) const; @@ -98,6 +101,8 @@ class PermissionConfig : public QObject void triggerNotify(const QJsonObject &data); + bool eventFilter(QObject *watched, QEvent *event) override; + private: enum Status { NotOpen, Open, Close }; @@ -114,6 +119,10 @@ class PermissionConfig : public QObject WaterMarkData readWaterMark; WaterMarkData printWaterMark; #endif // DTKWIDGET_CLASS_DWaterMarkHelper + + bool breakPrintSpacingLimit = false; // 打破打印间距限制 + qreal printRowSpacing = 0.0; + qreal printColumnSpacing = 0.0; }; #endif // PERMISSIONCONFIG_H diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index 23b3d80e..bec3a670 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -143,6 +143,11 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) #endif #ifdef DTKWIDGET_CLASS_DWaterMarkHelper + // 检查是否过滤 DTK 部分设置 + if (PermissionConfig::instance()->installFilterPrintDialog(&printDialog2)) { + qInfo() << qPrintable("Enable breakPrintSpacingLimit, filter print spacing config."); + } + // 定制分支,水印功能不依赖DTK版本,更新打印水印设置 if (PermissionConfig::instance()->hasPrintWaterMark()) { auto data = PermissionConfig::instance()->printWaterMarkData(); diff --git a/tests/test_permissionconfig.cpp b/tests/test_permissionconfig.cpp index 85f75fd8..0aa370e8 100644 --- a/tests/test_permissionconfig.cpp +++ b/tests/test_permissionconfig.cpp @@ -37,7 +37,9 @@ void UT_PermissionConfig::TearDown() static QString permission_config() { - QByteArray permission("{ \"permission\": { \"edit\": true, \"copy\": true, \"setWallpaper\": true, \"pictureSwitch\": true, \"printCount\": 2 } }"); + QByteArray permission("{ \"permission\": { \"edit\": true, \"copy\": true, \"setWallpaper\": true, " + "\"pictureSwitch\": true, \"printCount\": 2, " + "\"ignoreDevicePixelRatio\": true, \"breakPrintSpacingLimit\": true } }"); return permission.toBase64(); } @@ -93,6 +95,9 @@ TEST_F(UT_PermissionConfig, initFromArguments_NormalParam_Pass) PermissionConfig::EnableCopy | PermissionConfig::EnableEdit | PermissionConfig::EnableSwitch | PermissionConfig::EnableWallpaper); EXPECT_EQ(PermissionConfig::instance()->authFlags, auth); EXPECT_EQ(PermissionConfig::instance()->targetImagePath, tempPath); + + EXPECT_EQ(PermissionConfig::instance()->ignoreDevicePixelRatio, true); + EXPECT_EQ(PermissionConfig::instance()->breakPrintSpacingLimit, true); } TEST_F(UT_PermissionConfig, initFromArguments_NormalParam_Fail) @@ -300,4 +305,82 @@ TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckValue_Pass) EXPECT_TRUE(qFuzzyCompare(realColumnSpacing, root.value("columnSpacing").toDouble())); } +TEST_F(UT_PermissionConfig, installFilterPrintDialog_Filter_Disable) +{ + auto ins = PermissionConfig::instance(); + DPrintPreviewDialog dialog; + + ins->breakPrintSpacingLimit = false; + ins->printRowSpacing = 10.0; + ins->printColumnSpacing = 2.0; + EXPECT_FALSE(ins->installFilterPrintDialog(&dialog)); + + ins->breakPrintSpacingLimit = true; + EXPECT_FALSE(ins->installFilterPrintDialog(&dialog)); +} + +TEST_F(UT_PermissionConfig, installFilterPrintDialog_Filter_Enable) +{ + auto ins = PermissionConfig::instance(); + DPrintPreviewDialog dialog; + + ins->breakPrintSpacingLimit = true; + ins->printRowSpacing = 11.0; + ins->printColumnSpacing = 2.0; + EXPECT_TRUE(ins->installFilterPrintDialog(&dialog)); + + ins->printRowSpacing = 0; + ins->printColumnSpacing = 2.1; + EXPECT_TRUE(ins->installFilterPrintDialog(&dialog)); +} + +TEST_F(UT_PermissionConfig, installFilterPrintDialog_FilterData_Pass) +{ + auto ins = PermissionConfig::instance(); + DPrintPreviewDialog dialog; + + ins->breakPrintSpacingLimit = true; + ins->printRowSpacing = 11.0; + ins->printColumnSpacing = 2.1; + EXPECT_TRUE(ins->installFilterPrintDialog(&dialog)); + + DPrintPreviewWidget *widget = dialog.findChild(); + ASSERT_NE(nullptr, widget); + + widget->setProperty("_d_print_waterMarkRowSpacing", 2.0); + EXPECT_NE(widget->property("_d_print_waterMarkRowSpacing").toReal(), 2.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkRowSpacing").toReal(), 11.0)); + widget->setProperty("_d_print_waterMarkColumnSpacing", 1.0); + EXPECT_NE(widget->property("_d_print_waterMarkColumnSpacing").toReal(), 2.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkColumnSpacing").toReal(), 2.1)); + + ins->printRowSpacing = 15.0; + ins->printColumnSpacing = 12.0; + widget->setProperty("_d_print_waterMarkRowSpacing", 2.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkRowSpacing").toReal(), 15.0)); + widget->setProperty("_d_print_waterMarkColumnSpacing", 1.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkColumnSpacing").toReal(), 12.0)); +} + +TEST_F(UT_PermissionConfig, installFilterPrintDialog_FilterData_Ignore) +{ + auto ins = PermissionConfig::instance(); + DPrintPreviewDialog dialog; + + ins->breakPrintSpacingLimit = true; + ins->printRowSpacing = 11.0; + ins->printColumnSpacing = 2.1; + EXPECT_TRUE(ins->installFilterPrintDialog(&dialog)); + + DPrintPreviewWidget *widget = dialog.findChild(); + ASSERT_NE(nullptr, widget); + + ins->printRowSpacing = 5.0; + ins->printColumnSpacing = 1.0; + widget->setProperty("_d_print_waterMarkRowSpacing", 2.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkRowSpacing").toReal(), 2.0)); + widget->setProperty("_d_print_waterMarkColumnSpacing", 1.0); + EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkColumnSpacing").toReal(), 1.0)); +} + #endif From 8fee59ce75e823c28c95ef20f4f4df190eb765cd Mon Sep 17 00:00:00 2001 From: renbin Date: Mon, 15 Jan 2024 14:12:16 +0800 Subject: [PATCH 12/12] feat: Merge permission/watermark into master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并权限控制和水印需求到主线. * 新增权限控制(PermissionConfig), 允许配置功能开闭; * 新增水印功能, 允许通过配置定制阅读和打印水印; * 水印接口特殊, DTKWidget主线和定制线的接口不同; * 调整合入代码以兼容各版本. Log: 回合看图权限控制和水印需求 Task: https://pms.uniontech.com/story-view-27785.html Task: https://pms.uniontech.com/story-view-30217.html Influence: Print Permission --- libimageviewer/imageengine.cpp | 44 ++- libimageviewer/imageengine.h | 2 +- libimageviewer/imageviewer.cpp | 61 ++-- libimageviewer/imageviewer.h | 51 ++-- libimageviewer/service/permissionconfig.cpp | 294 ++++++++++++++------ libimageviewer/service/permissionconfig.h | 56 +++- libimageviewer/unionimage/baseutils.cpp | 1 - libimageviewer/viewpanel/viewpanel.cpp | 3 +- libimageviewer/viewpanel/viewpanel.h | 2 +- libimageviewer/widgets/printhelper.cpp | 147 ++-------- libimageviewer/widgets/printhelper.h | 11 +- tests/test_permissionconfig.cpp | 143 ++++++++-- 12 files changed, 496 insertions(+), 319 deletions(-) diff --git a/libimageviewer/imageengine.cpp b/libimageviewer/imageengine.cpp index 491ba0fb..941f0432 100644 --- a/libimageviewer/imageengine.cpp +++ b/libimageviewer/imageengine.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -17,6 +17,7 @@ #include "unionimage/unionimage.h" #include "service/permissionconfig.h" #include "service/imagedataservice.h" + class ImageEnginePrivate { public: @@ -26,11 +27,13 @@ class ImageEnginePrivate public: ImageEngine *const q_ptr; LibImgOperate *m_worker = nullptr; -// QString m_thumbnailSavePath;//缩略图保存路径 + Q_DECLARE_PUBLIC(ImageEngine) + Q_DISABLE_COPY(ImageEnginePrivate) }; -ImageEnginePrivate::ImageEnginePrivate(ImageEngine *parent): q_ptr(parent) +ImageEnginePrivate::ImageEnginePrivate(ImageEngine *parent) + : q_ptr(parent) { Q_Q(ImageEngine); // 关联授权操作通知信号 @@ -44,10 +47,7 @@ ImageEnginePrivate::ImageEnginePrivate(ImageEngine *parent): q_ptr(parent) workerThread->start(); } -ImageEnginePrivate::~ImageEnginePrivate() -{ - -} +ImageEnginePrivate::~ImageEnginePrivate() {} ImageEngine *ImageEngine::m_ImageEngine = nullptr; @@ -64,28 +64,23 @@ ImageEngine::ImageEngine(QWidget *parent) : QObject(parent) , d_ptr(new ImageEnginePrivate(this)) { -// Q_D(ImageEngine); } -ImageEngine::~ImageEngine() -{ - -} +ImageEngine::~ImageEngine() {} +/** + @brief 构造传入图像 `paths` 的缩略图到路径 `thumbnailSavePath` , `remake` 标识是否重新创建 + @note 由于兼容性问题,此处的 `thumbnailSavePath` `paths` 没有调整为引用传递 + */ void ImageEngine::makeImgThumbnail(QString thumbnailSavePath, QStringList paths, int makeCount, bool remake) { //执行子线程制作缩略图动作 -// QMetaObject::invokeMethod(d->m_worker, "slotMakeImgThumbnail" -// , Q_ARG(QString, thumbnailSavePath) -// , Q_ARG(QStringList, paths) -// , Q_ARG(int, makeCount) -// , Q_ARG(bool, remake) -// ); Q_UNUSED(makeCount); if (paths.count() > 0) { LibImageDataService::instance()->readThumbnailByPaths(thumbnailSavePath, paths, remake); } } + //判断是否图片格式 bool ImageEngine::isImage(const QString &path) { @@ -93,8 +88,8 @@ bool ImageEngine::isImage(const QString &path) QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(path, QMimeDatabase::MatchContent); QMimeType mt1 = db.mimeTypeForFile(path, QMimeDatabase::MatchExtension); - if (mt.name().startsWith("image/") || mt.name().startsWith("video/x-mng") || - mt1.name().startsWith("image/") || mt1.name().startsWith("video/x-mng")) { + if (mt.name().startsWith("image/") || mt.name().startsWith("video/x-mng") || mt1.name().startsWith("image/") || + mt1.name().startsWith("video/x-mng")) { bRet = true; } return bRet; @@ -102,7 +97,7 @@ bool ImageEngine::isImage(const QString &path) bool ImageEngine::isRotatable(const QString &path) { - //BUG#93143 + // BUG#93143 //由于底层代码判断是否可旋转的依据是文件名后缀所代表的图片是否可保存 //因此文件是否存在的判断添加在这一层 QFileInfo info(path); @@ -112,15 +107,16 @@ bool ImageEngine::isRotatable(const QString &path) return LibUnionImage_NameSpace::isImageSupportRotate(path); } } + //根据文件路径制作md5 QString ImageEngine::makeMD5(const QString &path) { QFile file(path); - QString stHashValue; - if (file.open(QIODevice::ReadOnly)) { //只读方式打开 + QString stHashValue; + if (file.open(QIODevice::ReadOnly)) { //只读方式打开 QCryptographicHash hash(QCryptographicHash::Md5); - QByteArray buf = file.read(10 * 1024 * 1024); // 每次读取10M + QByteArray buf = file.read(10 * 1024 * 1024); // 每次读取10M buf = buf.append(path.toUtf8()); hash.addData(buf); // 将数据添加到Hash中 stHashValue.append(hash.result().toHex()); diff --git a/libimageviewer/imageengine.h b/libimageviewer/imageengine.h index c00fb072..e54784aa 100644 --- a/libimageviewer/imageengine.h +++ b/libimageviewer/imageengine.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/libimageviewer/imageviewer.cpp b/libimageviewer/imageviewer.cpp index 9e65f383..7864de7b 100644 --- a/libimageviewer/imageviewer.cpp +++ b/libimageviewer/imageviewer.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -31,15 +31,24 @@ class ImageViewerPrivate { public: - ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar, ImageViewer *parent); + ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewerType, + const QString &savePath, + AbstractTopToolbar *customTopToolbar, + ImageViewer *parent); + private: - ImageViewer *q_ptr; - LibViewPanel *m_panel = nullptr; - imageViewerSpace::ImgViewerType m_imgViewerType; + ImageViewer *q_ptr; + LibViewPanel *m_panel = nullptr; + imageViewerSpace::ImgViewerType m_imgViewerType; + Q_DECLARE_PUBLIC(ImageViewer) + Q_DISABLE_COPY(ImageViewerPrivate) }; -ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar, ImageViewer *parent) +ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewerType, + const QString &savePath, + AbstractTopToolbar *customTopToolbar, + ImageViewer *parent) : q_ptr(parent) { // 在界面前初始化授权配置 @@ -64,8 +73,7 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer QStringList parseLocalNameList = QLocale::system().name().split("_", QString::SkipEmptyParts); if (parseLocalNameList.length() > 0) { - QString translateFilename = QString("/libimageviewer_%2.qm") - .arg(parseLocalNameList.at(0)); + QString translateFilename = QString("/libimageviewer_%2.qm").arg(parseLocalNameList.at(0)); QString translatePath = PLUGINTRANSPATH + translateFilename; if (QFile::exists(translatePath)) { @@ -98,21 +106,31 @@ ImageViewerPrivate::ImageViewerPrivate(imageViewerSpace::ImgViewerType imgViewer DWaterMarkHelper::instance()->registerWidget(m_panel); // 仅权限控制图片单独展示 - QObject::connect(PermissionConfig::instance(), &PermissionConfig::currentImagePathChanged, q_ptr, [ this ](const QString &, bool isTargetImage){ - if (!PermissionConfig::instance()->isValid()) { - return; - } - - DWaterMarkWidget *mark = m_panel->findChild(); - if (mark) { - mark->setVisible(isTargetImage); - } - }); + QObject::connect(PermissionConfig::instance(), + &PermissionConfig::currentImagePathChanged, + q_ptr, + [this](const QString &, bool isTargetImage) { + if (!PermissionConfig::instance()->isValid()) { + return; + } + + DWaterMarkWidget *mark = m_panel->findChild(); + if (mark) { + mark->setVisible(isTargetImage); + } + }); } #endif } -ImageViewer::ImageViewer(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar, QWidget *parent) +/** + @brief 构造视图展示控件 + @note 由于兼容性问题,此处的 `savePath` 没有调整为引用传递 + */ +ImageViewer::ImageViewer(imageViewerSpace::ImgViewerType imgViewerType, + QString savePath, + AbstractTopToolbar *customTopToolbar, + QWidget *parent) : DWidget(parent) , d_ptr(new ImageViewerPrivate(imgViewerType, savePath, customTopToolbar, this)) { @@ -141,7 +159,8 @@ bool ImageViewer::startdragImage(const QStringList &paths, const QString &firstP return d->m_panel->startdragImage(paths, firstPath); } -bool ImageViewer::startdragImageWithUID(const QStringList &paths, const QString &firstPath, bool isCustom, const QString &album, int UID) +bool ImageViewer::startdragImageWithUID( + const QStringList &paths, const QString &firstPath, bool isCustom, const QString &album, int UID) { Q_D(ImageViewer); d->m_panel->setIsCustomAlbumWithUID(isCustom, album, UID); @@ -161,7 +180,7 @@ void ImageViewer::startImgView(const QString ¤tPath, const QStringList &pa //启动线程制作缩略图 if (LibCommonService::instance()->getImgViewerType() == imageViewerSpace::ImgViewerTypeLocal || - LibCommonService::instance()->getImgViewerType() == imageViewerSpace::ImgViewerTypeNull) { + LibCommonService::instance()->getImgViewerType() == imageViewerSpace::ImgViewerTypeNull) { //首先生成当前图片的缓存 ImageEngine::instance()->makeImgThumbnail(LibCommonService::instance()->getImgSavePath(), QStringList(realPath), 1); //看图制作全部缩略图 diff --git a/libimageviewer/imageviewer.h b/libimageviewer/imageviewer.h index 9ab0f0e5..b336f22f 100644 --- a/libimageviewer/imageviewer.h +++ b/libimageviewer/imageviewer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -14,69 +14,74 @@ class AbstractTopToolbar; DWIDGET_USE_NAMESPACE - class ImageViewerPrivate; class IMAGEVIEWERSHARED_EXPORT ImageViewer : public DWidget { Q_OBJECT public: - //ImgViewerType:图片展示类型, savePath:缩略图保存位置,customTopToolbar:自定义标题栏,nullptr的时候使用内置方案 - explicit ImageViewer(imageViewerSpace::ImgViewerType imgViewerType, QString savePath, AbstractTopToolbar *customTopToolbar = nullptr, QWidget *parent = nullptr); + // ImgViewerType:图片展示类型, savePath:缩略图保存位置,customTopToolbar:自定义标题栏,nullptr的时候使用内置方案 + explicit ImageViewer(imageViewerSpace::ImgViewerType imgViewerType, + QString savePath, + AbstractTopToolbar *customTopToolbar = nullptr, + QWidget *parent = nullptr); ~ImageViewer() override; - //调用文件选择窗口 + // 调用文件选择窗口 bool startChooseFileDialog(); - //传入路径加载图片 paths:所有照片 firstPath:第一张 isCustom:是否是自定义相册 album:自定义相册名称 - bool startdragImage(const QStringList &paths, const QString &firstPath = "", bool isCustom = false, const QString &album = ""); + // 传入路径加载图片 paths:所有照片 firstPath:第一张 isCustom:是否是自定义相册 album:自定义相册名称 + bool + startdragImage(const QStringList &paths, const QString &firstPath = "", bool isCustom = false, const QString &album = ""); - //传入路径加载图片 paths:所有照片 firstPath:第一张 isCustom:是否是自定义相册 album:自定义相册名称 UID:相册的UID - bool startdragImageWithUID(const QStringList &paths, const QString &firstPath = "", bool isCustom = false, const QString &album = "", int UID = -1); + // 传入路径加载图片 paths:所有照片 firstPath:第一张 isCustom:是否是自定义相册 album:自定义相册名称 UID:相册的UID + bool startdragImageWithUID( + const QStringList &paths, const QString &firstPath = "", bool isCustom = false, const QString &album = "", int UID = -1); - //启动图片展示入口 + // 启动图片展示入口 void startImgView(const QString ¤tPath, const QStringList &paths = QStringList()); - //设置topbar的显示和隐藏 + // 设置topbar的显示和隐藏 void setTopBarVisible(bool visible); - //设置Bottomtoolbar的显示和隐藏 + // 设置Bottomtoolbar的显示和隐藏 void setBottomtoolbarVisible(bool visible); - //获得工具栏按钮 + // 获得工具栏按钮 DIconButton *getBottomtoolbarButton(imageViewerSpace::ButtonType type); - //获得当前展示图片路径 + // 获得当前展示图片路径 QString getCurrentPath(); - //二次开发接口 + // 二次开发接口 - //设置图片显示panel右键菜单的显示和隐藏,false为永久隐藏,true为跟随原有策略显示或隐藏 + // 设置图片显示panel右键菜单的显示和隐藏,false为永久隐藏,true为跟随原有策略显示或隐藏 void setViewPanelContextMenuItemVisible(imageViewerSpace::NormalMenuItemId id, bool visible); - //设置下方工具栏按钮的显示和隐藏,true为永久隐藏,false为跟随原有策略显示或隐藏 + // 设置下方工具栏按钮的显示和隐藏,true为永久隐藏,false为跟随原有策略显示或隐藏 void setBottomToolBarButtonAlawysNotVisible(imageViewerSpace::ButtonType id, bool notVisible); - //相册-给右键菜单传输自定义相册名称 + // 相册-给右键菜单传输自定义相册名称 void setCustomAlbumName(const QMap map, bool isFav); - //相册-给右键菜单传输自定义相册名称的UID方案接口,map的参数依次是UID,相册名,是否在此相册 + // 相册-给右键菜单传输自定义相册名称的UID方案接口,map的参数依次是UID,相册名,是否在此相册 void setCustomAlbumNameAndUID(const QMap > &map, bool isFav); - //设置全屏/退出全屏 + // 设置全屏/退出全屏 void switchFullScreen(); - //设置启动幻灯片 + // 设置启动幻灯片 void startSlideShow(const QStringList &paths, const QString &firstPath); - //设置panel拖拽使能 + // 设置panel拖拽使能 void setDropEnabled(bool enable); protected: void resizeEvent(QResizeEvent *e) override; void showEvent(QShowEvent *e) override; + private: QScopedPointer d_ptr; Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), ImageViewer) }; -#endif // IMAGEVIEWER_H +#endif // IMAGEVIEWER_H diff --git a/libimageviewer/service/permissionconfig.cpp b/libimageviewer/service/permissionconfig.cpp index 8aa0fdb2..41261852 100644 --- a/libimageviewer/service/permissionconfig.cpp +++ b/libimageviewer/service/permissionconfig.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -18,7 +18,10 @@ #include #include -#include +// dprintpreviewsettinginfo 没有单独宏,使用 DWaterMarkHelper (版本更前)进行限制 +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper +#include +#endif static const QString g_KeyTid = "tid"; static const QString g_KeyOperate = "operate"; @@ -36,6 +39,9 @@ static const char *g_PrintColumnSpacingProp = "_d_print_waterMarkColumnSpacing"; static const qreal g_PrintRowSpacingLimit = 10.0; static const qreal g_PrintColumnSpacingLimit = 2.0; +// 打印水印默认字体大小,用于计算转换系数 +static const qreal g_DefaultPrintFontSize = 65.0; + /** @brief 通过dbus接口从任务栏激活窗口 */ @@ -70,7 +76,7 @@ bool activateWindowFromDock(quintptr winId) } /** - @class AuthoriseConfig + @class PermissionConfig @brief 授权控制类,提供操作授权和水印配置 @details 授权控制主要包括编辑、拷贝、删除、保存等操作,水印包括阅读水印及打印水印。 配置信息通过命令行参数获取,当授权控制开启时,进行主要操作将自动发送通知信息。 @@ -283,9 +289,8 @@ bool PermissionConfig::eventFilter(QObject *watched, QEvent *event) return false; } - auto checkFunc = [&](const char *prop, qreal value, qreal limit){ - if (QByteArray(prop) != propEvent->propertyName() - || (value <= limit)) { + auto checkFunc = [&](const char *prop, qreal value, qreal limit) { + if (QByteArray(prop) != propEvent->propertyName() || (value <= limit)) { return; } @@ -345,6 +350,101 @@ WaterMarkData PermissionConfig::printWaterMarkData() const return printWaterMark; } +/** + @brief 添加 \a dialog 的过滤器,这是用于解除 DTK 中对打印水印间距的限制 + 仅在设置 `breakPrintSpacingLimit` 权限后,且打印水印间距系数超过 DTK 限制时有效。 + 此函数时预留的,用于预防后期新增需求。 + + 打印的插件接口在5.5.50后提供,DTKWIDGET_CLASS_DWaterMarkHelper(5.6.9)包含这部分功能 + */ +bool PermissionConfig::installFilterPrintDialog(DPrintPreviewDialog *dialog) +{ + if (!breakPrintSpacingLimit) { + return false; + } + + // 判断是否需要调整间距,若间距没有超过限制,则无需调整 + if (!(printRowSpacing > g_PrintRowSpacingLimit || printColumnSpacing > g_PrintColumnSpacingLimit)) { + return false; + } + + if (dialog) { + DPrintPreviewWidget *widget = dialog->findChild(); + if (widget) { + widget->installEventFilter(this); + return true; + } + } + + return false; +} + +/** + @brief 设置打印对话框 `dialog` 的打印水印格式,当系统环境中存在打印插件时,将不会进入此函数。 + */ +bool PermissionConfig::setDialogPrintWatermark(DPrintPreviewDialog *dialog) const +{ + if (!dialog) { + return false; + } + + // 使用适配的水印配置 + bool ret = false; + const AdapterWaterMarkData &data = printAdapterWaterMark; + DPrintPreviewSettingInfo *baseInfo = dialog->createDialogSettingInfo(DPrintPreviewWatermarkInfo::PS_Watermark); + + if (baseInfo) { + DPrintPreviewWatermarkInfo *info = dynamic_cast(baseInfo); + if (info) { + // 打印水印实现方式同阅读水印略有不同,调整参数以使得效果一致。 + info->opened = true; + + info->angle = static_cast(data.rotation); + info->transparency = static_cast(data.opacity * 100); + QFontMetrics fm(data.font); + QSize textSize = fm.size(Qt::TextSingleLine, data.text); + if (textSize.height() > 0) { + info->rowSpacing = qMax(0.0, (qreal(data.lineSpacing + textSize.height()) / textSize.height()) - 1.0); + } + if (textSize.width() > 0) { + info->columnSpacing = qMax(0.0, (qreal(data.spacing + textSize.width()) / textSize.width()) - 1.0); + } + info->layout = (data.layout == PermissionConfig::AdapterWaterMarkData::Center) ? DPrintPreviewWatermarkInfo::Center : + DPrintPreviewWatermarkInfo::Tiled; + info->currentWatermarkType = (data.type == PermissionConfig::AdapterWaterMarkData::Text) ? + DPrintPreviewWatermarkInfo::TextWatermark : + DPrintPreviewWatermarkInfo::ImageWatermark; + info->textType = DPrintPreviewWatermarkInfo::Custom; + info->customText = data.text; + info->textColor = data.color; + info->fontList.append(data.font.family()); + // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 + info->size = int(data.font.pointSizeF() / g_DefaultPrintFontSize * 100); + + dialog->updateDialogSettingInfo(info); + + ret = true; + } else { + qWarning() << qPrintable("Can't convert DPrintPreviewDialog watermark info.") << baseInfo->type(); + } + + delete baseInfo; + } else { + qWarning() << qPrintable("Can't get DPrintPreviewDialog watermark info."); + } + + // 在不使用打印插件时,需要手动设置打印插件选项显示且不可编辑,在设置后调用 + // WaterMarkFrame 和 WaterMarkContentFrame 是DTK窗口中水印设置控件的 objectName + auto widgetList = dialog->findChildren("WaterMarkFrame"); + widgetList.append(dialog->findChildren("WaterMarkContentFrame")); + for (auto wid : widgetList) { + wid->setVisible(true); + wid->setEnabled(false); + } + + return ret; +} + #endif // DTKWIDGET_CLASS_DWaterMarkHelper /** @@ -381,8 +481,8 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) QString configParam; QStringList imageList; - bool ret = parseConfigOption(arguments, configParam, imageList); - if (ret) { + bool parseRet = parseConfigOption(arguments, configParam, imageList); + if (parseRet) { // 获取带权限控制的文件路径 for (const QString &arg : imageList) { QFileInfo info(arg); @@ -406,8 +506,10 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) if (!doc.isNull()) { QJsonObject root = doc.object(); initAuthorise(root.value("permission").toObject()); +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper initReadWaterMark(root.value("readWatermark").toObject()); initPrintWaterMark(root.value("printWatermark").toObject()); +#endif // DTKWIDGET_CLASS_DWaterMarkHelper qInfo() << qPrintable("Current Enable permission") << authFlags; } else { @@ -415,10 +517,12 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) << QString("Parse authorise config error at pos: %1, details: %2").arg(error.offset).arg(error.errorString()); } +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper // 存在打印水印设置时,检测是否存在打印水印插件,若存在则通过设置环境变量调用打印插件而不是手动设置 if (authFlags.testFlag(EnablePrintWaterMark)) { detectWaterMarkPluginExists(); } +#endif // DTKWIDGET_CLASS_DWaterMarkHelper // 只要传入参数、图片即认为有效,无论参数是否正常解析 valid = true; @@ -431,9 +535,9 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) if (valid) { // 传入权限控制参数时,绑定DBus唤醒信号 QDBusConnection connection = QDBusConnection::sessionBus(); - bool ret = connection.connect( + bool connRet = connection.connect( "com.wps.cryptfs", "/com/wps/cryptfs", "cryptfs.method.Type", "activateProcess", this, SLOT(activateProcess(qint64))); - if (!ret) { + if (!connRet) { qWarning() << qPrintable("DBus connect activateProcess failed!"); } else { qInfo() << qPrintable("DBus connect activateProcess success!"); @@ -441,33 +545,6 @@ void PermissionConfig::initFromArguments(const QStringList &arguments) } } -/** - @brief 添加 \a dialog 的过滤器,这是用于解除 DTK 中对打印水印间距的限制 - 仅在设置 `breakPrintSpacingLimit` 权限后,且打印水印间距系数超过 DTK 限制时有效。 - 此函数时预留的,用于预防后期新增需求。 - */ -bool PermissionConfig::installFilterPrintDialog(DPrintPreviewDialog *dialog) -{ - if (!breakPrintSpacingLimit) { - return false; - } - - // 判断是否需要调整间距,若间距没有超过限制,则无需调整 - if (!(printRowSpacing > g_PrintRowSpacingLimit || printColumnSpacing > g_PrintColumnSpacingLimit)) { - return false; - } - - if (dialog) { - DPrintPreviewWidget *widget = dialog->findChild(); - if (widget) { - widget->installEventFilter(this); - return true; - } - } - - return false; -} - /** @return 解析命令行参数并返回设置的权限和水印配置,为设置则返回空 */ @@ -537,6 +614,8 @@ void PermissionConfig::initAuthorise(const QJsonObject ¶m) printLimitCount = param.value("printCount").toInt(0); } +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper + /** @brief 从 Json 配置 \a param 中取得阅读水印信息 */ @@ -547,32 +626,33 @@ void PermissionConfig::initReadWaterMark(const QJsonObject ¶m) return; } -#ifdef DTKWIDGET_CLASS_DWaterMarkHelper - readWaterMark.type = WaterMarkType::Text; - readWaterMark.font.setFamily(param.value("font").toString()); - readWaterMark.font.setPointSize(param.value("fontSize").toInt()); + readAdapterWaterMark.type = AdapterWaterMarkData::Text; + readAdapterWaterMark.font.setFamily(param.value("font").toString()); + readAdapterWaterMark.font.setPointSize(param.value("fontSize").toInt()); QString colorName = param.value("color").toString(); if (!colorName.startsWith('#')) { colorName.prepend('#'); } - readWaterMark.color.setNamedColor(colorName); - readWaterMark.opacity = param.value("opacity").toDouble() / 255; - readWaterMark.layout = param.value("layout").toInt() ? WaterMarkLayout::Tiled : WaterMarkLayout::Center; - readWaterMark.rotation = param.value("angle").toDouble(); - readWaterMark.lineSpacing = param.value("rowSpacing").toInt(); - readWaterMark.spacing = param.value("columnSpacing").toInt(); - readWaterMark.text = param.value("text").toString(); + readAdapterWaterMark.color.setNamedColor(colorName); + readAdapterWaterMark.opacity = param.value("opacity").toDouble() / 255; + readAdapterWaterMark.layout = param.value("layout").toInt() ? AdapterWaterMarkData::Tiled : AdapterWaterMarkData::Center; + readAdapterWaterMark.rotation = param.value("angle").toDouble(); + readAdapterWaterMark.lineSpacing = param.value("rowSpacing").toInt(); + readAdapterWaterMark.spacing = param.value("columnSpacing").toInt(); + readAdapterWaterMark.text = param.value("text").toString(); qreal deviceRatio = qApp->devicePixelRatio(); if (ignoreDevicePixelRatio && !qFuzzyCompare(1.0, deviceRatio) && deviceRatio > 0) { - readWaterMark.font.setPointSizeF(readWaterMark.font.pointSizeF() / deviceRatio); - readWaterMark.lineSpacing /= deviceRatio; - readWaterMark.spacing /= deviceRatio; + readAdapterWaterMark.font.setPointSizeF(readAdapterWaterMark.font.pointSizeF() / deviceRatio); + readAdapterWaterMark.lineSpacing /= deviceRatio; + readAdapterWaterMark.spacing /= deviceRatio; } authFlags.setFlag(EnableReadWaterMark, true); -#endif // DTKWIDGET_CLASS_DWaterMarkHelper + + // 转换为公共接口类型 + readWaterMark = convertAdapterWaterMarkData(readAdapterWaterMark); } /** @@ -585,42 +665,86 @@ void PermissionConfig::initPrintWaterMark(const QJsonObject ¶m) return; } -#ifdef DTKWIDGET_CLASS_DWaterMarkHelper - printWaterMark.type = WaterMarkType::Text; - printWaterMark.font.setFamily(param.value("font").toString()); - printWaterMark.font.setPointSize(param.value("fontSize").toInt()); + printAdapterWaterMark.type = AdapterWaterMarkData::Text; + printAdapterWaterMark.font.setFamily(param.value("font").toString()); + printAdapterWaterMark.font.setPointSize(param.value("fontSize").toInt()); QString colorName = param.value("color").toString(); if (!colorName.startsWith('#')) { colorName.prepend('#'); } - printWaterMark.color.setNamedColor(colorName); - printWaterMark.opacity = param.value("opacity").toDouble() / 255; - printWaterMark.layout = param.value("layout").toInt() ? WaterMarkLayout::Tiled : WaterMarkLayout::Center; - printWaterMark.rotation = param.value("angle").toDouble(); - printWaterMark.lineSpacing = param.value("rowSpacing").toInt(); - printWaterMark.spacing = param.value("columnSpacing").toInt(); - printWaterMark.text = param.value("text").toString(); + printAdapterWaterMark.color.setNamedColor(colorName); + printAdapterWaterMark.opacity = param.value("opacity").toDouble() / 255; + printAdapterWaterMark.layout = param.value("layout").toInt() ? AdapterWaterMarkData::Tiled : AdapterWaterMarkData::Center; + printAdapterWaterMark.rotation = param.value("angle").toDouble(); + printAdapterWaterMark.lineSpacing = param.value("rowSpacing").toInt(); + printAdapterWaterMark.spacing = param.value("columnSpacing").toInt(); + printAdapterWaterMark.text = param.value("text").toString(); authFlags.setFlag(EnablePrintWaterMark, true); // 计算 DTK 打印水印的间距转换系数,限制提升到 10000.0 - if (!printWaterMark.text.isEmpty()) { - QFontMetrics fm(printWaterMark.font); - QSize textSize = fm.size(Qt::TextSingleLine, printWaterMark.text); + if (!printAdapterWaterMark.text.isEmpty()) { + QFontMetrics fm(printAdapterWaterMark.font); + QSize textSize = fm.size(Qt::TextSingleLine, printAdapterWaterMark.text); if (textSize.height() > 0) { - printRowSpacing = (qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0; + printRowSpacing = (qreal(printAdapterWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0; printRowSpacing = qBound(0.0, printRowSpacing, 10000.0); } if (textSize.width() > 0) { - printColumnSpacing = (qreal(printWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0; + printColumnSpacing = (qreal(printAdapterWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0; printColumnSpacing = qBound(0.0, printColumnSpacing, 10000.0); } qInfo() << QString("Print config spacing ratio row: %1 column: %2").arg(printRowSpacing).arg(printColumnSpacing); } -#endif // DTKWIDGET_CLASS_DWaterMarkHelper + // 转换为公共接口类型 + printWaterMark = convertAdapterWaterMarkData(printAdapterWaterMark); +} + +/** + @brief 将内部使用的数据 `adptData` 转换为可被外部使用的公共接口类型 `WaterMarkData` , + `WaterMarkData` 在主线和定制分支的接口结构不同,需要转换以兼容不同版本 + */ +WaterMarkData PermissionConfig::convertAdapterWaterMarkData(const PermissionConfig::AdapterWaterMarkData &adptData) const +{ + WaterMarkData data; + + // DTKWidget 主线和定制线的水印接口不同,通过版本进行区分 + // 主线水印接口在 5.6.9 之后引入. + // 因此,判断定制线:存在水印接口,版本不低于 5.4.42.7 且低于 5.5 +#if DTK_VERSION_CHECK(5, 4, 42, 7) <= DTK_VERSION && DTK_VERSION < DTK_VERSION_CHECK(5, 5, 0, 0) + data.type = AdapterWaterMarkData::Text == adptData.type ? WaterMarkType::Text : WaterMarkType::Image; + data.layout = AdapterWaterMarkData::Center == adptData.layout ? WaterMarkLayout::Center : WaterMarkLayout::Tiled; + data.scaleFactor = adptData.scaleFactor; + data.spacing = adptData.spacing; + data.lineSpacing = adptData.lineSpacing; + data.text = adptData.text; + data.font = adptData.font; + data.color = adptData.color; + data.rotation = adptData.rotation; + data.opacity = adptData.opacity; + data.image = adptData.image; + data.grayScale = adptData.grayScale; + +#else + data.setType(AdapterWaterMarkData::Text == adptData.type ? WaterMarkData::Text : WaterMarkData::Image); + data.setLayout(AdapterWaterMarkData::Center == adptData.layout ? WaterMarkData::Center : WaterMarkData::Tiled); + data.setScaleFactor(adptData.scaleFactor); + data.setSpacing(adptData.spacing); + data.setLineSpacing(adptData.lineSpacing); + data.setText(adptData.text); + data.setFont(adptData.font); + data.setColor(adptData.color); + data.setRotation(adptData.rotation); + data.setOpacity(adptData.opacity); + data.setImage(adptData.image); + data.setGrayScale(adptData.grayScale); + +#endif // VERSION CHECK + + return data; } /** @@ -685,34 +809,34 @@ void PermissionConfig::detectWaterMarkPluginExists() bool PermissionConfig::initWaterMarkPluginEnvironment() { QJsonObject envData; -#ifdef DTKWIDGET_CLASS_DWaterMarkHelper - envData.insert("angle", static_cast(printWaterMark.rotation)); - envData.insert("transparency", static_cast(printWaterMark.opacity * 100)); - QFontMetrics fm(printWaterMark.font); - QSize textSize = fm.size(Qt::TextSingleLine, printWaterMark.text); + envData.insert("angle", static_cast(printAdapterWaterMark.rotation)); + envData.insert("transparency", static_cast(printAdapterWaterMark.opacity * 100)); + QFontMetrics fm(printAdapterWaterMark.font); + QSize textSize = fm.size(Qt::TextSingleLine, printAdapterWaterMark.text); if (textSize.height() > 0) { - envData.insert("rowSpacing", qMax(0.0, (qreal(printWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0)); + envData.insert("rowSpacing", + qMax(0.0, (qreal(printAdapterWaterMark.lineSpacing + textSize.height()) / textSize.height()) - 1.0)); } if (textSize.width() > 0) { - envData.insert("columnSpacing", qMax(0.0, (qreal(printWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0)); + envData.insert("columnSpacing", + qMax(0.0, (qreal(printAdapterWaterMark.spacing + textSize.width()) / textSize.width()) - 1.0)); } envData.insert("layout", - static_cast(printWaterMark.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : - DPrintPreviewWatermarkInfo::Tiled)); + static_cast(printAdapterWaterMark.layout == AdapterWaterMarkData::Center ? + DPrintPreviewWatermarkInfo::Center : + DPrintPreviewWatermarkInfo::Tiled)); // 仅文本水印 envData.insert("watermarkType", static_cast(DPrintPreviewWatermarkInfo::TextWatermark)); envData.insert("textType", static_cast(DPrintPreviewWatermarkInfo::Custom)); - envData.insert("customText", printWaterMark.text); - envData.insert("textColor", printWaterMark.color.name()); + envData.insert("customText", printAdapterWaterMark.text); + envData.insert("textColor", printAdapterWaterMark.color.name()); // 兼容插件传参格式 QJsonArray fontList; - fontList.append(printWaterMark.font.family()); + fontList.append(printAdapterWaterMark.font.family()); envData.insert("fontList", fontList); - static const qreal sc_defaultFontSize = 65.0; // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 - envData.insert("size", int(printWaterMark.font.pointSizeF() / sc_defaultFontSize * 100)); -#endif + envData.insert("size", int(printAdapterWaterMark.font.pointSizeF() / g_DefaultPrintFontSize * 100)); QJsonDocument doc; doc.setObject(envData); @@ -741,6 +865,8 @@ bool PermissionConfig::initWaterMarkPluginEnvironment() return true; } +#endif // DTKWIDGET_CLASS_DWaterMarkHelper + /** @return 返回文件 \a fileName 是否符合权限标识 \a authFlag */ diff --git a/libimageviewer/service/permissionconfig.h b/libimageviewer/service/permissionconfig.h index 6a50e22f..d94a93f6 100644 --- a/libimageviewer/service/permissionconfig.h +++ b/libimageviewer/service/permissionconfig.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -10,10 +10,11 @@ #include +// 在 5.6.9 版本后提供 #ifdef DTKWIDGET_CLASS_DWaterMarkHelper #include +// 较低版本(<5.5.50)不提供插件相关接口 #include -#include DWIDGET_USE_NAMESPACE #endif // DTKWIDGET_CLASS_DWaterMarkHelper @@ -76,31 +77,54 @@ class PermissionConfig : public QObject Q_SIGNAL void currentImagePathChanged(const QString &fileName, bool isTargetImage); QString targetImage() const; - // 阅读/打印水印 #ifdef DTKWIDGET_CLASS_DWaterMarkHelper + // 阅读/打印水印 WaterMarkData readWaterMarkData() const; WaterMarkData printWaterMarkData() const; + + bool installFilterPrintDialog(DPrintPreviewDialog *dialog); + bool setDialogPrintWatermark(DPrintPreviewDialog *dialog) const; #endif // DTKWIDGET_CLASS_DWaterMarkHelper Q_SLOT void activateProcess(qint64 pid); void initFromArguments(const QStringList &arguments); - bool installFilterPrintDialog(DPrintPreviewDialog *dialog); - private: // 解析配置 bool parseConfigOption(const QStringList &arguments, QString &configParam, QStringList &imageList) const; void initAuthorise(const QJsonObject ¶m); + +#ifdef DTKWIDGET_CLASS_DWaterMarkHelper + // 用于兼容主线/定制线的通用水印定义,适配不同接口 + struct AdapterWaterMarkData + { + enum AWaterMarkType { None, Text, Image }; // 水印类型 + enum AWaterMarkLayout { Center, Tiled }; // 水印布局 + + AWaterMarkType type = None; // 水印类型 + AWaterMarkLayout layout = Center; // 水印布局 + qreal scaleFactor = 1.0; // 整体大小缩放系数 + int spacing = 0; // 间距 + int lineSpacing = 0; // 行间距 + QString text; // 文本水印内容 + QFont font; // 文本水印字体 + QColor color; // 文本水印颜色 + qreal rotation = 0; // 水印旋转角度(0~360) + qreal opacity = 1; // 水印透明度(0~1) + QImage image; // 图片水印中的图片 + bool grayScale = true; // 是否灰度化图片 + }; + WaterMarkData convertAdapterWaterMarkData(const AdapterWaterMarkData &adptData) const; + void initReadWaterMark(const QJsonObject ¶m); void initPrintWaterMark(const QJsonObject ¶m); void detectWaterMarkPluginExists(); bool initWaterMarkPluginEnvironment(); +#endif // DTKWIDGET_CLASS_DWaterMarkHelper bool checkAuthInvalid(const QString &fileName = QString()) const; void reduceOnePrintCount(); - void triggerNotify(const QJsonObject &data); - bool eventFilter(QObject *watched, QEvent *event) override; private: @@ -113,16 +137,22 @@ class PermissionConfig : public QObject Status status = NotOpen; // 被控制权限图片的状态 Authorises authFlags = NoAuth; - bool ignoreDevicePixelRatio = false; // 过滤设备显示比率,按照原生像素计算 - bool useWaterMarkPlugin = false; // 是否使用水印插件 + // 内部拓展参数 + bool ignoreDevicePixelRatio = false; // 过滤设备显示比率,按照原生像素计算 + bool useWaterMarkPlugin = false; // 是否使用水印插件 + bool breakPrintSpacingLimit = false; // 打破打印间距限制 + qreal printRowSpacing = 0.0; + qreal printColumnSpacing = 0.0; + #ifdef DTKWIDGET_CLASS_DWaterMarkHelper - WaterMarkData readWaterMark; + AdapterWaterMarkData readAdapterWaterMark; // 水印数据,不区分DTK版本 + AdapterWaterMarkData printAdapterWaterMark; + + WaterMarkData readWaterMark; // 水印数据,根据不同DTK版本支持接口不同 WaterMarkData printWaterMark; #endif // DTKWIDGET_CLASS_DWaterMarkHelper - bool breakPrintSpacingLimit = false; // 打破打印间距限制 - qreal printRowSpacing = 0.0; - qreal printColumnSpacing = 0.0; + Q_DISABLE_COPY(PermissionConfig) }; #endif // PERMISSIONCONFIG_H diff --git a/libimageviewer/unionimage/baseutils.cpp b/libimageviewer/unionimage/baseutils.cpp index 3676535d..4e07fc0f 100755 --- a/libimageviewer/unionimage/baseutils.cpp +++ b/libimageviewer/unionimage/baseutils.cpp @@ -187,7 +187,6 @@ void copyImageToClipboard(const QStringList &paths) } // Get clipboard -// QClipboard *cb = QApplication::clipboard(); QClipboard *cb = qApp->clipboard(); // Ownership of the new data is transferred to the clipboard. diff --git a/libimageviewer/viewpanel/viewpanel.cpp b/libimageviewer/viewpanel/viewpanel.cpp index 308bbd35..35d986b7 100644 --- a/libimageviewer/viewpanel/viewpanel.cpp +++ b/libimageviewer/viewpanel/viewpanel.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -505,7 +505,6 @@ void LibViewPanel::updateMenuContent(const QString &path) imageViewerSpace::ImageType imageType = LibUnionImage_NameSpace::getImageType(currentPath);//图片类型 auto authIns = PermissionConfig::instance(); - bool isEditable = authIns->checkAuthFlag(PermissionConfig::EnableEdit, currentPath); bool isCopyable = authIns->checkAuthFlag(PermissionConfig::EnableCopy, currentPath); //判断是否是损坏图片 diff --git a/libimageviewer/viewpanel/viewpanel.h b/libimageviewer/viewpanel/viewpanel.h index aa5faa9b..a8f3c9e4 100644 --- a/libimageviewer/viewpanel/viewpanel.h +++ b/libimageviewer/viewpanel/viewpanel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/libimageviewer/widgets/printhelper.cpp b/libimageviewer/widgets/printhelper.cpp index bec3a670..8438d5bf 100755 --- a/libimageviewer/widgets/printhelper.cpp +++ b/libimageviewer/widgets/printhelper.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -16,7 +16,6 @@ #include #include -#include #include #ifdef USE_UNIONIMAGE @@ -24,7 +23,6 @@ #include "unionimage/unionimage.h" #endif - PrintHelper *PrintHelper::m_Printer = nullptr; PrintHelper *PrintHelper::getIntance() @@ -46,58 +44,6 @@ PrintHelper::~PrintHelper() m_re->deleteLater(); } -//暂时没有使用配置文件的快捷键,现在是根据代码中的快捷键 -/* -static QAction *hookToolBarActionIcons(QToolBar *bar, QAction **pageSetupAction = nullptr) -{ - QAction *last_action = nullptr; - - for (QAction *action : bar->actions()) { - const QString &text = action->text(); - - if (text.isEmpty()) - continue; - - // 防止被lupdate扫描出来 - const char *context = "QPrintPreviewDialog"; - const char *print = "Print"; - - const QMap map { - {QCoreApplication::translate(context, "Next page"), QStringLiteral("go-next")}, - {QCoreApplication::translate(context, "Previous page"), QStringLiteral("go-previous")}, - {QCoreApplication::translate(context, "First page"), QStringLiteral("go-first")}, - {QCoreApplication::translate(context, "Last page"), QStringLiteral("go-last")}, - {QCoreApplication::translate(context, "Fit width"), QStringLiteral("fit-width")}, - {QCoreApplication::translate(context, "Fit page"), QStringLiteral("fit-page")}, - {QCoreApplication::translate(context, "Zoom in"), QStringLiteral("zoom-in")}, - {QCoreApplication::translate(context, "Zoom out"), QStringLiteral("zoom-out")}, - {QCoreApplication::translate(context, "Portrait"), QStringLiteral("layout-portrait")}, - {QCoreApplication::translate(context, "Landscape"), QStringLiteral("layout-landscape")}, - {QCoreApplication::translate(context, "Show single page"), QStringLiteral("view-page-one")}, - {QCoreApplication::translate(context, "Show facing pages"), QStringLiteral("view-page-sided")}, - {QCoreApplication::translate(context, "Show overview of all pages"), QStringLiteral("view-page-multi")}, - {QCoreApplication::translate(context, print), QStringLiteral("print")}, - {QCoreApplication::translate(context, "Page setup"), QStringLiteral("page-setup")} - }; - - - const QString &icon_name = map.value(action->text()); - - if (icon_name.isEmpty()) - continue; - - if (pageSetupAction && icon_name == "page-setup") { - *pageSetupAction = action; - } - - QIcon icon(QStringLiteral(":/qt-project.org/dialogs/assets/images/qprintpreviewdialog/images/%1-24.svg").arg(icon_name)); -// action->setIcon(icon); - last_action = action; - } - - return last_action; -} -*/ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) { if (!PermissionConfig::instance()->isPrintable()) { @@ -108,7 +54,7 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) m_re->m_imgs.clear(); m_re->m_paths = paths; - QStringList tempExsitPaths;//保存存在的图片路径 + QStringList tempExsitPaths; // 保存存在的图片路径 for (const QString &path : paths) { QString errMsg; QImageReader imgReadreder(path); @@ -118,7 +64,7 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) m_re->m_imgs << imgReadreder.read(); } } else { - //QImage不应该多次赋值,所以换到这里来,修复style问题 + // QImage不应该多次赋值,所以换到这里来,修复style问题 QImage img; LibUnionImage_NameSpace::loadStaticImageFromFile(path, img, errMsg); if (!img.isNull()) { @@ -128,15 +74,14 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) tempExsitPaths << paths; } - //看图采用同步,因为只有一张图片,传入父指针 - DPrintPreviewDialog printDialog2(parent); -#if (DTK_VERSION_MAJOR > 5 \ - || (DTK_VERSION_MAJOR >=5 && DTK_VERSION_MINOR > 4) \ - || (DTK_VERSION_MAJOR >= 5 && DTK_VERSION_MINOR >= 4 && DTK_VERSION_PATCH >= 10))//5.4.4暂时没有合入 - //增加运行时版本判断 + // 看图采用同步,因为只有一张图片,传入父指针 + DPrintPreviewDialog printDialog2(parent); +#if (DTK_VERSION_MAJOR > 5 || (DTK_VERSION_MAJOR >= 5 && DTK_VERSION_MINOR > 4) || \ + (DTK_VERSION_MAJOR >= 5 && DTK_VERSION_MINOR >= 4 && DTK_VERSION_PATCH >= 10)) // 5.4.4暂时没有合入 + // 增加运行时版本判断 if (DApplication::runtimeDtkVersion() >= DTK_VERSION_CHECK(5, 4, 10, 0)) { if (!tempExsitPaths.isEmpty()) { - //直接传递为路径,不会有问题 + // 直接传递为路径,不会有问题 printDialog2.setDocName(QFileInfo(tempExsitPaths.at(0)).absoluteFilePath()); } } @@ -150,57 +95,11 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) // 定制分支,水印功能不依赖DTK版本,更新打印水印设置 if (PermissionConfig::instance()->hasPrintWaterMark()) { - auto data = PermissionConfig::instance()->printWaterMarkData(); - - DPrintPreviewSettingInfo *baseInfo = printDialog2.createDialogSettingInfo(DPrintPreviewWatermarkInfo::PS_Watermark); - if (baseInfo) { - DPrintPreviewWatermarkInfo *info = dynamic_cast(baseInfo); - if (info) { - // 打印水印实现方式同阅读水印略有不同,调整参数以使得效果一致。 - info->opened = true; - info->angle = static_cast(data.rotation); - info->transparency = static_cast(data.opacity * 100); - QFontMetrics fm(data.font); - QSize textSize = fm.size(Qt::TextSingleLine, data.text); - if (textSize.height() > 0) { - info->rowSpacing = qMax(0.0, (qreal(data.lineSpacing + textSize.height()) / textSize.height()) - 1.0); - } - if (textSize.width() > 0) { - info->columnSpacing = qMax(0.0, (qreal(data.spacing + textSize.width()) / textSize.width()) - 1.0); - } - info->layout = data.layout == WaterMarkLayout::Center ? DPrintPreviewWatermarkInfo::Center : DPrintPreviewWatermarkInfo::Tiled; - info->currentWatermarkType = (data.type == WaterMarkType::Text) ? DPrintPreviewWatermarkInfo::TextWatermark : DPrintPreviewWatermarkInfo::ImageWatermark; - info->textType = DPrintPreviewWatermarkInfo::Custom; - info->customText = data.text; - info->textColor = data.color; - info->fontList.append(data.font.family()); - static const qreal sc_defaultFontSize = 65.0; - // 字体使用缩放滑块处理 10%~200%, 默认字体大小为65 - info->size = int(data.font.pointSizeF() / sc_defaultFontSize * 100); - printDialog2.updateDialogSettingInfo(info); - } else { - qWarning() << qPrintable("Can't convert DPrintPreviewDialog watermark info.") << baseInfo->type(); - } - - delete baseInfo; - } else { - qWarning() << qPrintable("Can't get DPrintPreviewDialog watermark info."); - } - - // 在不使用打印插件时,需要手动设置打印插件选项显示且不可编辑,在设置后调用 - // WaterMarkFrame 和 WaterMarkContentFrame 是DTK窗口中水印设置控件的 objectName - auto widgetList = printDialog2.findChildren("WaterMarkFrame"); - widgetList.append(printDialog2.findChildren("WaterMarkContentFrame")); - for (auto wid: widgetList) { - wid->setVisible(true); - wid->setEnabled(false); - } - + PermissionConfig::instance()->setDialogPrintWatermark(&printDialog2); } #endif - connect(&printDialog2, SIGNAL(paintRequested(DPrinter *)), - m_re, SLOT(paintRequestSync(DPrinter *))); + connect(&printDialog2, SIGNAL(paintRequested(DPrinter *)), m_re, SLOT(paintRequestSync(DPrinter *))); #ifndef USE_TEST int ret = printDialog2.exec(); @@ -209,7 +108,7 @@ void PrintHelper::showPrintDialog(const QStringList &paths, QWidget *parent) int ret = QDialog::Accepted; #endif - if (QDialog::Accepted == ret) { + if (QDialog::Accepted == ret) { if (!tempExsitPaths.isEmpty()) { PermissionConfig::instance()->triggerPrint(tempExsitPaths.first()); } @@ -224,40 +123,34 @@ RequestedSlot::RequestedSlot(QObject *parent) Q_UNUSED(parent) } -RequestedSlot::~RequestedSlot() -{ - -} +RequestedSlot::~RequestedSlot() {} void RequestedSlot::paintRequestSync(DPrinter *_printer) { - //由于之前再度修改了打印的逻辑,导致了相同图片不在被显示,多余多页tiff来说不合理 + // 由于之前再度修改了打印的逻辑,导致了相同图片不在被显示,多余多页tiff来说不合理 QPainter painter(_printer); int indexNum = 0; for (QImage img : m_imgs) { if (!img.isNull()) { painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); - QRect wRect = _printer->pageRect(); - //修复bug98129,打印不完全问题,ratio应该是适应宽或者高,不应该直接适应宽 + QRect wRect = _printer->pageRect(); + // 修复bug98129,打印不完全问题,ratio应该是适应宽或者高,不应该直接适应宽 qreal ratio = 0.0; qDebug() << wRect; ratio = wRect.width() * 1.0 / img.width(); if (qreal(wRect.height() - img.height() * ratio) > 0) { - painter.drawImage(QRectF(0, abs(qreal(wRect.height() - img.height() * ratio)) / 2, - wRect.width(), img.height() * ratio), img); + painter.drawImage( + QRectF(0, abs(qreal(wRect.height() - img.height() * ratio)) / 2, wRect.width(), img.height() * ratio), img); } else { ratio = wRect.height() * 1.0 / img.height(); - painter.drawImage(QRectF(qreal(wRect.width() - img.width() * ratio) / 2, 0, - img.width() * ratio, wRect.height()), img); + painter.drawImage(QRectF(qreal(wRect.width() - img.width() * ratio) / 2, 0, img.width() * ratio, wRect.height()), + img); } - - } indexNum++; if (indexNum != m_imgs.size()) { _printer->newPage(); - } } painter.end(); diff --git a/libimageviewer/widgets/printhelper.h b/libimageviewer/widgets/printhelper.h index e27a238c..caa56ec8 100755 --- a/libimageviewer/widgets/printhelper.h +++ b/libimageviewer/widgets/printhelper.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -10,14 +10,17 @@ #include #include DWIDGET_USE_NAMESPACE + //重构printhelper,因为dtk更新 //绘制图片处理类 class RequestedSlot : public QObject { Q_OBJECT + public: explicit RequestedSlot(QObject *parent = nullptr); ~RequestedSlot(); + private slots: void paintRequestSync(DPrinter *_printer); @@ -25,6 +28,7 @@ private slots: QStringList m_paths; QList m_imgs; }; + class PrintHelper : public QObject { Q_OBJECT @@ -33,14 +37,13 @@ class PrintHelper : public QObject static PrintHelper *getIntance(); ~PrintHelper(); - // static QSize adjustSize(PrintOptionsPage* optionsPage, QImage img, int resolution, const QSize & viewportSize); - // static QPoint adjustPosition(PrintOptionsPage* optionsPage, const QSize& imageSize, const QSize & viewportSize); void showPrintDialog(const QStringList &paths, QWidget *parent = nullptr); RequestedSlot *m_re = nullptr; + private: explicit PrintHelper(QObject *parent = nullptr); static PrintHelper *m_Printer; }; -#endif // PRINTHELPER_H +#endif // PRINTHELPER_H diff --git a/tests/test_permissionconfig.cpp b/tests/test_permissionconfig.cpp index 0aa370e8..45e30a14 100644 --- a/tests/test_permissionconfig.cpp +++ b/tests/test_permissionconfig.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -48,14 +48,14 @@ TEST_F(UT_PermissionConfig, parseConfigOption_NormalParam_Pass) QString configParam; QStringList imageList; QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; - QStringList tmpList {tempPath}; - QStringList arguments {qApp->arguments().first(), "--config=test", tempPath}; + QStringList tmpList{tempPath}; + QStringList arguments{qApp->arguments().first(), "--config=test", tempPath}; EXPECT_TRUE(PermissionConfig::instance()->parseConfigOption(arguments, configParam, imageList)); EXPECT_EQ(configParam, QString("test")); EXPECT_EQ(imageList, tmpList); - QStringList arguments2 {qApp->arguments().first(), "--config", "test", tempPath}; + QStringList arguments2{qApp->arguments().first(), "--config", "test", tempPath}; EXPECT_TRUE(PermissionConfig::instance()->parseConfigOption(arguments2, configParam, imageList)); EXPECT_EQ(configParam, QString("test")); } @@ -65,13 +65,13 @@ TEST_F(UT_PermissionConfig, parseConfigOption_NormalParam_Fail) QString configParam; QStringList imageList; QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; - QStringList arguments {qApp->arguments().first(), "config", "test", tempPath}; + QStringList arguments{qApp->arguments().first(), "config", "test", tempPath}; EXPECT_FALSE(PermissionConfig::instance()->parseConfigOption({}, configParam, imageList)); EXPECT_TRUE(configParam.isEmpty()); EXPECT_TRUE(imageList.isEmpty()); - QStringList tmpList {"config", "test", tempPath}; + QStringList tmpList{"config", "test", tempPath}; EXPECT_FALSE(PermissionConfig::instance()->parseConfigOption(arguments, configParam, imageList)); EXPECT_TRUE(configParam.isEmpty()); EXPECT_EQ(imageList, tmpList); @@ -84,15 +84,14 @@ TEST_F(UT_PermissionConfig, initFromArguments_NormalParam_Pass) QString tempPath = QApplication::applicationDirPath() + "/svg.svg"; QStringList args; - args << qApp->arguments().first() - << QString("--config=%1").arg(permission_config()) - << tempPath; + args << qApp->arguments().first() << QString("--config=%1").arg(permission_config()) << tempPath; PermissionConfig::instance()->initFromArguments(args); EXPECT_TRUE(PermissionConfig::instance()->isValid()); - PermissionConfig::Authorises auth = PermissionConfig::Authorises( - PermissionConfig::EnableCopy | PermissionConfig::EnableEdit | PermissionConfig::EnableSwitch | PermissionConfig::EnableWallpaper); + PermissionConfig::Authorises auth = + PermissionConfig::Authorises(PermissionConfig::EnableCopy | PermissionConfig::EnableEdit | + PermissionConfig::EnableSwitch | PermissionConfig::EnableWallpaper); EXPECT_EQ(PermissionConfig::instance()->authFlags, auth); EXPECT_EQ(PermissionConfig::instance()->targetImagePath, tempPath); @@ -167,7 +166,12 @@ TEST_F(UT_PermissionConfig, print_Count_Pass) EXPECT_TRUE(PermissionConfig::instance()->isUnlimitPrint()); } -TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass) +// DTKWidget 主线和定制线的水印接口不同,通过版本进行区分 +// 主线水印接口在 5.6.9 之后引入. +// 因此,判断定制线:存在水印接口,版本不低于 5.4.42.7 且低于 5.5 +#if DTK_VERSION_CHECK(5, 4, 42, 7) <= DTK_VERSION && DTK_VERSION < DTK_VERSION_CHECK(5, 5, 0, 0) + +TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass_5_4_42) { // Default value auto markData = PermissionConfig::instance()->readWaterMarkData(); @@ -176,7 +180,7 @@ TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass) EXPECT_EQ(markData.layout, WaterMarkLayout::Center); EXPECT_TRUE(markData.text.isEmpty()); - QJsonObject param {{"text", "test"}, {"opacity", 0}, {"layout", 1}}; + QJsonObject param{{"text", "test"}, {"opacity", 0}, {"layout", 1}}; PermissionConfig::instance()->initReadWaterMark(param); markData = PermissionConfig::instance()->readWaterMarkData(); @@ -190,14 +194,40 @@ TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass) EXPECT_EQ(markData.type, WaterMarkType::Text); } +#else + +TEST_F(UT_PermissionConfig, watarMark_JsonData_Pass) +{ + // Default value + auto markData = PermissionConfig::instance()->readWaterMarkData(); + EXPECT_EQ(markData.type(), WaterMarkData::WaterMarkType::None); + EXPECT_EQ(markData.opacity(), 1); + EXPECT_EQ(markData.layout(), WaterMarkData::WaterMarkLayout::Center); + EXPECT_TRUE(markData.text().isEmpty()); + + QJsonObject param{{"text", "test"}, {"opacity", 0}, {"layout", 1}}; + + PermissionConfig::instance()->initReadWaterMark(param); + markData = PermissionConfig::instance()->readWaterMarkData(); + EXPECT_EQ(markData.type(), WaterMarkData::WaterMarkType::Text); + EXPECT_EQ(markData.opacity(), 0); + EXPECT_EQ(markData.layout(), WaterMarkData::WaterMarkLayout::Tiled); + EXPECT_EQ(markData.text(), QString("test")); + + PermissionConfig::instance()->initPrintWaterMark(param); + markData = PermissionConfig::instance()->printWaterMarkData(); + EXPECT_EQ(markData.type(), WaterMarkData::WaterMarkType::Text); +} +#endif // VERSION CHECK + TEST_F(UT_PermissionConfig, triggerAction_SignalNotify_Pass) { QJsonObject notifyData; - QObject::connect(PermissionConfig::instance(), &PermissionConfig::authoriseNotify, [&](const QJsonObject ¬ify){ + QObject::connect(PermissionConfig::instance(), &PermissionConfig::authoriseNotify, [&](const QJsonObject ¬ify) { notifyData = notify; }); - auto TriggerFunction = [&](PermissionConfig::TidType tid, const QString &operate){ + auto TriggerFunction = [&](PermissionConfig::TidType tid, const QString &operate) { // PermissionConfig::targetImagePath is empty. PermissionConfig::instance()->triggerAction(tid, ""); // ReportMode::ReportAndBroadcast = 0b11 @@ -255,11 +285,17 @@ TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckPlugin_Pass) EXPECT_FALSE(envData.isEmpty()); } -TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckValue_Pass) +TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckValue) { static const qreal sc_defaultFontSize = 65.0; // 验证打印水印设置值 - WaterMarkData &watermark = PermissionConfig::instance()->printWaterMark; +#if DTK_VERSION_CHECK(5, 4, 42, 7) <= DTK_VERSION && DTK_VERSION < DTK_VERSION_CHECK(5, 5, 0, 0) + WaterMarkData &watermark = PermissionConfig::instance()->printWaterMark; + watermark.layout = WaterMarkLayout::Center; +#else + PermissionConfig::AdapterWaterMarkData &watermark = PermissionConfig::instance()->printAdapterWaterMark; + watermark.layout = PermissionConfig::AdapterWaterMarkData::Center; +#endif // VERSION CHECK watermark.rotation = 45; watermark.opacity = 0.3; watermark.font.setPointSize(30); @@ -267,7 +303,6 @@ TEST_F(UT_PermissionConfig, initWaterMarkPluginEnvironment_CheckValue_Pass) watermark.lineSpacing = 0; watermark.spacing = 0; watermark.color = "#FF00FF"; - watermark.layout = WaterMarkLayout::Center; // 强制设置环境变量 PermissionConfig::instance()->initWaterMarkPluginEnvironment(); @@ -383,4 +418,76 @@ TEST_F(UT_PermissionConfig, installFilterPrintDialog_FilterData_Ignore) EXPECT_TRUE(qFuzzyCompare(widget->property("_d_print_waterMarkColumnSpacing").toReal(), 1.0)); } +#if DTK_VERSION_CHECK(5, 4, 42, 7) <= DTK_VERSION && DTK_VERSION < DTK_VERSION_CHECK(5, 5, 0, 0) + +TEST_F(UT_PermissionConfig, convertAdapterWaterMarkData_Equal_Pass) +{ + WaterMarkData data; + data.type = WaterMarkData::Text; + data.layout = WaterMarkData::Tiled; + data.text = "123Test"; + QFont f; + f.setPointSize(15); + data.font = f; + QColor color(Qt::red); + data.color = color; + data.lineSpacing = 10; + data.rotation = 10; + + PermissionConfig::AdapterWaterMarkData adptData; + adptData.type = PermissionConfig::AdapterWaterMarkData::Text; + adptData.layout = PermissionConfig::AdapterWaterMarkData::Tiled; + adptData.text = "123Test"; + adptData.font = f; + adptData.color = color; + adptData.lineSpacing = 10; + adptData.rotation = 10; + + auto cvtData = PermissionConfig::instance()->convertAdapterWaterMarkData(adptData); + EXPECT_EQ(data.type, cvtData.type); + EXPECT_EQ(data.layout, cvtData.layout); + EXPECT_EQ(data.text, cvtData.text); + EXPECT_EQ(data.font, cvtData.font); + EXPECT_EQ(data.color, cvtData.color); + EXPECT_EQ(data.lineSpacing, cvtData.lineSpacing); + EXPECT_EQ(data.rotation, cvtData.rotation); +} + +#else + +TEST_F(UT_PermissionConfig, convertAdapterWaterMarkData_Equal_Pass) +{ + WaterMarkData data; + data.setType(WaterMarkData::Text); + data.setLayout(WaterMarkData::Tiled); + data.setText("123Test"); + QFont f; + f.setPointSize(15); + data.setFont(f); + QColor color(Qt::red); + data.setColor(color); + data.setLineSpacing(10); + data.setRotation(10); + + PermissionConfig::AdapterWaterMarkData adptData; + adptData.type = PermissionConfig::AdapterWaterMarkData::Text; + adptData.layout = PermissionConfig::AdapterWaterMarkData::Tiled; + adptData.text = "123Test"; + adptData.font = f; + adptData.color = color; + adptData.lineSpacing = 10; + adptData.rotation = 10; + + auto cvtData = PermissionConfig::instance()->convertAdapterWaterMarkData(adptData); + EXPECT_EQ(data.type(), cvtData.type()); + EXPECT_EQ(data.layout(), cvtData.layout()); + EXPECT_EQ(data.text(), cvtData.text()); + EXPECT_EQ(data.font(), cvtData.font()); + EXPECT_EQ(data.color(), cvtData.color()); + EXPECT_EQ(data.lineSpacing(), cvtData.lineSpacing()); + EXPECT_EQ(data.rotation(), cvtData.rotation()); +} + +#endif // VERSION CHECK + #endif