From 5b36e890ec32584c6c5c70347f8a54cc9bb67f12 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Sep 2026 12:27:56 +0200 Subject: [PATCH] tdf#173649 sw: don't wrap docx text into a narrow gap beside a shape Writer laid text into a 0.2 cm gap left of a text box, where Word leaves the gap empty. Word gives up a gap under a quarter inch wide, measured from the column edge to the object's wrap boundary, when the other side has at least that much room. Follow the same rule for documents the Word filters read. Change-Id: Ifdacbf0469296ade1665fb0b56526301cd19f973 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211280 Reviewed-by: Samuel Mehrbrodt Tested-by: Jenkins --- sw/inc/IDocumentSettingAccess.hxx | 1 + sw/qa/core/text/data/min-wrap-gap.docx | Bin 0 -> 1504 bytes sw/qa/core/text/txtfly.cxx | 19 +++++++++++++++ sw/source/core/doc/DocumentSettingManager.cxx | 12 +++++++++ sw/source/core/inc/DocumentSettingManager.hxx | 1 + sw/source/core/text/txtfly.cxx | 23 ++++++++++++++++++ sw/source/filter/ww8/ww8par.cxx | 2 ++ sw/source/uibase/uno/SwXDocumentSettings.cxx | 13 ++++++++++ .../writerfilter/dmapper/SettingsTable.cxx | 1 + 9 files changed, 72 insertions(+) create mode 100644 sw/qa/core/text/data/min-wrap-gap.docx diff --git a/sw/inc/IDocumentSettingAccess.hxx b/sw/inc/IDocumentSettingAccess.hxx index 407f0ec43c14..8e99f7be4d59 100644 --- a/sw/inc/IDocumentSettingAccess.hxx +++ b/sw/inc/IDocumentSettingAccess.hxx @@ -71,6 +71,7 @@ enum class DocumentSettingId MS_WORD_COMP_TRAILING_BLANKS, // tdf#128197 MS Word in some modes can have line height based on shape height, not on font MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, + MS_WORD_COMP_MIN_TEXT_WRAP_GAP, UNIX_FORCE_ZERO_EXT_LEADING, TABS_RELATIVE_TO_INDENT, PROTECT_FORM, diff --git a/sw/qa/core/text/data/min-wrap-gap.docx b/sw/qa/core/text/data/min-wrap-gap.docx new file mode 100644 index 0000000000000000000000000000000000000000..792f822df50b56cee0efc9a62760166308151a99 GIT binary patch literal 1504 zcmWIWW@Zs#U|`^2`0QpHd#3sF#y3FTW<~}EVIUptoS#>cnpYAZQdy8%9IIE6n^PNb z(vR7YXYY4yrTB|JYesfV`w|3Z{qs4R zHT#TN$CkhMpC4M9DKTf^=DM=es**|5t~KYM_izwDFaIL>>FT560VsjPeAT;U129l@ zfq}yfq~nWHbBgu#KxFUvy?jju0t^r4F3-rD-qdW^b|}dwg4fRBvfR>%|CX$^-?LX% zW{QQyz5C_mzTZuZcP4k-iexK|WOUS?b0y4h2hZ}eavnZ+yzH#IdBl1yY;`R@Hgma+ zu=-o&lO4T0%clHkyriLKVl(aB;Opb-G}Jf$wgE8C!zFfY-*ma=YDa9s+e>^et|h#z z3eUMIrWQSG$&r(Fe>F}P-r6v67r{p_X$ig zOVDmv)yWt-b%&_dbgz_<>>Uf9Rfx0&#|U3rz~{D?)lG!;^ov~g1I-$zIvW*AjxMoV zwDps2!A_;O4d=WkZNFFHqVk?W=Gb+wztT=;ja0X2oN-lqrDt)Rsn7kPw%zM4kFFS2 zGbMWq$7N-an!k_FvdKvHC=mxtrlIm+TU%V$+j zlsR&(CM!?lc|$kTp`97>4^4}d*0!hLh!=TtcVlzx#_gU9XRiHK>z}aFW$pr>54m?2 zZ24TN!{zHMbvEs@K}!jbdH<^8m7Kr-aD@rEdby^2mHN4U!FjnU?|O5m^DLSX=jSy0 z@S6*#V;#0=WTe?ug}-}bYIiX2E>BW^aEC_oHT>H2Zv(sFf6KYtcVxF|1Z5VxyUIo=I&u~D=rG;1&3IhfuXk?0 zp!czz{w(=S5_#d1FCLM-*tGdh)Z9g<&hmyI?7I=QGrs)m0p4hyhC|H<)@1y??%w0S z^ML-PxqKz>ubZw3{^hrA)2G^h^BNO^lD40zZ(h9c-#(s=I@j$Pmv3!;t2IYyg7$>v zpHJ`17x?hQMGetFrameFormat(); @@ -1553,6 +1557,25 @@ css::text::WrapTextMode SwTextFly::GetSurroundForTextWrap( const SwAnchoredObjec } } + // Ask the format rather than eSurroundForTextWrap, because an optimal wrap + // which the branch above turned into a parallel one measures its sides + // against the paragraph and keeps that result. + if (css::text::WrapTextMode_PARALLEL == rFlyFormat.GetSurround() + && GetMaster()->GetDoc().getIDocumentSettingAccess().get( + DocumentSettingId::MS_WORD_COMP_MIN_TEXT_WRAP_GAP)) + { + SwRectFnSet aRectFnSet(*m_pCurrFrame); + const SwRect& rObj(pAnchoredObj->GetObjRectWithSpaces()); + const SwRect& rCol(m_pCurrFrame->getFrameArea()); + const tools::Long nLeft = aRectFnSet.GetLeft(rObj) - aRectFnSet.GetLeft(rCol); + const tools::Long nRight = aRectFnSet.GetRight(rCol) - aRectFnSet.GetRight(rObj); + // If neither side has enough space, keep the wrap mode as PARALLEL + if (nLeft < WORD_TEXT_MIN && nRight >= WORD_TEXT_MIN) + eSurroundForTextWrap = css::text::WrapTextMode_RIGHT; + else if (nRight < WORD_TEXT_MIN && nLeft >= WORD_TEXT_MIN) + eSurroundForTextWrap = css::text::WrapTextMode_LEFT; + } + return eSurroundForTextWrap; } diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx index 7d5ba5770f58..4eca952761c5 100644 --- a/sw/source/filter/ww8/ww8par.cxx +++ b/sw/source/filter/ww8/ww8par.cxx @@ -1954,6 +1954,8 @@ void SwWW8ImplReader::ImportDop() DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true); m_rDoc.getIDocumentSettingAccess().set( DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true); + m_rDoc.getIDocumentSettingAccess().set( + DocumentSettingId::MS_WORD_COMP_MIN_TEXT_WRAP_GAP, true); // tdf#128195 m_rDoc.getIDocumentSettingAccess().set( DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true); diff --git a/sw/source/uibase/uno/SwXDocumentSettings.cxx b/sw/source/uibase/uno/SwXDocumentSettings.cxx index 763750463f0d..05c76612b1a7 100644 --- a/sw/source/uibase/uno/SwXDocumentSettings.cxx +++ b/sw/source/uibase/uno/SwXDocumentSettings.cxx @@ -114,6 +114,7 @@ enum SwDocumentSettingsPropertyHandles HANDLE_USE_OLD_PRINTER_METRICS, HANDLE_PROTECT_FORM, HANDLE_MS_WORD_COMP_TRAILING_BLANKS, + HANDLE_MS_WORD_COMP_MIN_TEXT_WRAP_GAP, HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, HANDLE_TABS_RELATIVE_TO_INDENT, HANDLE_RSID, @@ -237,6 +238,7 @@ static rtl::Reference lcl_createSettingsInfo() { u"RsidRoot"_ustr, HANDLE_RSID_ROOT, cppu::UnoType::get(), 0}, { u"ProtectForm"_ustr, HANDLE_PROTECT_FORM, cppu::UnoType::get(), 0}, { u"MsWordCompTrailingBlanks"_ustr, HANDLE_MS_WORD_COMP_TRAILING_BLANKS, cppu::UnoType::get(), 0 }, + { u"MsWordCompMinTextWrapGap"_ustr, HANDLE_MS_WORD_COMP_MIN_TEXT_WRAP_GAP, cppu::UnoType::get(), 0 }, { u"MsWordCompMinLineHeightByFly"_ustr, HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, cppu::UnoType::get(), 0 }, { u"TabAtLeftIndentForParagraphsInList"_ustr, HANDLE_TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST, cppu::UnoType::get(), 0}, { u"ModifyPasswordInfo"_ustr, HANDLE_MODIFYPASSWORDINFO, cppu::UnoType< cppu::UnoSequenceType >::get(), 0}, @@ -825,6 +827,12 @@ void SwXDocumentSettings::_setSingleValue( const comphelper::PropertyInfo & rInf mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, bTmp); } break; + case HANDLE_MS_WORD_COMP_MIN_TEXT_WRAP_GAP: + { + bool bTmp = *o3tl::doAccess(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::MS_WORD_COMP_MIN_TEXT_WRAP_GAP, bTmp); + } + break; case HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: { bool bTmp = *o3tl::doAccess(rValue); @@ -1618,6 +1626,11 @@ void SwXDocumentSettings::_getSingleValue( const comphelper::PropertyInfo & rInf rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS); } break; + case HANDLE_MS_WORD_COMP_MIN_TEXT_WRAP_GAP: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_MIN_TEXT_WRAP_GAP); + } + break; case HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: { rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY); diff --git a/sw/source/writerfilter/dmapper/SettingsTable.cxx b/sw/source/writerfilter/dmapper/SettingsTable.cxx index 157b78ea320e..23122b42fe02 100644 --- a/sw/source/writerfilter/dmapper/SettingsTable.cxx +++ b/sw/source/writerfilter/dmapper/SettingsTable.cxx @@ -675,6 +675,7 @@ void SettingsTable::ApplyProperties(rtl::Reference const& xDoc) // Shared between DOCX and RTF, unconditional flags. xDocumentSettings->setPropertyValue(u"TableRowKeep"_ustr, uno::Any(true)); + xDocumentSettings->setPropertyValue(u"MsWordCompMinTextWrapGap"_ustr, uno::Any(true)); xDocumentSettings->setPropertyValue(u"AddVerticalFrameOffsets"_ustr, uno::Any(true)); xDocumentSettings->setPropertyValue(u"ApplyParagraphMarkFormatToEmptyLineAtEndOfParagraph"_ustr, uno::Any(true));