From ad40615f6cd889af3e638d3a0c4b8357734cb50b Mon Sep 17 00:00:00 2001 From: ZhangTingan Date: Mon, 10 Nov 2025 15:59:37 +0800 Subject: [PATCH] feat: [xps] enable dual-page and sidebar support with robust scaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - safeguard SheetBrowser’s fit logic against zero-sized page metrics to keep dual-page mode stable for XPS documents - initialise XPS DocSheets with the standard thumbnail/catalog/bookmark sidebar - allow TitleWidget controls (sidebar toggle, zoom menu) for XPS files now that the underlying features are wired - keep XPS render path improvements aligned with existing viewer behaviour task: https://pms.uniontech.com/task-view-383459.html --- reader/browser/BrowserPage.cpp | 6 +++- reader/browser/SheetBrowser.cpp | 23 ++++++------ reader/document/XpsDocumentAdapter.cpp | 48 +++++++++++++++++--------- reader/uiframe/DocSheet.cpp | 4 +++ reader/uiframe/TitleWidget.cpp | 7 +++- 5 files changed, 59 insertions(+), 29 deletions(-) diff --git a/reader/browser/BrowserPage.cpp b/reader/browser/BrowserPage.cpp index 53c89277..39ad3a04 100644 --- a/reader/browser/BrowserPage.cpp +++ b/reader/browser/BrowserPage.cpp @@ -1234,7 +1234,11 @@ BrowserWord *BrowserPage::getBrowserWord(const QPointF &point) bool BrowserPage::isBigDoc() { qCDebug(appLog) << "BrowserPage::isBigDoc() - Starting is big doc"; - bool isBig = Dr::PDF == m_sheet->fileType() && boundingRect().width() > 1000 && boundingRect().height() > 1000; + bool supportedType = (Dr::PDF == m_sheet->fileType()); +#ifdef XPS_SUPPORT_ENABLED + supportedType = supportedType || (Dr::XPS == m_sheet->fileType()); +#endif + bool isBig = supportedType && boundingRect().width() > 1000 && boundingRect().height() > 1000; qCDebug(appLog) << "Checking if document is big:" << isBig; return isBig; } diff --git a/reader/browser/SheetBrowser.cpp b/reader/browser/SheetBrowser.cpp index 0add1f60..1219cfe3 100644 --- a/reader/browser/SheetBrowser.cpp +++ b/reader/browser/SheetBrowser.cpp @@ -893,6 +893,9 @@ void SheetBrowser::deform(SheetOperation &operation) m_lastScaleFactor = operation.scaleFactor; + const qreal safeMaxWidth = qFuzzyIsNull(m_maxWidth) ? 1.0 : m_maxWidth; + const qreal safeMaxHeight = qFuzzyIsNull(m_maxHeight) ? (qFuzzyIsNull(m_maxWidth) ? 1.0 : m_maxWidth) : m_maxHeight; + //根据缩放模式调整缩放比例 switch (operation.rotation) { default: @@ -900,15 +903,15 @@ void SheetBrowser::deform(SheetOperation &operation) case Dr::RotateBy180: qCDebug(appLog) << "SheetBrowser::deform() - Rotation is 0 or 180"; if (Dr::FitToPageWidthMode == operation.scaleMode) - operation.scaleFactor = static_cast(this->width() - 25.0) / m_maxWidth / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); + operation.scaleFactor = static_cast(this->width() - 25.0) / safeMaxWidth / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); else if (Dr::FitToPageHeightMode == operation.scaleMode) - operation.scaleFactor = static_cast(this->height()) / m_maxHeight; + operation.scaleFactor = static_cast(this->height()) / safeMaxHeight; else if (Dr::FitToPageDefaultMode == operation.scaleMode) operation.scaleFactor = 1.0 ; else if (Dr::FitToPageWorHMode == operation.scaleMode) { - qreal scaleFactor = static_cast(this->width() - 25.0) / m_maxWidth / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); - if (scaleFactor * m_maxHeight > this->height()) - scaleFactor = static_cast(this->height()) / m_maxHeight; + qreal scaleFactor = static_cast(this->width() - 25.0) / safeMaxWidth / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); + if (scaleFactor * safeMaxHeight > this->height()) + scaleFactor = static_cast(this->height()) / safeMaxHeight; operation.scaleFactor = scaleFactor; } else operation.scaleMode = Dr::ScaleFactorMode; @@ -917,15 +920,15 @@ void SheetBrowser::deform(SheetOperation &operation) case Dr::RotateBy270: qCDebug(appLog) << "SheetBrowser::deform() - Rotation is 90 or 270"; if (Dr::FitToPageWidthMode == operation.scaleMode) - operation.scaleFactor = static_cast(this->width() - 25.0) / m_maxHeight / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); + operation.scaleFactor = static_cast(this->width() - 25.0) / safeMaxHeight / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); else if (Dr::FitToPageHeightMode == operation.scaleMode) - operation.scaleFactor = static_cast(this->height()) / m_maxWidth; + operation.scaleFactor = static_cast(this->height()) / safeMaxWidth; else if (Dr::FitToPageDefaultMode == operation.scaleMode) operation.scaleFactor = 1.0 ; else if (Dr::FitToPageWorHMode == operation.scaleMode) { - qreal scaleFactor = static_cast(this->width() - 25.0) / m_maxHeight / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); - if (scaleFactor * m_maxWidth > this->height()) - scaleFactor = static_cast(this->height()) / m_maxWidth; + qreal scaleFactor = static_cast(this->width() - 25.0) / safeMaxHeight / (Dr::TwoPagesMode == operation.layoutMode ? 2 : 1); + if (scaleFactor * safeMaxWidth > this->height()) + scaleFactor = static_cast(this->height()) / safeMaxWidth; operation.scaleFactor = scaleFactor; } else operation.scaleMode = Dr::ScaleFactorMode; diff --git a/reader/document/XpsDocumentAdapter.cpp b/reader/document/XpsDocumentAdapter.cpp index 33bf3928..58dbd020 100644 --- a/reader/document/XpsDocumentAdapter.cpp +++ b/reader/document/XpsDocumentAdapter.cpp @@ -488,21 +488,30 @@ QImage XpsDocumentAdapter::renderPage(int pageIndex, int width, int height, cons } const QSizeF logicalSize = m_pageSizes.at(pageIndex); - const QRectF fullRect(QPointF(0, 0), logicalSize); - QRectF clipRect = slice.isValid() ? QRectF(slice) : fullRect; - clipRect = clipRect.intersected(fullRect); - if (clipRect.isEmpty()) { - clipRect = fullRect; - } - if (clipRect.width() <= 0.0 || clipRect.height() <= 0.0) { - qCWarning(appLog) << "Invalid clip rectangle for XPS render" << clipRect; + if (logicalSize.isEmpty() || logicalSize.width() <= 0.0 || logicalSize.height() <= 0.0) { + qCWarning(appLog) << "Invalid logical size for XPS page" << logicalSize; return QImage(); } - const double scaleX = width / clipRect.width(); - const double scaleY = height / clipRect.height(); + const double pixelsPerDocX = static_cast(width) / logicalSize.width(); + const double pixelsPerDocY = static_cast(height) / logicalSize.height(); - QImage image(width, height, QImage::Format_ARGB32_Premultiplied); + if (pixelsPerDocX <= 0.0 || pixelsPerDocY <= 0.0) { + qCWarning(appLog) << "Invalid pixels-per-document ratio" << pixelsPerDocX << pixelsPerDocY; + return QImage(); + } + + const QRect fullSlice(0, 0, width, height); + const QRect requestedSlice = slice.isValid() ? slice : fullSlice; + + if (requestedSlice.width() <= 0 || requestedSlice.height() <= 0) { + qCWarning(appLog) << "Requested slice has non-positive size" << requestedSlice; + return QImage(); + } + + const QSize targetSize(requestedSlice.size()); + + QImage image(targetSize.width(), targetSize.height(), QImage::Format_ARGB32_Premultiplied); if (image.isNull()) { qCWarning(appLog) << "Failed to allocate image for XPS render"; return image; @@ -511,8 +520,8 @@ QImage XpsDocumentAdapter::renderPage(int pageIndex, int width, int height, cons CairoSurfacePtr surface(cairo_image_surface_create_for_data(image.bits(), CAIRO_FORMAT_ARGB32, - width, - height, + targetSize.width(), + targetSize.height(), image.bytesPerLine())); if (!surface || cairo_surface_status(surface.get()) != CAIRO_STATUS_SUCCESS) { qCWarning(appLog) << "Failed to create Cairo surface for XPS render"; @@ -529,11 +538,16 @@ QImage XpsDocumentAdapter::renderPage(int pageIndex, int width, int height, cons cairo_paint(context.get()); cairo_matrix_t matrix; + cairo_set_antialias(context.get(), CAIRO_ANTIALIAS_BEST); + + const double translateX = slice.isValid() ? -static_cast(requestedSlice.left()) : 0.0; + const double translateY = slice.isValid() ? -static_cast(requestedSlice.top()) : 0.0; + cairo_matrix_init(&matrix, - scaleX, 0.0, - 0.0, scaleY, - -clipRect.left() * scaleX, - -clipRect.top() * scaleY); + pixelsPerDocX, 0.0, + 0.0, pixelsPerDocY, + translateX, + translateY); cairo_set_matrix(context.get(), &matrix); GErrorPtr renderError; diff --git a/reader/uiframe/DocSheet.cpp b/reader/uiframe/DocSheet.cpp index 38e02c94..a50735d7 100644 --- a/reader/uiframe/DocSheet.cpp +++ b/reader/uiframe/DocSheet.cpp @@ -76,6 +76,10 @@ DocSheet::DocSheet(const Dr::FileType &fileType, const QString &filePath, QWidg m_sidebar = new SheetSidebar(this, PREVIEW_THUMBNAIL | PREVIEW_BOOKMARK); else if (Dr::DOCX == fileType) m_sidebar = new SheetSidebar(this, PREVIEW_THUMBNAIL | PREVIEW_CATALOG | PREVIEW_BOOKMARK | PREVIEW_NOTE); +#ifdef XPS_SUPPORT_ENABLED + else if (Dr::XPS == fileType) + m_sidebar = new SheetSidebar(this, PREVIEW_THUMBNAIL | PREVIEW_CATALOG | PREVIEW_BOOKMARK); +#endif else m_sidebar = new SheetSidebar(this); diff --git a/reader/uiframe/TitleWidget.cpp b/reader/uiframe/TitleWidget.cpp index 22f1534b..a0d08695 100644 --- a/reader/uiframe/TitleWidget.cpp +++ b/reader/uiframe/TitleWidget.cpp @@ -136,7 +136,12 @@ void TitleWidget::onCurSheetChanged(DocSheet *sheet) } qCDebug(appLog) << "Setting up controls for file type:" << m_curSheet->fileType(); - if (Dr::PDF == m_curSheet->fileType() || Dr::DOCX == m_curSheet->fileType()) { + if (Dr::PDF == m_curSheet->fileType() + || Dr::DOCX == m_curSheet->fileType() +#ifdef XPS_SUPPORT_ENABLED + || Dr::XPS == m_curSheet->fileType() +#endif + ) { if (m_curSheet->opened()) { qCDebug(appLog) << "Document opened, enabling controls"; setBtnDisable(false);