Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions reader/document/XpsDocumentAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ QImage XpsDocumentAdapter::renderPage(int pageIndex, int width, int height, cons
cairo_set_source_rgb(context.get(), 1.0, 1.0, 1.0);
cairo_paint(context.get());

// Scaling via cairo_matrix keeps high-DPI print requests accurate, no device-scale override needed here.
cairo_matrix_t matrix;
cairo_set_antialias(context.get(), CAIRO_ANTIALIAS_BEST);

Expand Down
210 changes: 198 additions & 12 deletions reader/uiframe/DocSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,26 @@
#include <QFileInfo>
#include <QPropertyAnimation>
#include <QScreen>
#include <QDebug>

Check warning on line 37 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTemporaryDir>

Check warning on line 38 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTemporaryDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QProcess>

Check warning on line 39 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QProcess> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QPrinter>

Check warning on line 40 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QPrinter> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QtMath>

Check warning on line 41 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QtMath> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QGraphicsDropShadowEffect>

Check warning on line 42 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QGraphicsDropShadowEffect> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <signal.h>

Check warning on line 44 in reader/uiframe/DocSheet.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <signal.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sys/types.h>
extern "C" {
#include "load_libs.h"
}
DWIDGET_USE_NAMESPACE

namespace {
constexpr double kXpsLogicalDpi = 96.0;
constexpr int kFallbackPrintDpi = 300;
constexpr int kMaxPrintPixelsPerSide = 10000;
}

QReadWriteLock DocSheet::g_lock;
QStringList DocSheet::g_uuidList;
QList<DocSheet *> DocSheet::g_sheetList;
Expand Down Expand Up @@ -508,6 +516,56 @@
return m_renderer->getImage(index, width, height, slice);;
}

QSize DocSheet::calculatePrintTargetSize(int pageIndex, const QPrinter &printer, const QRectF &pageRect) const
{
if (m_fileType != Dr::XPS) {
return QSize();
}

const QSizeF logicalSize = m_renderer ? m_renderer->getPageSize(pageIndex) : QSizeF();
if (logicalSize.isEmpty() || logicalSize.width() <= 0.0 || logicalSize.height() <= 0.0) {
qCWarning(appLog) << "Unable to determine logical page size for high DPI print. index:" << pageIndex;
return QSize();
}

int printerDpi = printer.resolution();
if (printerDpi <= 0) {
qCWarning(appLog) << "Printer resolution is invalid, fallback to default DPI:" << kFallbackPrintDpi;
printerDpi = kFallbackPrintDpi;
}

double widthPixels = logicalSize.width() / kXpsLogicalDpi * static_cast<double>(printerDpi);
double heightPixels = logicalSize.height() / kXpsLogicalDpi * static_cast<double>(printerDpi);

if (widthPixels <= 0.0 || heightPixels <= 0.0) {
qCWarning(appLog) << "Calculated print size is invalid:" << widthPixels << heightPixels;
return QSize();
}

if (pageRect.width() > 0.0 && pageRect.height() > 0.0) {
const double scale = qMin(pageRect.width() / widthPixels, pageRect.height() / heightPixels);
if (scale < 1.0) {
widthPixels *= scale;
heightPixels *= scale;
}
}

const double maxDimension = qMax(widthPixels, heightPixels);
if (maxDimension > kMaxPrintPixelsPerSide) {
const double clampScale = static_cast<double>(kMaxPrintPixelsPerSide) / maxDimension;
widthPixels *= clampScale;
heightPixels *= clampScale;
qCWarning(appLog) << "Requested print resolution too large, clamping to" << kMaxPrintPixelsPerSide << "pixels. index:" << pageIndex;
}

const int roundedWidth = qMax(1, qRound(widthPixels));
const int roundedHeight = qMax(1, qRound(heightPixels));

qCDebug(appLog) << "Calculated print target size for page" << pageIndex << ":" << roundedWidth << "x" << roundedHeight << "@" << printerDpi << "DPI";

return QSize(roundedWidth, roundedHeight);
}

