Bug 170136 - OutputDevice::LogicToLogic ignores MapMode scaling factors (SetScaleX/Y) during unit conversion
Summary: OutputDevice::LogicToLogic ignores MapMode scaling factors (SetScaleX/Y) duri...
Status: RESOLVED INVALID
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: graphics stack (show other bugs)
Version:
(earliest affected)
Inherited From OOo
Hardware: All All
: medium normal
Assignee: Not Assigned
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-12-27 03:52 UTC by Chris Sherlock
Modified: 2025-12-27 05:54 UTC (History)
0 users

See Also:
Crash report or crash signature:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Sherlock 2025-12-27 03:52:25 UTC
Description:
he static method OutputDevice::LogicToLogic(Point, MapMode, MapMode) fails to account for user-defined scaling factors (SetScaleX / SetScaleY) when converting between different MapUnit types.

While the function correctly handles Origin shifts (SetOrigin), it seems to calculate the conversion factor solely based on the physical unit ratio (e.g., MapMM vs MapInch) and ignores the specific scaling fraction set on the MapMode.

This means that if a Source MapMode is set to MapMM with a scale of 0.5 (50% Zoom), and the Destination is Map100thMM (scale 1.0), the function performs a purely physical conversion (10mm -> 1000 100thMM) rather than the expected scaled conversion (10mm * 0.5 -> 500 100thMM).

Steps to Reproduce:
So I stumbled across this whilst trying to write some unit tests for LogicToLogic(). The following function in a unit test shows the issue:

void testCombinedScaleAndOrigin()
{
    // Scenario: Source is MM with 50% scale. Dest is 100thMM.
    // We expect the result to be scaled by 0.5.

    MapMode aSource(MapUnit::MapMM);
    aSource.SetScaleX(Fraction(1, 2)); // 0.5 Scale
    aSource.SetScaleY(Fraction(1, 2));
    aSource.SetOrigin(Point(10, 0));

    MapMode aDest(MapUnit::Map100thMM);

    Point aPt(30, 0);

    // Expected Calculation:
    // 1. Remove Origin: 30 - 10 = 20
    // 2. Apply Scale:   20 * 0.5 = 10 (Physical MM)
    // 3. Convert Unit:  10 MM -> 1000 100thMM
    // Expected Result: 1000

    Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest);

    // Actual Result: 2000
    // The scale factor (0.5) was ignored.
    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aResult.X());
}

Actual Results:
The function returns 2000 (it calculated (30-10) * 1.0 * UnitFactor).

Expected Results:
The function should return 1000 (it should calculate (30-10) * 0.5 * UnitFactor).


Reproducible: Always


User Profile Reset: No

Additional Info:
The issue is located in vcl/source/outdev/map.cxx. The implementation calculates the unit conversion factors (likely via lcl_calcConversionMapRes) but never multiplies them by rMapModeSource.GetScaleX() or rMapModeDest.GetScaleX().

This affects any stateless coordinate conversion that passes explicit MapMode objects, particularly in:

* PDF Export (vcl/source/pdf/pdfwriter_impl2.cxx)

* SVG/Metafile Export (filter/source/svg/svgwriter.cxx, vcl/source/gdi/gdimtf.cxx)

* EditEngine text layout (editeng/source/editeng/impedit.cxx)

Member functions like OutputDevice::LogicToPixel (which rely on mnMapScNumX member variables) are seemingly unaffected as SetMapMode correctly pre-calculates the factors.

Now... I can fix the function, but the issue is: I don't know what impact this would have on anything that is relying on this existing functionality! However, I'm pretty sure this is causing a lot of weird issues in exports...
Comment 1 Chris Sherlock 2025-12-27 03:59:54 UTC
Apologies, I had started to refactor some code and had already switched to using the function lcl_calcConversionMapRes() and I accidentally referred to this in my report. 

My code refactoring efforts will shortly be pushed to gerrit - they haven't changed any functionality, but the unit tests I wrote did find this particular issue.
Comment 2 Chris Sherlock 2025-12-27 05:54:40 UTC
I'm mistaken.