print: QPageLayout: Fix pt unit conversion

This patch corrects the miscalculation in point unit conversion,
ensuring correct margin updates.

Previously, non-pt units were rounded to two decimal places. When
converting back to pt, rounding was to zero decimals, making the result
always less than the original. This could result in margins falling
below the minimum allowed.

Example:
original_points = 8.4
multiplier = 2.83464566929
mm: qRound(8.4 / multiplier * 100) / 100 = 2.96
new_points: qRound(2.96 * multiplier) = 8 // wrong!

The fix rounds back-converted values up to two decimals, ensuring they
are never less than the original and thus stay above minimum margins.

new_points: qCeil(2.96 * multiplier * 100) / 100 = 8.4

Also, remove unused function qt_convertPoint.

Pick-to: 6.6
Change-Id: I6109f8d381aec96db1ce04cc167f7b73c1c0b9a8
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Jarkko Koivikko 2023-10-26 14:02:48 +03:00
parent 5fb2d50e42
commit d420c1e25a
2 changed files with 56 additions and 28 deletions

View File

@ -39,41 +39,19 @@ Q_GUI_EXPORT qreal qt_pointMultiplier(QPageLayout::Unit unit)
// Multiplier for converting pixels to points.
extern qreal qt_pixelMultiplier(int resolution);
QPointF qt_convertPoint(const QPointF &xy, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits)
{
// If the size have the same units, or are all 0, then don't need to convert
if (fromUnits == toUnits || xy.isNull())
return xy;
// If converting to points then convert and round to 0 decimal places
if (toUnits == QPageLayout::Point) {
const qreal multiplier = qt_pointMultiplier(fromUnits);
return QPointF(qRound(xy.x() * multiplier),
qRound(xy.y() * multiplier));
}
// If converting to other units, need to convert to unrounded points first
QPointF pointXy = (fromUnits == QPageLayout::Point) ? xy : xy * qt_pointMultiplier(fromUnits);
// Then convert from points to required units rounded to 2 decimal places
const qreal multiplier = qt_pointMultiplier(toUnits);
return QPointF(qRound(pointXy.x() * 100 / multiplier) / 100.0,
qRound(pointXy.y() * 100 / multiplier) / 100.0);
}
Q_GUI_EXPORT QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits)
{
// If the margins have the same units, or are all 0, then don't need to convert
if (fromUnits == toUnits || margins.isNull())
return margins;
// If converting to points then convert and round to 0 decimal places
// If converting to points then convert and round up to 2 decimal places
if (toUnits == QPageLayout::Point) {
const qreal multiplier = qt_pointMultiplier(fromUnits);
return QMarginsF(qRound(margins.left() * multiplier),
qRound(margins.top() * multiplier),
qRound(margins.right() * multiplier),
qRound(margins.bottom() * multiplier));
const qreal multiplierX100 = qt_pointMultiplier(fromUnits) * 100;
return QMarginsF(qCeil(margins.left() * multiplierX100) / 100.0,
qCeil(margins.top() * multiplierX100) / 100.0,
qCeil(margins.right() * multiplierX100) / 100.0,
qCeil(margins.bottom() * multiplierX100) / 100.0);
}
// If converting to other units, need to convert to unrounded points first

View File

@ -12,6 +12,8 @@ private slots:
void invalid();
void basics();
void setGetMargins();
void setUnits_data();
void setUnits();
};
void tst_QPageLayout::invalid()
@ -238,6 +240,54 @@ void tst_QPageLayout::setGetMargins()
QCOMPARE(fullPage.maximumMargins(), max);
}
void tst_QPageLayout::setUnits_data()
{
QTest::addColumn<QPageLayout::Unit>("units");
QTest::newRow("Millimeter") << QPageLayout::Millimeter;
QTest::newRow("Point") << QPageLayout::Point;
QTest::newRow("Inch") << QPageLayout::Inch;
QTest::newRow("Pica") << QPageLayout::Pica;
QTest::newRow("Didot") << QPageLayout::Didot;
QTest::newRow("Cicero") << QPageLayout::Cicero;
}
void tst_QPageLayout::setUnits()
{
QFETCH(QPageLayout::Unit, units);
QPageLayout pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(), units);
int maxLeftX100 = qFloor(pageLayout.maximumMargins().left() * 100);
QVERIFY(maxLeftX100 > 0);
for (int i = 1; i <= maxLeftX100; ++i) {
const qreal margin = i / 100.;
const QMarginsF unitsMargins = QMarginsF(margin, margin, margin, margin);
pageLayout.setMargins(unitsMargins);
pageLayout.setUnits(QPageLayout::Point);
const QMarginsF pointsMargins = pageLayout.margins();
if (units == QPageLayout::Point) {
QCOMPARE(pointsMargins, unitsMargins);
} else {
QCOMPARE_GT(pointsMargins.left(), unitsMargins.left());
QCOMPARE_GT(pointsMargins.top(), unitsMargins.top());
QCOMPARE_GT(pointsMargins.right(), unitsMargins.right());
QCOMPARE_GT(pointsMargins.bottom(), unitsMargins.bottom());
}
pageLayout.setUnits(units);
const QMarginsF convertedUnitsMargins = pageLayout.margins();
if (units == QPageLayout::Didot) {
// When using Didot units, the small multiplier and ceiling function in conversion
// may cause the converted units to not match the original exactly. However, we
// can verify that the converted margins are always greater than or equal to the
// original.
QCOMPARE_GE(convertedUnitsMargins.left(), unitsMargins.left());
QCOMPARE_GE(convertedUnitsMargins.top(), unitsMargins.top());
QCOMPARE_GE(convertedUnitsMargins.right(), unitsMargins.right());
QCOMPARE_GE(convertedUnitsMargins.bottom(), unitsMargins.bottom());
} else {
QCOMPARE(convertedUnitsMargins, unitsMargins);
}
}
}
QTEST_APPLESS_MAIN(tst_QPageLayout)
#include "tst_qpagelayout.moc"