download.lst | 4 editeng/source/editeng/impedit2.cxx | 13 + external/openldap/0001-const-up-oids.patch.1 | 44 ----- external/openldap/UnpackedTarball_openldap.mk | 1 sd/qa/ui/func/func.cxx | 47 +++++ sd/source/ui/func/fuolbull.cxx | 2 sfx2/source/doc/objmisc.cxx | 2 svx/source/sdr/primitive2d/sdrdecompositiontools.cxx | 12 + sw/inc/AnnotationWin.hxx | 1 sw/inc/PostItMgr.hxx | 4 sw/inc/fldbas.hxx | 6 sw/qa/core/text/itrpaint.cxx | 11 + sw/qa/extras/ooxmlexport/ooxmlexport15.cxx | 6 sw/qa/extras/uiwriter/data/tdf163194-two-comments.fodt | 41 +++++ sw/qa/extras/uiwriter/uiwriter11.cxx | 136 +++++++++++++++++ sw/qa/extras/uiwriter/uiwriter4.cxx | 62 +++++++ sw/qa/extras/uiwriter/uiwriter8.cxx | 2 sw/source/core/layout/fly.cxx | 7 sw/source/core/view/vprint.cxx | 10 + sw/source/filter/ww8/docxattributeoutput.cxx | 2 sw/source/filter/ww8/wrtw8sty.cxx | 5 sw/source/filter/ww8/wrtww8.cxx | 1 sw/source/filter/ww8/wrtww8.hxx | 1 sw/source/uibase/docvw/AnnotationWin2.cxx | 14 + sw/source/uibase/docvw/PostItMgr.cxx | 36 +++- sw/source/uibase/fldui/fldmgr.cxx | 5 sw/source/uibase/uiview/view0.cxx | 18 ++ sw/source/uibase/uiview/viewmdi.cxx | 3 28 files changed, 416 insertions(+), 80 deletions(-)
New commits: commit 230edfdac202e68a622eed5a8bf3ce250de28a41 Author: Andreas Heinisch <[email protected]> AuthorDate: Thu Jan 15 18:08:26 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:45 2026 +0100 tdf#98446 - Switch to single-page view mode when hiding whitespace Switch to single-page view mode when hiding whitespace while in multi-page or book view since hiding whitespace is only possible in single-view page mode. Change-Id: Ib3a4281aa0ff1d47a8821aef765003de9ed99173 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197381 Tested-by: Jenkins Reviewed-by: Andreas Heinisch <[email protected]> (cherry picked from commit ef562a3be1d9a283966ad2289fa3a24a7014c774) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198478 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx index 914586cade41..a4c3b4a62bbc 100644 --- a/sw/qa/extras/uiwriter/uiwriter4.cxx +++ b/sw/qa/extras/uiwriter/uiwriter4.cxx @@ -113,6 +113,68 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf96515) CPPUNIT_ASSERT_EQUAL(1, getPages()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf98446_switch_to_single_page_on_hide_whitespace) +{ + createSwDoc(); + + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + SwView* pView = pDocShell->GetView(); + + // Enable hide whitespace view mode + SwViewOption aViewOptions(*pWrtShell->GetViewOptions()); + aViewOptions.SetHideWhitespaceMode(true); + pWrtShell->ApplyViewOptions(aViewOptions); + CPPUNIT_ASSERT(pWrtShell->GetViewOptions()->IsWhitespaceHidden()); + + // --- Case 1: Multiple pages per row ------------------------------------- + + // Switch to multiple pages per row + dispatchCommand(mxComponent, u".uno:MultiplePagesPerRow"_ustr, {}); + // Without the fix in place, this test would have failed with + // - Expected: Switching back to single-page layout when hiding whitespace + // - Actual : Layout remained in multi-column mode + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden()); + + // Re-apply hide whitespace in multi-column mode + pWrtShell->ApplyViewOptions(aViewOptions); + + // Without the fix in place, this test would have failed with + // - Expected: Switching back to single-page layout when hiding whitespace + // - Actual : Layout remained in multi-column mode + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsViewLayoutBookMode()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pWrtShell->GetViewOptions()->GetViewLayoutColumns()); + + // --- Case 2: Book mode -------------------------------------------------- + dispatchCommand(mxComponent, u".uno:BookView"_ustr, {}); + // Without the fix in place, this test would have failed with + // - Expected: Switching back to single-page layout when hiding whitespace + // - Actual : Layout remained in book mode + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden()); + + // Re-apply hide whitespace in multi-column mode + pWrtShell->ApplyViewOptions(aViewOptions); + // Without the fix in place, this test would have failed with + // - Expected: Switching back to single-page layout when hiding whitespace + // - Actual : Layout remained in book mode + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsViewLayoutBookMode()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pWrtShell->GetViewOptions()->GetViewLayoutColumns()); + + // --- Case 3: Manual layout changes -------------------------------------- + + // Without the fix in place, this test would have failed with + // - Expected: Multi-column layout disables whitespace hiding + // - Actual : Whitespace remained hidden in multi-column mode + pView->SetViewLayout(/*nColumns=*/0, /*bBookMode=*/false); + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden()); + + // Without the fix in place, this test would have failed with + // - Expected: Multi-column layout disables whitespace hiding + // - Actual : Whitespace remained hidden in multi-column mode + pView->SetViewLayout(/*nColumns=*/2, /*bBookMode=*/true); + CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden()); +} + static OUString lcl_translitTest(SwDoc& rDoc, const SwPaM& rPaM, TransliterationFlags const nType) { utl::TransliterationWrapper aTrans(::comphelper::getProcessComponentContext(), nType); diff --git a/sw/source/uibase/uiview/view0.cxx b/sw/source/uibase/uiview/view0.cxx index 0ee6dab7bf72..842f12aa3241 100644 --- a/sw/source/uibase/uiview/view0.cxx +++ b/sw/source/uibase/uiview/view0.cxx @@ -523,6 +523,13 @@ void SwView::ExecViewOptions(SfxRequest &rReq) bFlag = !pOpt->IsHideWhitespaceMode(); pOpt->SetHideWhitespaceMode(bFlag); + + // tdf#98446 - switch to single-page view when selecting hide whitespaces option + if (bFlag && !pOpt->CanHideWhitespace()) + { + pOpt->SetViewLayoutBookMode(false); + pOpt->SetViewLayoutColumns(1); + } break; case FN_VIEW_SHOW_WHITESPACE: @@ -530,6 +537,13 @@ void SwView::ExecViewOptions(SfxRequest &rReq) bFlag = pOpt->IsHideWhitespaceMode(); pOpt->SetHideWhitespaceMode(!bFlag); + + // tdf#98446 - switch to single-page view when deselecting show whitespaces option + if (!bFlag && !pOpt->CanHideWhitespace()) + { + pOpt->SetViewLayoutBookMode(false); + pOpt->SetViewLayoutColumns(1); + } break; case FN_VIEW_SMOOTH_SCROLL: @@ -809,11 +823,15 @@ void SwView::ExecViewOptions(SfxRequest &rReq) case FN_MULTIPLE_PAGES_PER_ROW: pOpt->SetViewLayoutBookMode( false ); pOpt->SetViewLayoutColumns( 0 ); + // tdf#98446 - hiding whitespace is only possible in single-page view + pOpt->SetHideWhitespaceMode(false); break; case FN_BOOKVIEW: pOpt->SetViewLayoutColumns( 2 ); pOpt->SetViewLayoutBookMode( true ); + // tdf#98446 - hiding whitespace is only possible in single-page view + pOpt->SetHideWhitespaceMode(false); break; case SID_CLICK_CHANGE_ROTATION: if( STATE_TOGGLE == eState ) diff --git a/sw/source/uibase/uiview/viewmdi.cxx b/sw/source/uibase/uiview/viewmdi.cxx index 24b55ca906d4..0d77ec8dc5b2 100644 --- a/sw/source/uibase/uiview/viewmdi.cxx +++ b/sw/source/uibase/uiview/viewmdi.cxx @@ -252,6 +252,9 @@ void SwView::SetViewLayout( sal_uInt16 nColumns, bool bBookMode, bool bViewOnly SwViewOption aOpt( *pOpt ); aOpt.SetViewLayoutColumns( nColumns ); aOpt.SetViewLayoutBookMode( bBookMode ); + // tdf#98446 - hiding whitespace is only possible in single-page view + if (!aOpt.CanHideWhitespace()) + aOpt.SetHideWhitespaceMode(false); m_pWrtShell->ApplyViewOptions( aOpt ); } commit 2919f8f11476fabe225015311143a694883f88ac Author: Miklos Vajna <[email protected]> AuthorDate: Wed Jan 28 15:26:24 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 cool#13988 sw redline render mode: add colored border for inline images Anchored images conditionally got a red/green frame in since commit afdf4e287fb91c7baf2767eb95b0565472b8343d (cool#13988 sw redline render mode: add colored border for anchored images, 2026-01-27), but the same didn't work for inline images. Seems what happens is that SwFlyFrame::GetRedlineRenderModeFrame() sets the border line with to 1 (in twips, so some minimal value), but then this is turned into a SdrFrameBorderPrimitive2D, which gets decomposed into a PolygonStrokePrimitive2D. At this point the cairo pixel processor handles it at CairoPixelProcessor2D::processPolygonStrokePrimitive2D(), which assumes that hairline is when the width is 0. The trouble is that in case Writer sets the width to 0, then we don't even start processing such stroke primitives. Solve the problem by going with a larger border width: just query from output device what would be a logic value for a 1px border, so it does show up reliably. This is similar to how SwViewOption::s_nPixelTwips gets configured already. Note that the testcase just asserts these polygons are painted, but it can't verify how CairoPixelProcessor2D simply didn't paint the old bad width. Change-Id: I83f7aec4d09fc628dd314e1325bfd47af07308ca Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198339 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 36a6ca3e6de7106bdd0497386d29a05337c5a90c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198352 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx index 14c1af1a02b4..b5debdfa8f83 100644 --- a/sw/qa/core/text/itrpaint.cxx +++ b/sw/qa/core/text/itrpaint.cxx @@ -290,6 +290,9 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); + std::vector<tools::Polygon> aPolygons = GetMetaFilePolylines(*xMetaFile); + // No frames around images. + CPPUNIT_ASSERT(aPolygons.empty()); // Omit insert: default, default, grayscale. SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); @@ -306,6 +309,10 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) // Without the accompanying fix in place, this test would have failed, the image's center pixel // wasn't gray. CPPUNIT_ASSERT(IsGrayScale(aImages[2])); + aPolygons = GetMetaFilePolylines(*xMetaFile); + // Frame around the deleted image: no polygons -> no frame. + CPPUNIT_ASSERT(!aPolygons.empty()); + CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[1].m_aRectangle, aPolygons)); // Omit deletes: default, grayscale, default. aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes); @@ -318,6 +325,10 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(IsGrayScale(aImages[1])); CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); + aPolygons = GetMetaFilePolylines(*xMetaFile); + // Frame around the inserted image. + CPPUNIT_ASSERT(!aPolygons.empty()); + CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[2].m_aRectangle, aPolygons)); } } diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 8e02d478681a..ff838a1e5763 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -2364,7 +2364,12 @@ bool SwFlyFrame::GetRedlineRenderModeFrame(SvxBoxItem& rBoxItem) const } editeng::SvxBorderLine aBorderLine; - aBorderLine.SetWidth(1); + + // Set a logic value which is roughly 1 pixel wide, so the border is visible. + vcl::RenderContext* pOut = pViewShell->GetOut(); + tools::Long nWidth = pOut ? pOut->PixelToLogic(Size(1, 1)).Width() : 0; + aBorderLine.SetWidth(nWidth); + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); aBorderLine.SetColor(*oColor); rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::LEFT); commit 12c43741345e06096c60356da899a15a8465f2ff Author: Miklos Vajna <[email protected]> AuthorDate: Fri Jan 30 13:08:27 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 editeng: fix crash in ImpEditEngine::GetXPos() gdb backtrace on the core file from the crashreport: #5 0x0000000000afc0ee in std::__glibcxx_assert_fail(char const*, int, char const*, char const*) () #6 0x00007078315a78ff in std::vector<double, std::allocator<double> >::operator[] (this=<optimized out>, __n=<optimized out>) at /opt/rh/devtoolset-12/root/usr/include/c++/12/bits/stl_vector.h:1140 #7 std::vector<double, std::allocator<double> >::operator[] (this=<optimized out>, __n=<optimized out>) at /opt/rh/devtoolset-12/root/usr/include/c++/12/bits/stl_vector.h:1140 #8 ImpEditEngine::GetXPos (this=this@entry=0x3e8745e0, rParaPortion=..., rLine=..., nIndex=-18, bPreferPortionStart=<optimized out>) at editeng/source/editeng/impedit2.cxx:4303 #9 0x00007078315a79f6 in ImpEditEngine::GetEditCursor (this=this@entry=0x3e8745e0, rPortion=..., rLine=..., nIndex=<optimized out>, aFlags=aFlags@entry=...) at editeng/source/editeng/impedit2.cxx:3113 and: #8 ImpEditEngine::GetXPos (this=this@entry=0x3e8745e0, rParaPortion=..., rLine=..., nIndex=-18, bPreferPortionStart=<optimized out>) at editeng/source/editeng/impedit2.cxx:4303 4303 nPortionTextWidth = rLine.GetCharPosArray()[nTextPortionStart + rPortion.GetLen() - 1 - rLine.GetStart()]; (gdb) print rLine.maPositions $1 = std::vector of length 1, capacity 1 = {171.307373046875} (gdb) print nTextPortionStart $2 = 0 (gdb) print rPortion $3 = (const TextPortion &) @0x3b3cc250: {xExtraInfos = std::unique_ptr<ExtraPortionInfo> = {get() = 0x0}, nLen = 38, aOutSz = {<SizeTemplate<Size>> = {<SizeTemplateBase> = {<Pair> = { mnA = 5661, mnB = 344}, <No data fields>}, <No data fields>}, <No data fields>}, nKind = PortionKind::TEXT, nRightToLeftLevel = 0 ' (gdb) print rLine $4 = (const EditLine &) @0x3e968420: {maPositions = std::vector of length 1, capacity 1 = {171.307373046875}, maKashidaPositions = std::vector of length 0, capacity 0, mnTextWidth = 171, mnStartPosX = 3443, mnNextLinePosXDiff = 0, mnStart = 39, mnEnd = 40, mnStartPortion = 2, mnEndPortion = 2, mnHeight = 344, mnTextHeight = 344, mnMaxAscent = 265, mbHangingPunctuation = false, mbInvalid = true} Seeing nPortionTextWidth is updated conditionally, also require to only update if the array index would be inside the array bounds. Change-Id: I98adbc55187f0221534bc358755e51160cdb992b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198436 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit d7c2f3dfe78c91cb7610cc71545f822481235595) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198438 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index 6d64243e1ffe..21709b00dcb6 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -4604,7 +4604,18 @@ tools::Long ImpEditEngine::GetXPos(ParaPortion const& rParaPortion, EditLine con // But the array might not be init yet, if using text ranger this method is called within CreateLines()... tools::Long nPortionTextWidth = rPortion.GetSize().Width(); if ( ( rPortion.GetKind() == PortionKind::TEXT ) && rPortion.GetLen() && !GetTextRanger() ) - nPortionTextWidth = rLine.GetCharPosArray()[nTextPortionStart + rPortion.GetLen() - 1 - rLine.GetStart()]; + { + sal_Int32 nCharPosArrayIndex = nTextPortionStart + rPortion.GetLen() - 1 - rLine.GetStart(); + if (nCharPosArrayIndex >= 0 + && o3tl::make_unsigned(nCharPosArrayIndex) < rLine.GetCharPosArray().size()) + { + nPortionTextWidth = rLine.GetCharPosArray()[nCharPosArrayIndex]; + } + else + { + SAL_WARN("editeng", "ImpEditEngine::GetXPos: out of bounds access to rLine.GetCharPosArray()"); + } + } if ( nTextPortionStart != nIndex ) { commit 5b92e588c7591119ae644af8d248afc05b2299af Author: Xisco Fauli <[email protected]> AuthorDate: Fri Jan 30 09:39:52 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 openldap: upgrade to 2.6.12 * 0001-const-up-oids.patch.1 has been fixed upstream Downloaded from https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.6.12.tgz Change-Id: I253858a6f7b60218f30352ec751cd18fa6dfdf09 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198401 Reviewed-by: Xisco Fauli <[email protected]> Tested-by: Jenkins (cherry picked from commit 0e38fd3a3f2453048b68a24c5af3dd0534ab5b66) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198409 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/download.lst b/download.lst index 5283c13b7c1a..c0b238734f3b 100644 --- a/download.lst +++ b/download.lst @@ -617,8 +617,8 @@ ONLINEUPDATE_TARBALL := onlineupdate-c003be8b9727672e7d30972983b375f4c200233f-2. # three static lines # so that git cherry-pick # will not run into conflicts -OPENLDAP_SHA256SUM := c065f04aad42737aebd60b2fe4939704ac844266bc0aeaa1609f0cad987be516 -OPENLDAP_TARBALL := openldap-2.6.10.tgz +OPENLDAP_SHA256SUM := 1716ad779e85d743694c3e3b05277fb71b6a5eadca43c7a958aa62683b22208e +OPENLDAP_TARBALL := openldap-2.6.12.tgz # three static lines # so that git cherry-pick # will not run into conflicts diff --git a/external/openldap/0001-const-up-oids.patch.1 b/external/openldap/0001-const-up-oids.patch.1 deleted file mode 100644 index 53d6df075325..000000000000 --- a/external/openldap/0001-const-up-oids.patch.1 +++ /dev/null @@ -1,44 +0,0 @@ -From c7571ffd4d17825a9054e2e204ee61127bedb0b0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <[email protected]> -Date: Sat, 26 Jul 2025 21:00:50 +0100 -Subject: [PATCH] const up oids - -to move it out of the .data section ---- - libraries/libldap/tls2.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c -index 1bda3fb2aa..1fb878aab8 100644 ---- a/libraries/libldap/tls2.c -+++ b/libraries/libldap/tls2.c -@@ -51,7 +51,7 @@ typedef struct oid_name { - struct berval name; - } oid_name; - --static oid_name oids[] = { -+static const oid_name oids[] = { - { BER_BVC("2.5.4.3"), BER_BVC("cn") }, - { BER_BVC("2.5.4.4"), BER_BVC("sn") }, - { BER_BVC("2.5.4.6"), BER_BVC("c") }, -@@ -1394,7 +1394,7 @@ ldap_start_tls_s ( LDAP *ld, - #define LBER_TAG_UNIVERSAL ((ber_tag_t) 0x1cUL) - #define LBER_TAG_BMP ((ber_tag_t) 0x1eUL) - --static oid_name * -+static const oid_name * - find_oid( struct berval *oid ) - { - int i; -@@ -1519,7 +1519,7 @@ ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, - int csize; - ber_tag_t tag; - ber_len_t len; -- oid_name *oidname; -+ const oid_name *oidname; - - struct berval Oid, Val, oid2, *in = x509_name; - --- -2.49.0 - diff --git a/external/openldap/UnpackedTarball_openldap.mk b/external/openldap/UnpackedTarball_openldap.mk index a5b365535b2e..661d509187f9 100644 --- a/external/openldap/UnpackedTarball_openldap.mk +++ b/external/openldap/UnpackedTarball_openldap.mk @@ -24,7 +24,6 @@ $(eval $(call gb_UnpackedTarball_update_autoconf_configs,openldap,\ $(eval $(call gb_UnpackedTarball_add_patches,openldap,\ external/openldap/openldap-2.4.44.patch.1 \ - external/openldap/0001-const-up-oids.patch.1 \ )) # vim: set noet sw=4 ts=4: commit b01fb1622b9cb131476c981696c5b7a04c3d5811 Author: Andreas Heinisch <[email protected]> AuthorDate: Fri Jan 23 18:24:45 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 tdf#170392 - Align file name format constants with translate constants Otherwise, conversions during the save/insert process will change the format of the file name field because the constants need to be converted from SwFileNameFormat to FilenameDisplayFormat and vice versa. Change-Id: I6ee3ae3adc34a86560381f8e9585a016d4f312ec Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198019 Tested-by: Jenkins Reviewed-by: Andreas Heinisch <[email protected]> (cherry picked from commit f7588e928a6de891e1fbf3b4d591e7d69347813a) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198451 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sw/inc/fldbas.hxx b/sw/inc/fldbas.hxx index 0f60a85a0e00..1d29362b3caa 100644 --- a/sw/inc/fldbas.hxx +++ b/sw/inc/fldbas.hxx @@ -148,12 +148,14 @@ enum class SwFieldTypesEnum : sal_uInt16 { LAST = ParagraphSignature, Unknown = USHRT_MAX // used by SwFieldMgr::GetCurTypeId }; + +// tdf#170392 - this should be aligned with FMT_FF_ARY in sw/source/uibase/fldui/fldmgr.cxx enum class SwFileNameFormat { // most of the constants are a regular enum - Name, - PathName, Path, + PathName, NameNoExt, + Name, UIName, UIRange, End, // marker value, used for asserts diff --git a/sw/source/uibase/fldui/fldmgr.cxx b/sw/source/uibase/fldui/fldmgr.cxx index 0ad355618552..802d7f37f219 100644 --- a/sw/source/uibase/fldui/fldmgr.cxx +++ b/sw/source/uibase/fldui/fldmgr.cxx @@ -188,12 +188,13 @@ const TranslateId FMT_NUM_ARY[] = FMT_NUM_PAGESPECIAL }; +// tdf#170392 - this should be aligned with enum SwFileNameFormat in sw/inc/fldbas.hxx const TranslateId FMT_FF_ARY[] = { - FMT_FF_NAME, - FMT_FF_PATHNAME, FMT_FF_PATH, + FMT_FF_PATHNAME, FMT_FF_NAME_NOEXT, + FMT_FF_NAME, FMT_FF_UI_NAME, FMT_FF_UI_RANGE }; commit 8e2580f9ca67a9b750ac3357b0b3d7000ed29f0f Author: Mike Kaganski <[email protected]> AuthorDate: Sat Jan 31 20:37:23 2026 +0500 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 tdf#163194: Make width of comments in margin fixed on print / PDF export Previously, the width of the comments in margin on print / PDF export was calculated using the same width factor, as when shown in the UI. Before commit ac2720dcbe4e51e7f6733a385b5f7b571c6431e9 (tdf#73953 sw: Allow resizing the comment section, 2024-01-11), there was no way to change that, and the factor was hardcoded as 1.8 (and the print / PDF export implementation in SwViewShell::PrintOrPDFExport relied on that). The mentioned commit introduced a configuration DisplayWidthFactor, that stored the user-defined width factor; and then in commit cf9d8631ee5d4b894425446a9e6a8c939ab0309c (Related tdf#73953 Increase default comment width, 2024-01-11), its default value was increased to 3.0, which immediately affected the width of the comments in print and PDF output. The new default created comments that were too wide for the page, even with default DPI. There was another problem with existing implementation: the comment width depended on DPI. It meant, that when a HiDPI monotor was used, the comments printed / exported in margins were narrower, than when it was done using a standard-DPI monitor. On HiDPI systems, a larger width factor was necessary to see the "comment doesn't fit into the page" problem. Here, the old behavior is restored, where print and PDF output use a fixed value of the comment width factor. Also, the output width now doesn't depend on DPI. Two other possibilities were considered: 1. Keep using user-defined width factor, as in UI, and calculate the correct comment position, as well as original page scaling factor, to fit them all on the output page; 2. Introduce a new configuration, dedicated for width of comments in margins of pages in print / PDF export (and, again, calculate the layout using that factor). They both were rejected, because it doesn't make sense to reduce the original page size too much. Currently, the original page is reduced by factor of 0.75, to make room for the comments. Allowing more space for comments could easily make the page reduced to one quarter of the original size, or even smaller. And the text of comments will scale down as well, making it unreadable with too wide comments area. One problem that required solving was that dynamically changing the width factor changed sizes of the comments, and that needed to be restored (otherwise, exporting to PDF would change all UI sizes of comments). For that, a good guess of the final comment text height was needed; previously, it worked because comments were re-layouted several times before they were finally shown, and that compensated for the incorrect initial height used in SwPostItMgr::LayoutPostIts. But for print / output, where there is only one layout for output, and one layout for restoring the size, a new method was introduced in SwAnnotationWin, GuessTextHeightForWidth. This restores testTdf152575 to the original state before the commit cf9d8631ee5d4b894425446a9e6a8c939ab0309c. Change-Id: Ib0dd195d7594b51be86e489cc06cae00b26bf29b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198457 Reviewed-by: Mike Kaganski <[email protected]> Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198464 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sw/inc/AnnotationWin.hxx b/sw/inc/AnnotationWin.hxx index 9be34683fc3d..622cb88f7396 100644 --- a/sw/inc/AnnotationWin.hxx +++ b/sw/inc/AnnotationWin.hxx @@ -111,6 +111,7 @@ class SAL_DLLPUBLIC_RTTI SwAnnotationWin final : public InterimItemWindow ::sw::overlay::OverlayRanges* TextRange() { return mpTextRangeOverlay.get();} tools::Long GetPostItTextHeight(); + tools::Long GuessTextHeightForWidth(tools::Long nWidth) const; void SwitchToPostIt(sal_uInt16 aDirection); void SwitchToFieldPos(); diff --git a/sw/inc/PostItMgr.hxx b/sw/inc/PostItMgr.hxx index 7ff84952c887..11c68d0e0cb8 100644 --- a/sw/inc/PostItMgr.hxx +++ b/sw/inc/PostItMgr.hxx @@ -99,7 +99,7 @@ class SAL_DLLPUBLIC_RTTI SwPostItMgr final : public SfxListener, FieldShadowState mShadowState; std::optional<OutlinerParaObject> mpAnswer; OUString maAnswerText; - bool mbForceShow = false; + bool mbForceShowForPrintOrPdf = false; // data structure to collect the <SwAnnotationWin> instances for certain <SwFrame> instances. std::unique_ptr<sw::sidebarwindows::SwFrameSidebarWinContainer> mpFrameSidebarWinContainer; @@ -255,7 +255,7 @@ class SAL_DLLPUBLIC_RTTI SwPostItMgr final : public SfxListener, sw::sidebarwindows::SidebarPosition GetSidebarPos(const Point& rPointLogic); - void SetForceShow(bool bForce) { mbForceShow = bForce; } + void SetForceShowForPrintOrPdf(bool bForce) { mbForceShowForPrintOrPdf = bForce; } // The commands that directly delete comments (as opposed to deletion of the text that the // comments are anchored to) behave differently, depending on the document type, when the diff --git a/sw/qa/extras/uiwriter/data/tdf163194-two-comments.fodt b/sw/qa/extras/uiwriter/data/tdf163194-two-comments.fodt new file mode 100644 index 000000000000..ae280a0dd204 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf163194-two-comments.fodt @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:font-face-decls> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="12.51mm" style:writing-mode="page"/> + <style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="GB" style:letter-kerning="true"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <style:style style:name="Comment" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"> + <style:paragraph-properties fo:margin-left="1mm" fo:margin-right="1mm" fo:margin-top="1mm" fo:margin-bottom="0mm" style:contextual-spacing="false" fo:line-height="100%" fo:text-indent="0mm" style:auto-text-indent="false"/> + <style:text-properties fo:font-size="10pt"/> + </style:style> + </office:styles> + <office:automatic-styles> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="210mm" fo:page-height="297mm" style:print-orientation="portrait" fo:margin-top="20mm" fo:margin-bottom="20mm" fo:margin-left="20mm" fo:margin-right="20mm"/> + </style:page-layout> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1"/> + </office:master-styles> + <office:body> + <office:text> + <text:p>He heard quiet steps behind him<office:annotation> + <dc:creator>Mike</dc:creator> + <dc:date>2026-01-31T17:49:06.393884900</dc:date> + <meta:creator-initials>M</meta:creator-initials> + <text:p text:style-name="Comment">A comment. One two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty.</text:p> + </office:annotation>. That didn't bode well. Who could be following him this late at night and in this deadbeat part of town? And at this particular moment, just after he pulled off the big time and was making off with the greenbacks<office:annotation> + <dc:creator>Mike</dc:creator> + <dc:date>2026-01-31T17:51:06.483107200</dc:date> + <meta:creator-initials>M</meta:creator-initials> + <text:p text:style-name="Comment">Another comment. Twenty-one twenty-two twenty-three twenty-four twenty-five twenty-six twenty-seven twenty-eight twenty-nine thirty.</text:p> + </office:annotation>. Was there another crook who'd had the same idea, and was now watching him and waiting for a chance to grab the fruit of his labour? Or did the steps behind him mean that one of many law officers in town was on to him and just waiting to pounce and snap those cuffs on his wrists? He nervously looked all around. Suddenly he saw the alley. Like lightning he darted off to the left and disappeared between the two warehouses almost falling over the bin lying in the middle of the pavement. He tried to nervously tap his way along in the inky darkness and suddenly stiffened: it was a dead-end, he would have to go back the way he had come. The steps got louder and louder, he saw the black outline of a figure coming around the corner. Is this the end of the line? he thought pressing himself back against the wall trying to make himself invisible in the dark, was all that planning and energy wasted? He was dripping with sweat now, cold and wet, he could smell the fear coming off his clothes. Suddenly next to him, with a barely noticeable squeak, a door swung quietly to and fro in the night's breeze. Could this be the haven he'd prayed for? Slowly he slid toward the door, pressing himself more and more into the wall, into the dark, away from his enemy. Would this door save his hide?</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter11.cxx b/sw/qa/extras/uiwriter/uiwriter11.cxx index dfe2c60ffee5..d04cd758dd06 100644 --- a/sw/qa/extras/uiwriter/uiwriter11.cxx +++ b/sw/qa/extras/uiwriter/uiwriter11.cxx @@ -8,6 +8,9 @@ */ #include <swmodeltestbase.hxx> + +#include <officecfg/Office/Writer.hxx> +#include <vcl/pdf/PDFPageObjectType.hxx> #include <vcl/scheduler.hxx> #include <comphelper/propertyvalue.hxx> @@ -425,6 +428,139 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testExtrudedShapeSelection) CPPUNIT_ASSERT(xSelections.is()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testTdf163194) +{ + // Test (1) that exporting comments in margin to PDF produces expected layout of the comments, + // that have limited and reasonable width and position, inside the page bounds (and close to + // its right side), independent on the manually set comment sidebar width; and (2) that the + // export doesn't change the annotation sizes in the document. + + // Annotation size depends on DisplayWidthFactor. Set it to a fixed value for testing. + comphelper::ScopeGuard + aReset([oldValue = officecfg::Office::Writer::Notes::DisplayWidthFactor::get()]() { + auto pChanges(comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::Notes::DisplayWidthFactor::set(oldValue, pChanges); + pChanges->commit(); + }); + { + auto pChanges(comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::Notes::DisplayWidthFactor::set(4.0, pChanges); + pChanges->commit(); + } + + createSwDoc("tdf163194-two-comments.fodt"); + + SwDocShell* pDocShell = getSwDocShell(); + CPPUNIT_ASSERT(pDocShell); + SwView* pView = pDocShell->GetView(); + CPPUNIT_ASSERT(pView); + SwPostItMgr* pPostItMgr = pView->GetPostItMgr(); + CPPUNIT_ASSERT(pPostItMgr); + + CPPUNIT_ASSERT_EQUAL(size_t(2), pPostItMgr->GetPostItFields().size()); + std::vector<tools::Long> aOriginalHeights; + for (const auto& pAnnotationItem : pPostItMgr->GetPostItFields()) + { + CPPUNIT_ASSERT(pAnnotationItem); + CPPUNIT_ASSERT(pAnnotationItem->mpPostIt); + const Size aSize = pAnnotationItem->mpPostIt->GetSizePixel(); + // Pixel width depends on DisplayWidthFactor, seems to not depend on DPI + CPPUNIT_ASSERT_EQUAL(tools::Long(400), aSize.Width()); + // Heights depend on width and current DPI scaling; just remember them for later comparison + aOriginalHeights.push_back(aSize.Height()); + } + + // Export to PDF with comments in margin + uno::Sequence aFilterData{ comphelper::makePropertyValue(u"ExportNotes"_ustr, false), + comphelper::makePropertyValue(u"ExportNotesInMargin"_ustr, true) }; + uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), + comphelper::makePropertyValue(u"URL"_ustr, maTempFile.GetURL()) }; + dispatchCommand(mxComponent, u".uno:ExportToPDF"_ustr, aDescriptor); + + if (auto pPdfDocument = parsePDFExport()) // This part will be skipped without PDFium + { + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + auto pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT(pPage); + // 595 pt corresponts to 210 mm (withh of A4) + CPPUNIT_ASSERT_DOUBLES_EQUAL(595, pPage->getWidth(), 1.0); + auto pTextPage = pPage->getTextPage(); + CPPUNIT_ASSERT(pTextPage); + + // There should be two filled path objects corresponding to the two annotations + int nPathCount = 0; + + // Check that there are all the expected lines of the annotations, i.e. that the layout + // of the comments is stable and independent of the DPI. + const OUString aLines[] = { + // First annotation + u"A comment. One two three four "_ustr, + u"five six seven eight nine ten "_ustr, + u"eleven twelve thirteen fourteen "_ustr, + u"fifteen sixteen seventeen "_ustr, + u"eighteen nineteen twenty."_ustr, + // Second annotation + u"Another comment. Twenty-one "_ustr, + u"twenty-two twenty-three "_ustr, + u"twenty-four twenty-five twenty-"_ustr, + u"six twenty-seven twenty-eight "_ustr, + u"twenty-nine thirty."_ustr, + }; + std::set<OUString> aFoundLines; + + for (int i = 0; i < pPage->getObjectCount(); ++i) + { + auto pObject = pPage->getObject(i); + + if (pObject->getType() == vcl::pdf::PDFPageObjectType::Path + && pObject->getPathSegmentCount() == 5) + { + // This is the filled rectangle of an annotation. I hope that path segment count + // is stable and unique enough to identify it. + CPPUNIT_ASSERT(pObject->getFillColor() != COL_TRANSPARENT); + basegfx::B2DRectangle bounds = pObject->getBounds(); + // The object must start at position 448, and have width 101 (so its right + // edge is ~at position 549, within the page's width of 595). + // Without the fix, this failed like "Expected: 101; Actual: 225", i.e. its + // right edge protruded beyond the right page limit (and generally depended + // on the UI width of the comment sidebar). + CPPUNIT_ASSERT_DOUBLES_EQUAL(101, bounds.getWidth(), 1.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(448, bounds.getMinX(), 1.0); + ++nPathCount; + } + else if (pObject->getType() == vcl::pdf::PDFPageObjectType::Text) + { + // This is text; check if it matches expected annotation lines + OUString aText = pObject->getText(pTextPage); + if (std::ranges::find(aLines, aText) != std::end(aLines)) + { + // Check that it fits into the expected horizontal range of the comment + basegfx::B2DRectangle bounds = pObject->getBounds(); + CPPUNIT_ASSERT_LESS(101.0, bounds.getWidth()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(450, bounds.getMinX(), 1.0); + + CPPUNIT_ASSERT(!aFoundLines.contains(aText)); // each line only once + aFoundLines.insert(aText); + } + } + } + CPPUNIT_ASSERT_EQUAL(2, nPathCount); // all annotation rectangles + CPPUNIT_ASSERT_EQUAL(std::size(aLines), aFoundLines.size()); // all annotation lines + } + + // Test that export didn't change the annotation sizes + CPPUNIT_ASSERT_EQUAL(size_t(2), pPostItMgr->GetPostItFields().size()); + for (size_t i = 0; i < 2; ++i) + { + const auto& pAnnotationItem = pPostItMgr->GetPostItFields()[i]; + CPPUNIT_ASSERT(pAnnotationItem); + CPPUNIT_ASSERT(pAnnotationItem->mpPostIt); + const Size aSize = pAnnotationItem->mpPostIt->GetSizePixel(); + CPPUNIT_ASSERT_EQUAL(tools::Long(400), aSize.Width()); + CPPUNIT_ASSERT_EQUAL(aOriginalHeights[i], aSize.Height()); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/uiwriter/uiwriter8.cxx b/sw/qa/extras/uiwriter/uiwriter8.cxx index aebb38aa28b0..4fd3027301c8 100644 --- a/sw/qa/extras/uiwriter/uiwriter8.cxx +++ b/sw/qa/extras/uiwriter/uiwriter8.cxx @@ -831,7 +831,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf152575) std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/1); CPPUNIT_ASSERT(pPdfPage); // Without the fix for tdf#152575 this would be only 42 objects - CPPUNIT_ASSERT_EQUAL(50, pPdfPage->getObjectCount()); + CPPUNIT_ASSERT_EQUAL(51, pPdfPage->getObjectCount()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf140731) diff --git a/sw/source/core/view/vprint.cxx b/sw/source/core/view/vprint.cxx index 337e6e3fbd37..95e6c3648f1f 100644 --- a/sw/source/core/view/vprint.cxx +++ b/sw/source/core/view/vprint.cxx @@ -520,12 +520,18 @@ bool SwViewShell::PrintOrPDFExport( if (pPostItManager) { - pPostItManager->SetForceShow(true); + pPostItManager->SetForceShowForPrintOrPdf(true); pPostItManager->CalcRects(); pPostItManager->LayoutPostIts(); pPostItManager->DrawNotesForPage(pOutDev, nPage-1); oOrigHeight.emplace(pStPage->getFrameArea().Height()); - pPostItManager->SetForceShow(false); + pPostItManager->SetForceShowForPrintOrPdf(false); + if (pPostItManager->ShowNotes()) + { + // In Print/Pdf mode, LayoutPostIts used a fixed sidebar width. Now restore the + // layout with the normal (user-defined) sidebar width. + pPostItManager->LayoutPostIts(); + } } } diff --git a/sw/source/uibase/docvw/AnnotationWin2.cxx b/sw/source/uibase/docvw/AnnotationWin2.cxx index 1b0137a4e01d..f665c6e9f22a 100644 --- a/sw/source/uibase/docvw/AnnotationWin2.cxx +++ b/sw/source/uibase/docvw/AnnotationWin2.cxx @@ -1196,6 +1196,20 @@ tools::Long SwAnnotationWin::GetPostItTextHeight() return mpOutliner ? LogicToPixel(mpOutliner->CalcTextSize()).Height() : 0; } +// Provides an estimation for the text height given a specific width of the annotation window. +// It doesn't calculate it accurately, e.g. it doesn't take scrollbars into account (see +// SwAnnotationWin::DoResize for more details of accurate calculation). Even though it avoids +// some calculations, it may still be quite expensive for large text. +tools::Long SwAnnotationWin::GuessTextHeightForWidth(tools::Long nWidth) const +{ + if (!mpOutliner) + return 0; + comphelper::ScopeGuard resetPaperSize([this, curSize = mpOutliner->GetPaperSize()]() + { mpOutliner->SetPaperSize(curSize); }); + mpOutliner->SetPaperSize(PixelToLogic(Size(nWidth, SAL_MAX_INT32))); + return LogicToPixel(mpOutliner->CalcTextSize()).Height(); +} + void SwAnnotationWin::SwitchToPostIt(sal_uInt16 aDirection) { SwAnnotationWin* pPostIt = mrMgr.GetNextPostIt(aDirection, this); diff --git a/sw/source/uibase/docvw/PostItMgr.cxx b/sw/source/uibase/docvw/PostItMgr.cxx index f5edf6fb607e..e201a5384634 100644 --- a/sw/source/uibase/docvw/PostItMgr.cxx +++ b/sw/source/uibase/docvw/PostItMgr.cxx @@ -981,6 +981,7 @@ void SwPostItMgr::LayoutPostIts() // - place SwPostIts on their initial position // - calculate necessary height for all PostIts together bool bUpdate = false; + const tools::Long nSidebarWidth = GetSidebarWidth(true); for (std::unique_ptr<SwPostItPageItem>& pPage : mPages) { // only layout if there are notes on this page @@ -1019,7 +1020,7 @@ void SwPostItMgr::LayoutPostIts() if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT ) { // x value for notes positioning - mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true); + mlPageBorder = mpEditWin->LogicToPixel(Point(pPage->mPageRect.Left(), 0)).X() - nSidebarWidth;// - GetSidebarBorderWidth(true); //bending point mlPageEnd = mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) @@ -1039,13 +1040,18 @@ void SwPostItMgr::LayoutPostIts() tools::Long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y(); + // Without taking new width into account, the text height will be wrong. + // GuessTextHeightForWidth is expensive, only use it when necessary. + tools::Long nTextHeight; + if (pPostIt->GetSizePixel().Width() == nSidebarWidth) + nTextHeight = pPostIt->GetPostItTextHeight(); + else + nTextHeight = pPostIt->GuessTextHeightForWidth(nSidebarWidth); + tools::Long postItPixelTextHeight = (comphelper::LibreOfficeKit::isActive() - ? mpEditWin - ->LogicToPixel( - Point(0, pPostIt->GetPostItTextHeight())) - .Y() - : pPostIt->GetPostItTextHeight()); + ? mpEditWin->LogicToPixel(Point(0, nTextHeight)).Y() + : nTextHeight); aPostItHeight = (postItPixelTextHeight < pPostIt->GetMinimumSizeWithoutMeta() ? pPostIt->GetMinimumSizeWithoutMeta() @@ -1053,7 +1059,7 @@ void SwPostItMgr::LayoutPostIts() + pPostIt->GetMetaHeight(); pPostIt->SetPosSizePixelRect( mlPageBorder , Y - GetInitialAnchorDistance(), - GetSidebarWidth(true), + nSidebarWidth, aPostItHeight, mlPageEnd ); } @@ -2418,7 +2424,7 @@ void SwPostItMgr::CorrectPositions() bool SwPostItMgr::ShowNotes() const { // we only want to see notes if Options - Writer - View - Notes is ticked - return mbForceShow || mpWrtShell->GetViewOptions()->IsPostIts(); + return mbForceShowForPrintOrPdf || mpWrtShell->GetViewOptions()->IsPostIts(); } bool SwPostItMgr::HasNotes() const @@ -2472,8 +2478,18 @@ tools::ULong SwPostItMgr::GetSidebarWidth(bool bPx) const double fScaleX = double(mpWrtShell->GetOut()->GetMapMode().GetScaleX()); nZoom = fScaleX * 100; } - tools::ULong aWidth = static_cast<tools::ULong>( - nZoom * officecfg::Office::Writer::Notes::DisplayWidthFactor::get()); + double fDisplayWidthFactor; + if (mbForceShowForPrintOrPdf) + { + // 1.8 is the fixed factor that empirically matches (at 96 PPI) the 0.75 scale applied to + // the page in SwViewShell::PrintOrPDFExport for "print in margins" mode. + fDisplayWidthFactor = 1.8 * (Application::GetDefaultDevice()->GetDPIX() / 96.0); + } + else + { + fDisplayWidthFactor = officecfg::Office::Writer::Notes::DisplayWidthFactor::get(); + } + tools::ULong aWidth = static_cast<tools::ULong>(nZoom * fDisplayWidthFactor); if (bPx) return aWidth; commit 278ffb178b9406913df17e1e7904f5a01ed8f222 Author: Miklos Vajna <[email protected]> AuthorDate: Fri Jan 30 08:26:30 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 tdf#170466 sd UI, bullet library: avoid unwanted defaults Load an Impress document, add a textbox, use the bullet library toolbar button to have a popup with previews, select the "white bullet" option, "black cricle" gets used. This went wrong in commit fe9fab9702613b1a5d192821d8a620aa527234b7 (Related: tdf#89365 sd UI, from numbering to bullet: fix defaults, 2025-12-18), where the old use-case was to just toggle from no numbering to bullet, without specifying any item from the bullet library, where the expectation was to connect this paragraph to the outermost outline style. Fix the new use-case by not setting bullet properties if a specific item is selected from the bullet library. This way the new use-case again takes bullet properties from the bullet library and the old use-case keeps setting the defaults to have a correct bullet size. Change-Id: I77931a48e604f177deff4976336783bcff8a7e8a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198398 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 49a49acf65f5d77383551217abc39c06dbd62909) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198424 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sd/qa/ui/func/func.cxx b/sd/qa/ui/func/func.cxx index f6df8fcc8857..6ae13f1baa1f 100644 --- a/sd/qa/ui/func/func.cxx +++ b/sd/qa/ui/func/func.cxx @@ -13,6 +13,7 @@ #include <com/sun/star/drawing/XDrawPage.hpp> #include <comphelper/sequenceashashmap.hxx> +#include <comphelper/propertyvalue.hxx> #include <vcl/scheduler.hxx> #include <DrawDocShell.hxx> @@ -81,6 +82,52 @@ CPPUNIT_TEST_FIXTURE(Test, testNoneToBullet) aNumberingRule[u"FirstLineOffset"_ustr] >>= nFirstLineOffset; CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-900), nFirstLineOffset); } + +CPPUNIT_TEST_FIXTURE(Test, testNoneToLibraryBullet) +{ + // Given a document with a shape, the only paragraph has a numbering of type "none": + createSdImpressDoc("odp/none-to-bullet.odp"); + sd::ViewShell* pViewShell = getSdDocShell()->GetViewShell(); + SdPage* pPage = pViewShell->GetActualPage(); + SdrObject* pShape = pPage->GetObj(0); + CPPUNIT_ASSERT(pShape); + SdrView* pView = pViewShell->GetView(); + pView->MarkObj(pShape, pView->GetSdrPageView()); + Scheduler::ProcessEventsToIdle(); + CPPUNIT_ASSERT(!pView->IsTextEdit()); + + // When turning the "none" numbering to a bullet from the library: + // Start text edit: + auto pImpressDocument = dynamic_cast<SdXImpressDocument*>(mxComponent.get()); + typeString(pImpressDocument, u"x"); + CPPUNIT_ASSERT(pView->IsTextEdit()); + // Do the switch, taking the second option from the list: + uno::Sequence<beans::PropertyValue> aArgs + = { comphelper::makePropertyValue("BulletIndex", static_cast<sal_uInt16>(2)) }; + dispatchCommand(mxComponent, u".uno:SetBullet"_ustr, aArgs); + // End text edit: + typeKey(pImpressDocument, KEY_ESCAPE); + + // Then make sure we switch to a bullet with the correct character: + CPPUNIT_ASSERT(!pView->IsTextEdit()); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xParagraph(getParagraphFromShape(0, xShape), + uno::UNO_QUERY); + // Check the list level 1 properties: + uno::Reference<container::XIndexAccess> xNumberingRules; + xParagraph->getPropertyValue(u"NumberingRules"_ustr) >>= xNumberingRules; + comphelper::SequenceAsHashMap aNumberingRule(xNumberingRules->getByIndex(0)); + OUString aBulletChar; + aNumberingRule[u"BulletChar"_ustr] >>= aBulletChar; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: ○ (white bullet) + // - Actual : ● (black circle) + // i.e. the bullet char was the default, not the selected one. + CPPUNIT_ASSERT_EQUAL(u"\u25CB"_ustr, aBulletChar); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sd/source/ui/func/fuolbull.cxx b/sd/source/ui/func/fuolbull.cxx index ecb7c5377a1b..246176da700c 100644 --- a/sd/source/ui/func/fuolbull.cxx +++ b/sd/source/ui/func/fuolbull.cxx @@ -207,7 +207,7 @@ void FuBulletAndPosition::SetCurrentBulletsNumbering(SfxRequest& rReq) if(nActNumLvl & nMask) { SvxNumberFormat aFmt(aTmpRule.GetLevel(i)); - if (nSId == FN_SVX_SET_BULLET) + if (nSId == FN_SVX_SET_BULLET && bToggle) { // If changing to a bullet, then make its format and indent has a good // default, similar to what the master page offers: commit 9cef022aa19788f738ffd69fdf6cdc4a541da598 Author: Samuel Mehrbrodt <[email protected]> AuthorDate: Mon Jan 26 14:04:42 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 tdf#165732 Fix misaligned text when text box is too small Change-Id: Iaabc61c545d579edd02cdcb5685e8417a387b99c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198147 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> (cherry picked from commit c0693ef135f4654d69451fc9ca5a735931aebde2) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198159 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 71b818d1d11d..b0237633303f 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -143,8 +143,16 @@ basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText, const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR, rSnapRange.getMaxY() - fDistanceForTextB); basegfx::B2DRange aAnchorRange; - aAnchorRange.expand(aTopLeft); - aAnchorRange.expand(aBottomRight); + + // tdf#165732 Margins are too large in some cases (left > right or top > bottom) + // - in this case do not expand the range + bool bInvalidMargins + = (aTopLeft.getX() >= aBottomRight.getX() || aTopLeft.getY() >= aBottomRight.getY()); + if (!bInvalidMargins) + { + aAnchorRange.expand(aTopLeft); + aAnchorRange.expand(aBottomRight); + } // If the shape has no width, then don't attempt to break the text into multiple // lines, not a single character would satisfy a zero width requirement. commit 5ad16ee737801f0eec9d8ef4e79a226dbd769458 Author: Miklos Vajna <[email protected]> AuthorDate: Thu Jan 29 09:54:57 2026 +0100 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 sfx2: fix crash in SfxObjectShell::PostActivateEvent_Impl() gdb backtrace on the core file from the crashreport: #0 0x00007078323c6c4e in std::__shared_ptr<SfxItemSet, (__gnu_cxx::_Lock_policy)2>::operator bool (this=<optimized out>) at /opt/rh/devtoolset-12/root/usr/include/c++/12/bits/shared_ptr_base.h:1670 #1 SfxMedium::GetItemSet (this=0x0) at sfx2/source/doc/docfile.cxx:3840 #2 0x0000707832428792 in SfxObjectShell::PostActivateEvent_Impl (this=0x3e357de0, pFrame=pFrame@entry=0x427b7460) at sfx2/source/doc/objmisc.cxx:933 #3 0x0000707832214661 in SfxApplication::SetViewFrame_Impl (this=0x3c0a39d0, pFrame=pFrame@entry=0x427b7460) at sfx2/source/appl/app.cxx:273 #4 0x000070783254804f in SfxViewFrame::SetViewFrame (pFrame=0x427b7460) at sfx2/source/view/viewfrm.cxx:3735 and: #2 0x0000707832428792 in SfxObjectShell::PostActivateEvent_Impl (this=0x3e357de0, pFrame=pFrame@entry=0x427b7460) at sfx2/source/doc/objmisc.cxx:933 933 const SfxBoolItem* pHiddenItem = pMedium->GetItemSet().GetItem(SID_HIDDEN, false); (gdb) print pMedium $1 = (SfxMedium *) 0x0 Assume that no medium means the same as medium having no hiddem item. Change-Id: I6295cfd90d2a3d529fa5e915983578e6768a2244 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198357 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 01d66be56971875a690f3e698093492e39485efa) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198402 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sfx2/source/doc/objmisc.cxx b/sfx2/source/doc/objmisc.cxx index ca56f3b25bee..2b7762e75ce2 100644 --- a/sfx2/source/doc/objmisc.cxx +++ b/sfx2/source/doc/objmisc.cxx @@ -931,7 +931,7 @@ void SfxObjectShell::PostActivateEvent_Impl( SfxViewFrame const * pFrame ) if ( pSfxApp->IsDowning() || IsLoading() || !pFrame || pFrame->GetFrame().IsClosing_Impl() ) return; - const SfxBoolItem* pHiddenItem = pMedium->GetItemSet().GetItem(SID_HIDDEN, false); + const SfxBoolItem* pHiddenItem = pMedium ? pMedium->GetItemSet().GetItem(SID_HIDDEN, false) : nullptr; if ( !pHiddenItem || !pHiddenItem->GetValue() ) { SfxEventHintId nId = pImpl->nEventId; commit cc91b989da909733b208fcf4dca822382b8846f1 Author: Noel Grandin <[email protected]> AuthorDate: Thu Jan 15 11:21:32 2026 +0200 Commit: Andras Timar <[email protected]> CommitDate: Sun Feb 1 12:20:44 2026 +0100 officeotron: duplicate <w:bidi/> elements we end up with: <w:document .. <w:bidi/> <w:bidi/> <w:docGrid w:type="default" w:linePitch="360" w:charSpace="0"/> </w:sectPr> </w:body> </w:document> Change-Id: Ie3a8cc5f02c40b5f4df7cb2efd43b057210fd4ad Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197423 Reviewed-by: Noel Grandin <[email protected]> Tested-by: Jenkins (cherry picked from commit a971b4c2ac8197e27ee30a21c02893fecb6fb989) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197553 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx index aa92610d3f92..b25b420ab83f 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx @@ -657,9 +657,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf135343_columnSectionBreak_c14v2, "tdf135343_colu DECLARE_OOXMLEXPORT_TEST(testTdf135343_columnSectionBreak_c12v3, "tdf135343_columnSectionBreak_c12v3.docx") { - //FIXME: validation error in OOXML export: Errors: 1 - skipValidation(); - // In this Word 20-3 v3, section one and two have different number of columns. It acts like a page break. uno::Reference<beans::XPropertySet> xTextSection = getProperty<uno::Reference<beans::XPropertySet>>(getParagraph(1, u"Four columns,"_ustr), u"TextSection"_ustr); uno::Reference<text::XTextColumns> xTextColumns = getProperty<uno::Reference<text::XTextColumns>>(xTextSection, u"TextColumns"_ustr); @@ -673,9 +670,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf135343_columnSectionBreak_c12v3, "tdf135343_colu DECLARE_OOXMLEXPORT_TEST(testTdf135343_columnSectionBreak_c15, "tdf135343_columnSectionBreak_c15.docx") { - //FIXME: validation error in OOXML export: Errors: 1 - skipValidation(); - // Word 2013+ version - nextColumn breaks inside column sections are always handled like nextPage breaks. uno::Reference<beans::XPropertySet> xTextSection = getProperty<uno::Reference<beans::XPropertySet>>(getParagraph(12, u"RTL 2"_ustr), u"TextSection"_ustr); uno::Reference<text::XTextColumns> xTextColumns = getProperty<uno::Reference<text::XTextColumns>>(xTextSection, u"TextColumns"_ustr); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 7c8aa610309b..b349f53fa821 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -10457,7 +10457,7 @@ void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDi if ( m_rExport.m_bOutPageDescs ) { m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), sTextFlow); - if ( bBiDi ) + if ( bBiDi && !m_rExport.m_bSuppressBidi ) m_pSerializer->singleElementNS(XML_w, XML_bidi); } else if ( !m_rExport.m_bOutFlyFrameAttrs ) diff --git a/sw/source/filter/ww8/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx index 2986f84f432f..bb3b9cc51059 100644 --- a/sw/source/filter/ww8/wrtw8sty.cxx +++ b/sw/source/filter/ww8/wrtw8sty.cxx @@ -1763,14 +1763,17 @@ void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAtt const SfxItemSet* pOldI = m_pISet; m_pISet = &aSet; + const bool bBiDi = SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ); + m_bSuppressBidi = bBiDi; // prevent duplicate <w:bidi/> elements // Switch off test on default item values, if page description // set (value of <bOutPgDscSet>) isn't written. AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet ); bOutputStyleItemSet = true; + m_bSuppressBidi = false; //Cannot export as normal page framedir, as continuous sections //cannot contain any grid settings like proper sections - AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) ); + AttrOutput().SectionBiDi( bBiDi ); m_pISet = pOldI; } diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 766f53434c1e..b960f625aa4f 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -3980,6 +3980,7 @@ MSWordExportBase::MSWordExportBase( SwDoc& rDocument, std::shared_ptr<SwUnoCurso , m_bHasFtr(false) , m_bSubstituteBullets(true) , m_bTabInTOC(false) + , m_bSuppressBidi(false) , m_bHideTabLeaderAndPageNumbers(false) , m_bExportModeRTF(false) , m_bFontSizeWritten(false) diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index 5aa6c208235b..a9991dfc6dda 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -560,6 +560,7 @@ public: bool m_bHasFtr : 1; bool m_bSubstituteBullets : 1; // true: SubstituteBullet() gets called bool m_bTabInTOC : 1; //true for TOC field flag 'w' + bool m_bSuppressBidi : 1; // prevent duplicate bidi elements bool m_bHideTabLeaderAndPageNumbers : 1 ; // true: the 'z' field of TOC is set. bool m_bExportModeRTF;