bool DocSheet::fileChanged()
{
qCDebug(appLog) << "fileChanged";
Expand Down Expand Up @@ -870,22 +928,72 @@

painter.setRenderHints(QPainter::Antialiasing | QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

const bool isXpsDocument = (m_fileType == Dr::XPS);
auto targetRectForSize = [&pageRect](const QSize &sourceSize) -> QRect {
if (!sourceSize.isValid() || sourceSize.isEmpty()) {
return QRect();
}

qreal targetWidth = pageRect.width();
qreal targetHeight = targetWidth * static_cast<qreal>(sourceSize.height()) / static_cast<qreal>(sourceSize.width());

if (targetHeight > pageRect.height()) {
targetHeight = pageRect.height();
targetWidth = targetHeight * static_cast<qreal>(sourceSize.width()) / static_cast<qreal>(sourceSize.height());
}

const int left = qRound((pageRect.width() - targetWidth) / 2.0);
const int top = qRound((pageRect.height() - targetHeight) / 2.0);
return QRect(left,
top,
qMax(1, qRound(targetWidth)),
qMax(1, qRound(targetHeight)));
};

for (int i = 0; i < pageRange.count(); ++i) {
if (pageRange[i] > pageCount() || pageRange[i] > m_browser->pages().count())
continue;

const QRectF boundingrect = m_browser->pages().at(pageRange[i] - 1)->boundingRect(); //文档页缩放后的原区域不受旋转影响
qreal printWidth = pageRect.width(); //适合打印的图片宽度
qreal printHeight = printWidth * boundingrect.height() / boundingrect.width(); //适合打印的图片高度
if (printHeight > pageRect.height()) {
printHeight = pageRect.height();
printWidth = printHeight * boundingrect.width() / boundingrect.height();
QImage image;
QRect targetRect;

if (isXpsDocument) {
const int zeroBasedIndex = pageRange[i] - 1;
const QSize requestedSize = calculatePrintTargetSize(zeroBasedIndex, *printer, pageRect);
if (!requestedSize.isValid()) {
qCWarning(appLog) << "Falling back to view-based print size for XPS page" << zeroBasedIndex;
}

if (requestedSize.isValid()) {
image = loading.getImageForPrint(this, zeroBasedIndex, requestedSize);
targetRect = targetRectForSize(image.size());
}
}

if (image.isNull()) {
const QRectF boundingrect = m_browser->pages().at(pageRange[i] - 1)->boundingRect();
qreal printWidth = pageRect.width();
qreal printHeight = printWidth * boundingrect.height() / boundingrect.width();
if (printHeight > pageRect.height()) {
printHeight = pageRect.height();
printWidth = printHeight * boundingrect.width() / boundingrect.height();
}
image = loading.getImage(this, pageRange[i] - 1, static_cast<int>(printWidth), static_cast<int>(printHeight));
targetRect = QRect((static_cast<int>(pageRect.width()) - image.width()) / 2,
(static_cast<int>(pageRect.height()) - image.height()) / 2,
image.width(), image.height());
}

if (image.isNull()) {
qCWarning(appLog) << "Failed to render image for printing page" << pageRange[i];
continue;
}

if (!targetRect.isValid()) {
targetRect = targetRectForSize(image.size());
}

QImage image = loading.getImage(this, pageRange[i] - 1, static_cast<int>(printWidth), static_cast<int>(printHeight));
painter.drawImage(QRect((static_cast<int>(pageRect.width()) - image.width()) / 2,
(static_cast<int>(pageRect.height()) - image.height()) / 2,
image.width(), image.height()), image);
painter.drawImage(targetRect, image, image.rect());

if (i != pageRange.count() - 1)
printer->newPage();
Expand Down Expand Up @@ -946,13 +1054,62 @@
LoadingWidget loading(qApp->activeWindow());
loading.show();

const bool isXpsDocument = (m_fileType == Dr::XPS);
auto targetRectForSize = [&pageRect](const QSize &sourceSize) -> QRect {
if (!sourceSize.isValid() || sourceSize.isEmpty()) {
return QRect();
}

qreal targetWidth = pageRect.width();
qreal targetHeight = targetWidth * static_cast<qreal>(sourceSize.height()) / static_cast<qreal>(sourceSize.width());

if (targetHeight > pageRect.height()) {
targetHeight = pageRect.height();
targetWidth = targetHeight * static_cast<qreal>(sourceSize.width()) / static_cast<qreal>(sourceSize.height());
}

const int left = qRound((pageRect.width() - targetWidth) / 2.0);
const int top = qRound((pageRect.height() - targetHeight) / 2.0);
return QRect(left,
top,
qMax(1, qRound(targetWidth)),
qMax(1, qRound(targetHeight)));
};

for (int index = fromIndex; index <= toIndex; index++) {
if (index >= pagesCount)
break;

QImage imageX3 = loading.getImage(this, index, int(pageRect.width() * 3), int(pageRect.height() * 3));
QImage image;
QRect targetRect;

if (isXpsDocument) {
const QSize requestedSize = calculatePrintTargetSize(index, *printer, pageRect);
if (!requestedSize.isValid()) {
qCWarning(appLog) << "Falling back to view-based print size for XPS page" << index;
} else {
image = loading.getImageForPrint(this, index, requestedSize);
targetRect = targetRectForSize(image.size());
}
}

if (image.isNull()) {
const int fallbackWidth = int(pageRect.width() * 3);
const int fallbackHeight = int(pageRect.height() * 3);
image = loading.getImage(this, index, fallbackWidth, fallbackHeight);
targetRect = pageRect.toRect();
}

if (image.isNull()) {
qCWarning(appLog) << "Failed to render image for printing page" << index;
continue;
}

if (!targetRect.isValid()) {
targetRect = targetRectForSize(image.size());
}

painter.drawImage(pageRect, imageX3, QRect(0, 0, imageX3.width(), imageX3.height()));
painter.drawImage(targetRect, image, image.rect());
if (index != toIndex)
printer->newPage();
}
Expand Down Expand Up @@ -1593,6 +1750,35 @@
return image;
}

QImage DocSheet::LoadingWidget::getImageForPrint(DocSheet *doc, int index, const QSize &targetSize)
{
qCDebug(appLog) << "getImageForPrint" << targetSize;
if (!doc) {
qCWarning(appLog) << "DocSheet pointer is null when requesting print image";
return QImage();
}
if (!targetSize.isValid() || targetSize.width() <= 0 || targetSize.height() <= 0) {
qCWarning(appLog) << "Invalid target size for print image" << targetSize;
return QImage();
}

QImage image;
QEventLoop loop;
const QSize requestSize = targetSize;
QThread *thread = QThread::create([ =, &image]() {
image = doc->getImage(index, requestSize.width(), requestSize.height());
});
QObject::connect(thread, &QThread::finished, &loop, &QEventLoop::quit);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);

thread->start();

loop.exec(QEventLoop::ExcludeSocketNotifiers);

qCDebug(appLog) << "getImageForPrint end";
return image;
}

void DocSheet::LoadingWidget::paintEvent(QPaintEvent *)
{
// qCDebug(appLog) << "paintEvent";
Expand Down
8 changes: 8 additions & 0 deletions reader/uiframe/DocSheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
#include "Model.h"

#include <DSplitter>
#include <QSet>

Check warning on line 12 in reader/uiframe/DocSheet.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QSet> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QReadWriteLock>

Check warning on line 13 in reader/uiframe/DocSheet.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QReadWriteLock> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QThread>

Check warning on line 14 in reader/uiframe/DocSheet.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QThread> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QSize>
#include <QRectF>

class SheetSidebar;
class SlideWidget;
Expand Down Expand Up @@ -810,6 +812,7 @@
void onExtractPassword(const QString &password);

private:
QSize calculatePrintTargetSize(int pageIndex, const QPrinter &printer, const QRectF &pageRect) const;
/**
* @brief setAlive
* 设置当前sheet是否存活
Expand Down Expand Up @@ -873,6 +876,11 @@
*/
QImage getImage(DocSheet *doc, int index, int width, int height);

/**
* @brief getImageForPrint 打印专用图像获取接口
*/
QImage getImageForPrint(DocSheet *doc, int index, const QSize &targetSize);

protected:
void paintEvent(QPaintEvent */*event*/) override;

Expand Down
1 change: 1 addition & 0 deletions reader/uiframe/SheetRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ QImage SheetRenderer::getImage(int index, int width, int height, const QRect &sl
return QImage();
}

// width/height may come from high-DPI print requests: keep pipeline ready for large images.
QImage image = m_pages.value(index)->render(width, height, slice);
qCDebug(appLog) << "SheetRenderer::getImage end";
return image;
Expand Down