cui/source/inc/paragrph.hxx | 1 cui/source/tabpages/paragrph.cxx | 23 + cui/uiconfig/ui/paragalignpage.ui | 60 ++-- editeng/source/editeng/editdoc.cxx | 16 + editeng/source/editeng/eerdll.cxx | 2 editeng/source/editeng/impedit.hxx | 1 editeng/source/editeng/impedit2.cxx | 59 ++++ editeng/source/items/paraitem.cxx | 20 + i18nutil/CppunitTest_i18nutil.mk | 1 i18nutil/Library_i18nutil.mk | 1 i18nutil/qa/cppunit/test_guessparadirection.cxx | 75 +++++ i18nutil/source/utility/guessparadirection.cxx | 65 ++++ include/editeng/autodiritem.hxx | 37 ++ include/editeng/editids.hrc | 1 include/editeng/editrids.hrc | 2 include/editeng/eeitem.hxx | 4 include/editeng/unotext.hxx | 3 include/i18nutil/guessparadirection.hxx | 30 ++ include/svl/poolitem.hxx | 1 include/xmloff/xmltoken.hxx | 1 sw/inc/editsh.hxx | 2 sw/inc/hintids.hxx | 11 sw/inc/inspectorproperties.hrc | 1 sw/inc/paratr.hxx | 3 sw/inc/swatrset.hxx | 2 sw/inc/unoprnms.hxx | 1 sw/qa/extras/odfexport/data/tdf162120-style-writing-mode-automatic.fodt | 147 ++++++++++ sw/qa/extras/odfexport/odfexport4.cxx | 13 sw/qa/extras/uiwriter/data/tdf162120-auto-rtl.fodt | 122 ++++++++ sw/qa/extras/uiwriter/uiwriter11.cxx | 23 + sw/qa/uitest/styleInspector/styleInspector.py | 20 - sw/qa/uitest/styleInspector/tdf137513.py | 2 sw/source/core/bastyp/init.cxx | 2 sw/source/core/doc/dbgoutsw.cxx | 1 sw/source/core/edit/eddel.cxx | 3 sw/source/core/edit/editsh.cxx | 64 ++++ sw/source/core/unocore/unomapproperties.hxx | 4 sw/source/filter/html/css1atr.cxx | 1 sw/source/filter/html/htmlatr.cxx | 1 sw/source/uibase/shells/txtattr.cxx | 6 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx | 1 xmloff/inc/xmlprop.hxx | 1 xmloff/source/core/xmltoken.cxx | 1 xmloff/source/text/txtprmap.cxx | 3 xmloff/source/token/tokens.txt | 1 45 files changed, 804 insertions(+), 35 deletions(-)
New commits: commit 69e902d0855d236aa12ec3105d9947ab6cd1d6c7 Author: Jonathan Clark <[email protected]> AuthorDate: Thu Oct 30 04:07:13 2025 -0600 Commit: Jonathan Clark <[email protected]> CommitDate: Thu Nov 13 04:45:50 2025 +0100 tdf#162120 Implement style:writing-mode-automatic Implements the ODF 1.2 paragraph attribute style:writing-mode-automatic, and supporting runtime features. When set, this attribute allows programs to automatically recompute the paragraph writing mode when the contents of the paragraph have been edited. The ODF standard does not suggest any particular algorithm for automatically determining paragraph direction. This implementation uses the rules from Unicode UAX #9 3.3.1. Currently, LO will only automatically change the writing direction when new text is typed or deleted. Other known interesting cases are already tracked as separate enhancement requests. Change-Id: I49eecea36952588dc98f2a98ebce712cd2846c0c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193547 Reviewed-by: Jonathan Clark <[email protected]> Tested-by: Jenkins diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx index ff6482526ed3..9d95e1adfa9b 100644 --- a/cui/source/inc/paragrph.hxx +++ b/cui/source/inc/paragrph.hxx @@ -151,6 +151,7 @@ class SvxParaAlignTabPage : public SfxTabPage std::unique_ptr<weld::Label> m_xVertAlignSdr; std::unique_ptr<svx::FrameDirectionListBox> m_xTextDirectionLB; + std::unique_ptr<weld::CheckButton> m_xAutoTextDirectionCB; /// word spacing std::unique_ptr<weld::Label> m_xLabelWordSpacing; diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx index 978c4811b8cf..427fb7356654 100644 --- a/cui/source/tabpages/paragrph.cxx +++ b/cui/source/tabpages/paragrph.cxx @@ -32,6 +32,7 @@ #include <svx/strings.hrc> #include <svx/dialmgr.hxx> #include <paragrph.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/frmdiritem.hxx> #include <editeng/lspcitem.hxx> #include <editeng/adjustitem.hxx> @@ -77,7 +78,8 @@ const WhichRangesContainer SvxParaAlignTabPage::pSdrAlignRanges( svl::Items< SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST, // 1076 SID_ATTR_PARA_ADJUST, SID_ATTR_PARA_ADJUST , // 10027 - SID_ATTR_FRAMEDIRECTION, SID_ATTR_FRAMEDIRECTION // 10944 + SID_ATTR_FRAMEDIRECTION, SID_ATTR_FRAMEDIRECTION, // 10944 + SID_ATTR_PARA_AUTOFRAMEDIRECTION, SID_ATTR_PARA_AUTOFRAMEDIRECTION >); const WhichRangesContainer SvxExtParagraphTabPage::pExtRanges(svl::Items< @@ -1282,6 +1284,7 @@ SvxParaAlignTabPage::SvxParaAlignTabPage(weld::Container* pPage, weld::DialogCon , m_xVertAlign(m_xBuilder->weld_label(u"labelFL_VERTALIGN"_ustr)) , m_xVertAlignSdr(m_xBuilder->weld_label(u"labelST_VERTALIGN_SDR"_ustr)) , m_xTextDirectionLB(new svx::FrameDirectionListBox(m_xBuilder->weld_combo_box(u"comboLB_TEXTDIRECTION"_ustr))) + , m_xAutoTextDirectionCB(m_xBuilder->weld_check_button(u"checkCB_AUTOTEXTDIRECTION"_ustr)) , m_xLabelWordSpacing(m_xBuilder->weld_label(u"labelWordSpacing"_ustr)) , m_xLabelMinimum(m_xBuilder->weld_label(u"labelMinimum"_ustr)) , m_xLabelDesired(m_xBuilder->weld_label(u"labelDesired"_ustr)) @@ -1458,6 +1461,13 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet ) } } + if (m_xAutoTextDirectionCB->get_state_changed_from_saved()) + { + rOutSet->Put(SvxAutoFrameDirectionItem(m_xAutoTextDirectionCB->get_active(), + GetWhich(SID_ATTR_PARA_AUTOFRAMEDIRECTION))); + bModified = true; + } + return bModified; } @@ -1618,6 +1628,16 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xTextDirectionLB->save_value(); } + _nWhich = GetWhich(SID_ATTR_PARA_AUTOFRAMEDIRECTION); + eItemState = rSet->GetItemState(_nWhich); + if (eItemState >= SfxItemState::DEFAULT) + { + const SvxAutoFrameDirectionItem& rSnap + = static_cast<const SvxAutoFrameDirectionItem&>(rSet->Get(_nWhich)); + m_xAutoTextDirectionCB->set_active(rSnap.GetValue()); + } + + m_xAutoTextDirectionCB->save_state(); m_xSnapToGridCB->save_state(); m_xVertAlignLB->save_value(); m_xLeft->save_state(); @@ -1642,6 +1662,7 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) void SvxParaAlignTabPage::ChangesApplied() { m_xTextDirectionLB->save_value(); + m_xAutoTextDirectionCB->save_state(); m_xSnapToGridCB->save_state(); m_xVertAlignLB->save_value(); m_xLeft->save_state(); diff --git a/cui/uiconfig/ui/paragalignpage.ui b/cui/uiconfig/ui/paragalignpage.ui index 46644dba15bf..4f0a095d9ef7 100644 --- a/cui/uiconfig/ui/paragalignpage.ui +++ b/cui/uiconfig/ui/paragalignpage.ui @@ -418,36 +418,62 @@ <property name="label-xalign">0</property> <property name="shadow-type">none</property> <child> - <object class="GtkBox" id="box3"> + <!-- n-columns=1 n-rows=2 --> + <object class="GtkGrid" id="grid3"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="margin-start">12</property> - <property name="margin-top">6</property> - <property name="spacing">6</property> <child> - <object class="GtkLabel" id="label2"> + <object class="GtkBox" id="box3"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="label" translatable="yes" context="paragalignpage|label2">_Text direction:</property> - <property name="use-underline">True</property> - <property name="mnemonic-widget">comboLB_TEXTDIRECTION</property> - <property name="xalign">0</property> + <property name="margin-start">12</property> + <property name="margin-top">6</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="paragalignpage|label2">_Text direction:</property> + <property name="use-underline">True</property> + <property name="mnemonic-widget">comboLB_TEXTDIRECTION</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="comboLB_TEXTDIRECTION"> + <property name="visible">True</property> + <property name="can-focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> + <property name="left-attach">0</property> + <property name="top-attach">0</property> </packing> </child> <child> - <object class="GtkComboBoxText" id="comboLB_TEXTDIRECTION"> + <object class="GtkCheckButton" id="checkCB_AUTOTEXTDIRECTION"> + <property name="label" translatable="yes" context="paragalignpage|checkCB_AUTOTEXTDIRECTION">Automatically set direction</property> <property name="visible">True</property> - <property name="can-focus">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="margin-start">22</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="left-attach">0</property> + <property name="top-attach">1</property> </packing> </child> </object> diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx index 05d7084a278b..581a22a88d30 100644 --- a/editeng/source/editeng/editdoc.cxx +++ b/editeng/source/editeng/editdoc.cxx @@ -19,6 +19,7 @@ #include <editdoc.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/tstpitem.hxx> #include <editeng/colritem.hxx> #include <editeng/fontitem.hxx> @@ -1031,6 +1032,14 @@ void YrsInsertAttribImplImpl(YrsWrite const& yw, SfxPoolItem const& rItm, attrName = "EE_PARA_WRITINGDIR"; break; } + case EE_PARA_AUTOWRITINGDIR: + { + SvxAutoFrameDirectionItem const& rItem{ static_cast<SvxAutoFrameDirectionItem const&>( + rItm) }; + attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE); + attrName = "EE_PARA_AUTOWRITINGDIR"; + break; + } case EE_PARA_HANGINGPUNCTUATION: { SvxHangingPunctuationItem const& rItem{static_cast<SvxHangingPunctuationItem const&>(rItm)}; @@ -2118,6 +2127,13 @@ void YrsImplInsertAttr(SfxItemSet & rSet, ::std::vector<sal_uInt16> *const pRemo rSet.Put(item); break; } + case EE_PARA_AUTOWRITINGDIR: + { + yvalidate(rValue.tag == Y_JSON_BOOL); + SvxAutoFrameDirectionitem const item{ rValue.value.flag == Y_TRUE, nWhich }; + rSet.Put(item); + break; + } case EE_PARA_HANGINGPUNCTUATION: { yvalidate(rValue.tag == Y_JSON_BOOL); diff --git a/editeng/source/editeng/eerdll.cxx b/editeng/source/editeng/eerdll.cxx index e828897160e1..bd154781d87b 100644 --- a/editeng/source/editeng/eerdll.cxx +++ b/editeng/source/editeng/eerdll.cxx @@ -40,6 +40,7 @@ #include <vcl/svapp.hxx> #include <vcl/virdev.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/autokernitem.hxx> #include <editeng/contouritem.hxx> #include <editeng/colritem.hxx> @@ -118,6 +119,7 @@ ItemInfoPackage& getItemInfoPackageEditEngine() { EE_PARA_TABS, new SvxTabStopItem( 0, 0, SvxTabAdjust::Left, EE_PARA_TABS ), SID_ATTR_TABSTOP, SFX_ITEMINFOFLAG_NONE }, { EE_PARA_JUST_METHOD, new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, EE_PARA_JUST_METHOD ), SID_ATTR_ALIGN_HOR_JUSTIFY_METHOD, SFX_ITEMINFOFLAG_NONE }, { EE_PARA_VER_JUST, new SvxVerJustifyItem( SvxCellVerJustify::Standard, EE_PARA_VER_JUST ), SID_ATTR_ALIGN_VER_JUSTIFY, SFX_ITEMINFOFLAG_NONE }, + { EE_PARA_AUTOWRITINGDIR, new SvxAutoFrameDirectionItem( false, EE_PARA_AUTOWRITINGDIR ), SID_ATTR_PARA_AUTOFRAMEDIRECTION, SFX_ITEMINFOFLAG_NONE }, { EE_CHAR_COLOR, new SvxColorItem( COL_AUTO, EE_CHAR_COLOR ), SID_ATTR_CHAR_COLOR, SFX_ITEMINFOFLAG_SUPPORT_SURROGATE }, // EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK and EE_CHAR_FONTINFO_CTL need on-demand initialization diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 72eadc1b79ac..aba08ab2f2ba 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -1044,6 +1044,7 @@ public: EditPaM InsertLineBreak(const EditSelection& aEditSelection); EditPaM InsertTab(const EditSelection& rEditSelection); EditPaM InsertField(const EditSelection& rCurSel, const SvxFieldItem& rFld); + void UpdateAutoParaDirection(const EditSelection& rCurSel); bool UpdateFields(); ErrCode Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs = nullptr ); diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index c72239dedc11..6d64243e1ffe 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -39,6 +39,7 @@ #include <editeng/ulspitem.hxx> #include <editeng/adjustitem.hxx> #include <editeng/frmdiritem.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/justifyitem.hxx> #include <editeng/scripthintitem.hxx> @@ -63,6 +64,7 @@ #include <svl/voiditem.hxx> #include <i18nutil/unicode.hxx> #include <i18nutil/scriptchangescanner.hxx> +#include <i18nutil/guessparadirection.hxx> #include <comphelper/diagnose_ex.hxx> #include <comphelper/flagguard.hxx> #include <comphelper/lok.hxx> @@ -466,6 +468,9 @@ bool ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView ) mpIMEInfos->nLen = pData->GetText().getLength(); } + // tdf#162120: Automatically adjust paragraph directions after edit + UpdateAutoParaDirection(EditSelection{ mpIMEInfos->aPos }); + ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() ); pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() ); FormatAndLayout( pView ); @@ -2532,6 +2537,10 @@ EditPaM ImpEditEngine::ImpDeleteSelection(const EditSelection& rCurSel) UpdateSelectionsDelete(deleted); UpdateSelections(); + + // tdf#162120: Automatically adjust paragraph directions after edit + UpdateAutoParaDirection(rCurSel); + TextModified(); return aStartPaM; } @@ -2763,6 +2772,9 @@ EditPaM ImpEditEngine::InsertTextUserInput( const EditSelection& rCurSel, aPaM.SetIndex( aPaM.GetIndex()+1 ); // does not do EditDoc-Method anymore } + // tdf#162120: Automatically adjust paragraph direction after edit + UpdateAutoParaDirection(EditSelection{ aPaM }); + TextModified(); if ( bUndoAction ) @@ -2771,6 +2783,53 @@ EditPaM ImpEditEngine::InsertTextUserInput( const EditSelection& rCurSel, return aPaM; } +void ImpEditEngine::UpdateAutoParaDirection(const EditSelection& rCurSel) +{ + EditPaM aPaM(rCurSel.Min()); + + auto nPara = maEditDoc.GetPos(aPaM.GetNode()); + if (nPara >= GetParaPortions().Count() || !HasParaAttrib(nPara, EE_PARA_AUTOWRITINGDIR)) + { + return; + } + + const SvxAutoFrameDirectionItem& rItem = GetParaAttrib(nPara, EE_PARA_AUTOWRITINGDIR); + if (!rItem.GetValue()) + { + return; + } + + bool bIsAlreadyRtl = IsRightToLeft(nPara); + + bool bShouldBeRtl = bIsAlreadyRtl; + switch (i18nutil::GuessParagraphDirection(aPaM.GetNode()->GetString())) + { + case i18nutil::ParagraphDirection::Ambiguous: + bShouldBeRtl = bIsAlreadyRtl; + break; + + case i18nutil::ParagraphDirection::LeftToRight: + bShouldBeRtl = false; + break; + + case i18nutil::ParagraphDirection::RightToLeft: + bShouldBeRtl = true; + break; + } + + if (bShouldBeRtl == bIsAlreadyRtl) + { + return; + } + + SvxFrameDirection eNeeded + = bShouldBeRtl ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; + + SfxItemSet aSet{ GetParaAttribs(nPara) }; + aSet.Put(SvxFrameDirectionItem{ eNeeded, EE_PARA_WRITINGDIR }); + SetParaAttribs(nPara, aSet); +} + EditPaM ImpEditEngine::ImpInsertText(const EditSelection& aCurSel, const OUString& rStr) { UndoActionStart( EDITUNDO_INSERT ); diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx index 9c16c3f78dca..f49b1f986717 100644 --- a/editeng/source/items/paraitem.cxx +++ b/editeng/source/items/paraitem.cxx @@ -43,6 +43,7 @@ #include <editeng/forbiddenruleitem.hxx> #include <editeng/paravertalignitem.hxx> #include <editeng/pgrditem.hxx> +#include <editeng/autodiritem.hxx> #include <rtl/ustring.hxx> #include <sal/log.hxx> #include <editeng/memberids.h> @@ -1514,5 +1515,24 @@ bool SvxParaGridItem::GetPresentation( return true; } +SvxAutoFrameDirectionItem::SvxAutoFrameDirectionItem(bool bValue, const sal_uInt16 nId) + : SfxBoolItem(nId, bValue) +{ +} + +SvxAutoFrameDirectionItem* SvxAutoFrameDirectionItem::Clone(SfxItemPool*) const +{ + return new SvxAutoFrameDirectionItem(*this); +} + +bool SvxAutoFrameDirectionItem::GetPresentation(SfxItemPresentation /*ePres*/, + MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, + OUString& rText, const IntlWrapper& /*rIntl*/) const +{ + rText = GetValue() ? EditResId(RID_SVXITEMS_AUTOFRAMEDIRECTION_ON) + : EditResId(RID_SVXITEMS_AUTOFRAMEDIRECTION_OFF); + + return true; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/i18nutil/CppunitTest_i18nutil.mk b/i18nutil/CppunitTest_i18nutil.mk index cdf79a2d2268..56cf5889f9ef 100644 --- a/i18nutil/CppunitTest_i18nutil.mk +++ b/i18nutil/CppunitTest_i18nutil.mk @@ -14,6 +14,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,i18nutil)) $(eval $(call gb_CppunitTest_use_sdk_api,i18nutil)) $(eval $(call gb_CppunitTest_add_exception_objects,i18nutil,\ + i18nutil/qa/cppunit/test_guessparadirection \ i18nutil/qa/cppunit/test_kashida \ i18nutil/qa/cppunit/test_scriptchangescanner \ )) diff --git a/i18nutil/Library_i18nutil.mk b/i18nutil/Library_i18nutil.mk index 8e68c3ba00c2..cf166a476401 100644 --- a/i18nutil/Library_i18nutil.mk +++ b/i18nutil/Library_i18nutil.mk @@ -44,6 +44,7 @@ $(eval $(call gb_Library_use_libraries,i18nutil,\ $(eval $(call gb_Library_add_exception_objects,i18nutil,\ i18nutil/source/utility/casefolding \ + i18nutil/source/utility/guessparadirection \ i18nutil/source/utility/kashida \ i18nutil/source/utility/oneToOneMapping \ i18nutil/source/utility/paper \ diff --git a/i18nutil/qa/cppunit/test_guessparadirection.cxx b/i18nutil/qa/cppunit/test_guessparadirection.cxx new file mode 100644 index 000000000000..1995fc18dbf4 --- /dev/null +++ b/i18nutil/qa/cppunit/test_guessparadirection.cxx @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/types.h> +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <i18nutil/guessparadirection.hxx> + +using namespace i18nutil; + +namespace +{ +class GuessParagraphDirectionTest : public CppUnit::TestFixture +{ +public: + void testEmpty(); + void testFirstStrongLTR(); + void testFirstStrongRTL(); + void testFirstStrongAR(); + void testIsolate(); + + CPPUNIT_TEST_SUITE(GuessParagraphDirectionTest); + CPPUNIT_TEST(testEmpty); + CPPUNIT_TEST(testFirstStrongLTR); + CPPUNIT_TEST(testFirstStrongRTL); + CPPUNIT_TEST(testFirstStrongAR); + CPPUNIT_TEST(testIsolate); + CPPUNIT_TEST_SUITE_END(); +}; + +void GuessParagraphDirectionTest::testEmpty() +{ + auto aText = u""_ustr; + CPPUNIT_ASSERT_EQUAL(ParagraphDirection::Ambiguous, GuessParagraphDirection(aText)); +} + +void GuessParagraphDirectionTest::testFirstStrongLTR() +{ + auto aText = u"..aاא.."_ustr; + CPPUNIT_ASSERT_EQUAL(ParagraphDirection::LeftToRight, GuessParagraphDirection(aText)); +} + +void GuessParagraphDirectionTest::testFirstStrongRTL() +{ + auto aText = u"..אaا.."_ustr; + CPPUNIT_ASSERT_EQUAL(ParagraphDirection::RightToLeft, GuessParagraphDirection(aText)); +} + +void GuessParagraphDirectionTest::testFirstStrongAR() +{ + auto aText = u"..اaא.."_ustr; + CPPUNIT_ASSERT_EQUAL(ParagraphDirection::RightToLeft, GuessParagraphDirection(aText)); +} + +void GuessParagraphDirectionTest::testIsolate() +{ + // U+2066: LRI + // U+2067: RLI + // U+2069: PDI + auto aText = u"...\u2069.\u2067aaa\u2066אאא\u2069.\u2069.."_ustr; + CPPUNIT_ASSERT_EQUAL(ParagraphDirection::Ambiguous, GuessParagraphDirection(aText)); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(GuessParagraphDirectionTest); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/i18nutil/source/utility/guessparadirection.cxx b/i18nutil/source/utility/guessparadirection.cxx new file mode 100644 index 000000000000..f882b2a6c928 --- /dev/null +++ b/i18nutil/source/utility/guessparadirection.cxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <i18nutil/guessparadirection.hxx> +#include <i18nutil/unicode.hxx> +#include <unicode/uchar.h> +#include <unicode/ubidi.h> + +i18nutil::ParagraphDirection i18nutil::GuessParagraphDirection(const OUString& rText) +{ + // tdf#162120: This algorithm implements Unicode UAX #9 3.3.1 + // for automatically determining the paragraph level without + // override from a higher-level protocol. + // The algorithm has been extended to explicitly communicate + // an ambiguous case, rather than defaulting to LTR. + + sal_Int32 nBase = 0; + + sal_Int32 nIsolateLevel = 0; + while (nBase < rText.getLength()) + { + auto nChar = rText.iterateCodePoints(&nBase); + auto nCharDir = u_charDirection(nChar); + + switch (nCharDir) + { + case U_POP_DIRECTIONAL_ISOLATE: + nIsolateLevel = std::max(sal_Int32{ 0 }, nIsolateLevel - 1); + break; + + case U_LEFT_TO_RIGHT_ISOLATE: + case U_RIGHT_TO_LEFT_ISOLATE: + ++nIsolateLevel; + break; + + case U_LEFT_TO_RIGHT: + if (nIsolateLevel == 0) + { + return ParagraphDirection::LeftToRight; + } + break; + + case U_RIGHT_TO_LEFT: + case U_RIGHT_TO_LEFT_ARABIC: + if (nIsolateLevel == 0) + { + return ParagraphDirection::RightToLeft; + } + break; + + default: + break; + } + } + + return ParagraphDirection::Ambiguous; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/editeng/autodiritem.hxx b/include/editeng/autodiritem.hxx new file mode 100644 index 000000000000..2b3cf3c4715a --- /dev/null +++ b/include/editeng/autodiritem.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <svl/eitem.hxx> +#include <editeng/editengdllapi.h> + +class EDITENG_DLLPUBLIC SvxAutoFrameDirectionItem final : public SfxBoolItem +{ +public: + DECLARE_ITEM_TYPE_FUNCTION(SvxAutoFrameDirectionItem) + SvxAutoFrameDirectionItem(const bool bValue, const sal_uInt16 nId); + + virtual SvxAutoFrameDirectionItem* Clone(SfxItemPool* pPool = nullptr) const override; + + virtual bool GetPresentation(SfxItemPresentation ePres, MapUnit eCoreMetric, + MapUnit ePresMetric, OUString& rText, + const IntlWrapper&) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/editeng/editids.hrc b/include/editeng/editids.hrc index 6a1a0e81f339..72b4a5a6d8ad 100644 --- a/include/editeng/editids.hrc +++ b/include/editeng/editids.hrc @@ -160,6 +160,7 @@ class SvxScriptHintItem; #define SID_PARA_VERTALIGN TypedWhichId<SvxParaVertAlignItem>( SID_SVX_START + 925 ) #define SID_ATTR_FRAMEDIRECTION TypedWhichId<SvxFrameDirectionItem>( SID_SVX_START + 944 ) #define SID_ATTR_PARA_SNAPTOGRID TypedWhichId<SvxParaGridItem>( SID_SVX_START + 945 ) +#define SID_ATTR_PARA_AUTOFRAMEDIRECTION TypedWhichId<SvxAutoFrameDirectionItem>( SID_SVX_START + 946 ) #define SID_ATTR_PARA_LRSPACE_VERTICAL ( SID_SVX_START + 947 ) #define SID_ATTR_PARA_LEFT_TO_RIGHT ( SID_SVX_START + 950 ) #define SID_ATTR_PARA_RIGHT_TO_LEFT ( SID_SVX_START + 951 ) diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc index fa82e0d90f33..a257b9004b58 100644 --- a/include/editeng/editrids.hrc +++ b/include/editeng/editrids.hrc @@ -301,6 +301,8 @@ #define RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT90 NC_("RID_SVXITEMS_FRMDIR_Vert_TOP_RIGHT90", "Text direction right-to-left (vertical, all characters rotated)") #define RID_SVXITEMS_PARASNAPTOGRID_ON NC_("RID_SVXITEMS_PARASNAPTOGRID_ON", "Paragraph snaps to text grid (if active)") #define RID_SVXITEMS_PARASNAPTOGRID_OFF NC_("RID_SVXITEMS_PARASNAPTOGRID_OFF", "Paragraph does not snap to text grid") +#define RID_SVXITEMS_AUTOFRAMEDIRECTION_ON NC_("RID_SVXITEMS_AUTOFRAMEDIRECTION_ON", "Text direction is automatically updated") +#define RID_SVXITEMS_AUTOFRAMEDIRECTION_OFF NC_("RID_SVXITEMS_AUTOFRAMEDIRECTION_OFF", "Text direction is not automatically updated") #define RID_SVXITEMS_CHARHIDDEN_FALSE NC_("RID_SVXITEMS_CHARHIDDEN_FALSE", "Not hidden") #define RID_SVXITEMS_CHARHIDDEN_TRUE NC_("RID_SVXITEMS_CHARHIDDEN_TRUE", "Hidden") diff --git a/include/editeng/eeitem.hxx b/include/editeng/eeitem.hxx index ce8315ec4e27..89b85207a24a 100644 --- a/include/editeng/eeitem.hxx +++ b/include/editeng/eeitem.hxx @@ -27,6 +27,7 @@ class SfxBoolItem; class SfxGrabBagItem; class SfxInt16Item; class SvxAdjustItem; +class SvxAutoFrameDirectionItem; class SvxAutoKernItem; class SvxCaseMapItem; class SvxCharReliefItem; @@ -93,7 +94,8 @@ inline constexpr TypedWhichId<SvxAdjustItem> EE_PARA_JUST inline constexpr TypedWhichId<SvxTabStopItem> EE_PARA_TABS (EE_PARA_START+17); inline constexpr TypedWhichId<SvxJustifyMethodItem> EE_PARA_JUST_METHOD (EE_PARA_START+18); inline constexpr TypedWhichId<SvxVerJustifyItem> EE_PARA_VER_JUST (EE_PARA_START+19); -inline constexpr sal_uInt16 EE_PARA_END (EE_PARA_START + 19); +inline constexpr TypedWhichId<SvxAutoFrameDirectionItem> EE_PARA_AUTOWRITINGDIR (EE_PARA_START+20); +inline constexpr sal_uInt16 EE_PARA_END (EE_PARA_START + 20); // Character attributes: inline constexpr sal_uInt16 EE_CHAR_START (EE_PARA_END + 1); diff --git a/include/editeng/unotext.hxx b/include/editeng/unotext.hxx index f2bc22fd4dba..37e82293dbb3 100644 --- a/include/editeng/unotext.hxx +++ b/include/editeng/unotext.hxx @@ -170,7 +170,8 @@ struct SfxItemPropertyMapEntry; {u"ParaLetterSpacingMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_LETTER_SPACING_MAX }, \ {u"ParaScaleWidthMinimum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_SCALE_WIDTH_MIN }, \ {u"ParaScaleWidthMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_SCALE_WIDTH_MAX }, \ - {u"WritingMode"_ustr, EE_PARA_WRITINGDIR, ::cppu::UnoType<sal_Int16>::get(), 0, 0 } + {u"WritingMode"_ustr, EE_PARA_WRITINGDIR, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, \ + {u"WritingModeAutomatic"_ustr, EE_PARA_AUTOWRITINGDIR, ::cppu::UnoType<bool>::get(), 0, 0 } class SvxFieldData; diff --git a/include/i18nutil/guessparadirection.hxx b/include/i18nutil/guessparadirection.hxx new file mode 100644 index 000000000000..6ad5a5d8d04d --- /dev/null +++ b/include/i18nutil/guessparadirection.hxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include <i18nutil/i18nutildllapi.h> +#include <com/sun/star/text/ScriptHintType.hpp> +#include <rtl/ustring.hxx> +#include <optional> +#include <memory> +#include <vector> + +namespace i18nutil +{ +enum class ParagraphDirection +{ + Ambiguous, + LeftToRight, + RightToLeft +}; + +I18NUTIL_DLLPUBLIC ParagraphDirection GuessParagraphDirection(const OUString& rText); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/svl/poolitem.hxx b/include/svl/poolitem.hxx index ceebf33f24f3..fbe4b6a62336 100644 --- a/include/svl/poolitem.hxx +++ b/include/svl/poolitem.hxx @@ -309,6 +309,7 @@ enum class SfxItemType : sal_uInt16 Svx3DTextureProjectionXItemType, Svx3DTextureProjectionYItemType, SvxAdjustItemType, + SvxAutoFrameDirectionItemType, SvxAutoKernItemType, SvxB3DVectorItemType, SvxBitmapListItemType, diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 828de31eb2aa..cd1cb76a3cd7 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -2214,6 +2214,7 @@ namespace xmloff::token { XML_WRAP_CONTOUR_MODE, XML_WRAP_OPTION, XML_WRITING_MODE, + XML_WRITING_MODE_AUTOMATIC, XML_WWW, XML_X, XML_X1, diff --git a/sw/inc/editsh.hxx b/sw/inc/editsh.hxx index a44b9e78eb73..aaa90df40c6c 100644 --- a/sw/inc/editsh.hxx +++ b/sw/inc/editsh.hxx @@ -161,6 +161,8 @@ class SAL_DLLPUBLIC_RTTI SwEditShell : public SwCursorShell using SwViewShell::UpdateFields; using sw::BroadcastingModify::GetInfo; + void UpdateSelectionAutoParaDirection(); + public: /// Edit (all selected ranges). void Insert( sal_Unicode, bool bOnlyCurrCursor = false ); diff --git a/sw/inc/hintids.hxx b/sw/inc/hintids.hxx index 9636779a1cc6..39676270559c 100644 --- a/sw/inc/hintids.hxx +++ b/sw/inc/hintids.hxx @@ -35,6 +35,7 @@ class SwFormatContentControl; class SvXMLAttrContainerItem; class SwMsgPoolItem; class SfxBoolItem; +class SvxAutoFrameDirectionItem; class SvxColorItem; class SvxLeftMarginItem; class SvxTextLeftMarginItem; @@ -314,9 +315,13 @@ inline constexpr TypedWhichId<SvxParaGridItem> RES_PARATR_SNAPTOGRID(RES_PARATR_ inline constexpr TypedWhichId<SwParaConnectBorderItem> RES_PARATR_CONNECT_BORDER(RES_PARATR_BEGIN + 15); inline constexpr TypedWhichId<SfxUInt16Item> RES_PARATR_OUTLINELEVEL(RES_PARATR_BEGIN + 16); -inline constexpr TypedWhichId<SvxRsidItem> RES_PARATR_RSID(RES_PARATR_BEGIN + 17); -inline constexpr TypedWhichId<SfxGrabBagItem> RES_PARATR_GRABBAG(RES_PARATR_BEGIN + 18); -inline constexpr sal_uInt16 RES_PARATR_END(RES_PARATR_BEGIN + 19); +inline constexpr TypedWhichId<SvxAutoFrameDirectionItem> RES_PARATR_AUTOFRAMEDIR(RES_PARATR_BEGIN + + 17); +// DocumentRedlineManager assumes RSID and GRABBAG are the last paragraph attributes. +// Insert new paragraph attributes before them. +inline constexpr TypedWhichId<SvxRsidItem> RES_PARATR_RSID(RES_PARATR_BEGIN + 18); +inline constexpr TypedWhichId<SfxGrabBagItem> RES_PARATR_GRABBAG(RES_PARATR_BEGIN + 19); +inline constexpr sal_uInt16 RES_PARATR_END(RES_PARATR_BEGIN + 20); // list attributes for paragraphs. // intentionally these list attributes are not contained in paragraph styles diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc index 5c7d5f2c62cd..d6dedab2f9f3 100644 --- a/sw/inc/inspectorproperties.hrc +++ b/sw/inc/inspectorproperties.hrc @@ -275,6 +275,7 @@ #define RID_UNVISITED_CHAR_STYLE_NAME NC_("RID_ATTRIBUTE_NAMES_MAP", "Unvisited Char Style Name") #define RID_VISITED_CHAR_STYLE_NAME NC_("RID_ATTRIBUTE_NAMES_MAP", "Visited Char Style Name") #define RID_WRITING_MODE NC_("RID_ATTRIBUTE_NAMES_MAP", "Writing Mode") +#define RID_WRITING_MODE_AUTOMATIC NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Auto Writing Mode") /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/paratr.hxx b/sw/inc/paratr.hxx index ae222249fe70..947e9a7c665e 100644 --- a/sw/inc/paratr.hxx +++ b/sw/inc/paratr.hxx @@ -26,6 +26,7 @@ #include "swatrset.hxx" #include "format.hxx" #include "charfmt.hxx" +#include <editeng/autodiritem.hxx> #include <editeng/adjustitem.hxx> #include <editeng/lspcitem.hxx> #include <editeng/spltitem.hxx> @@ -232,6 +233,8 @@ inline const SvxParaGridItem &SwAttrSet::GetParaGrid(bool bInP) const { return Get( RES_PARATR_SNAPTOGRID, bInP ); } inline const SwParaConnectBorderItem &SwAttrSet::GetParaConnectBorder(bool bInP) const { return Get( RES_PARATR_CONNECT_BORDER, bInP ); } +inline const SvxAutoFrameDirectionItem &SwAttrSet::GetAutoFrameDirection(bool bInP) const + { return Get( RES_PARATR_AUTOFRAMEDIR, bInP ); } // Implementation of paragraph-attributes methods of SwFormat inline const SvxLineSpacingItem &SwFormat::GetLineSpacing(bool bInP) const diff --git a/sw/inc/swatrset.hxx b/sw/inc/swatrset.hxx index 8e3df5fcb145..67709a849665 100644 --- a/sw/inc/swatrset.hxx +++ b/sw/inc/swatrset.hxx @@ -130,6 +130,7 @@ class SvxHangingPunctuationItem; class SvxForbiddenRuleItem; class SvxParaVertAlignItem; class SvxParaGridItem; +class SvxAutoFrameDirectionItem; class SwParaConnectBorderItem; // TableBox attributes @@ -322,6 +323,7 @@ public: inline const SvxParaVertAlignItem &GetParaVertAlign(bool = true) const; inline const SvxParaGridItem &GetParaGrid(bool = true) const; inline const SwParaConnectBorderItem &GetParaConnectBorder(bool = true ) const; + inline const SvxAutoFrameDirectionItem &GetAutoFrameDirection(bool = true) const; // Tablebox attributes - implementation in cellatr.hxx inline const SwTableBoxNumFormat &GetTableBoxNumFormat( bool = true ) const; diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 341777e80b23..57ed686f8442 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -736,6 +736,7 @@ inline constexpr OUString UNO_NAME_CELL_NAME = u"CellName"_ustr; inline constexpr OUString UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES = u"ParaUserDefinedAttributes"_ustr; inline constexpr OUString UNO_NAME_MERGE_LAST_PARA = u"MergeLastPara"_ustr; inline constexpr OUString UNO_NAME_WRITING_MODE = u"WritingMode"_ustr; +inline constexpr OUString UNO_NAME_PARA_WRITING_MODE_AUTOMATIC = u"WritingModeAutomatic"_ustr; inline constexpr OUString UNO_NAME_GRID_COLOR = u"GridColor"_ustr; inline constexpr OUString UNO_NAME_GRID_LINES = u"GridLines"_ustr; inline constexpr OUString UNO_NAME_GRID_BASE_HEIGHT = u"GridBaseHeight"_ustr; diff --git a/sw/qa/extras/odfexport/data/tdf162120-style-writing-mode-automatic.fodt b/sw/qa/extras/odfexport/data/tdf162120-style-writing-mode-automatic.fodt new file mode 100644 index 000000000000..bf3cc0283cf9 --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf162120-style-writing-mode-automatic.fodt @@ -0,0 +1,147 @@ +<?xml version='1.0' encoding='UTF-8'?> +<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:c alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns: meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2025-10-30T08:33:30.003457423</meta:creation-date><dc:date>2025-10-31T10:32:54.029847023</dc:date><meta:editing-duration>PT2M18S</meta:editing-duration><meta:editing-cycles>2</meta:editing-cycles><meta:generator>LibreOfficeDev/26.2.0.0.alpha0$Linux_X86_64 LibreOffice_project/08b6c9f129d48adf1e4850742651c78f750af488</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="4" meta:word-count="21" meta:character-count="114" meta:non-whitespace-character-count="97"/></office:meta> + <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"/> + <style:font-face style:name="Noto Sans1" svg:font-family="'Noto Sans'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif CJK SC'" style:font-family-generic="system" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="graphic"> + <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="CA" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" loext:hyphenation-keep-line="false" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="CA" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/> + </style:default-style> + <style:default-style style:family="table"> + <style:table-properties table:border-model="collapsing"/> + </style:default-style> + <style:default-style style:family="table-row"> + <style:table-row-properties fo:keep-together="auto"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <style:style style:name="AutoStyle" style:family="paragraph" style:parent-style-name="Standard"> + <style:paragraph-properties style:writing-mode-automatic="true"/> + </style:style> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/> + <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/> + <text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties/> + </style:style> + <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Standard"> + <style:paragraph-properties style:writing-mode-automatic="true"/> + <style:text-properties/> + </style:style> + <style:style style:name="P3" style:family="paragraph"> + <style:paragraph-properties style:writing-mode-automatic="true"/> + </style:style> + <style:style style:name="P4" style:family="paragraph"> + <loext:graphic-properties draw:fill="none" draw:fill-color="#ffffff"/> + </style:style> + <style:style style:name="gr1" style:family="graphic"> + <style:graphic-properties draw:stroke="none" svg:stroke-color="#000000" draw:fill="none" draw:fill-color="#ffffff" fo:min-height="11.933cm" loext:decorative="false" style:run-through="foreground" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:vertical-rel="paragraph" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true" style:flow-with-text="false"/> + <style:paragraph-properties style:writing-mode="lr-tb"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="21.59cm" fo:page-height="27.94cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm"> + <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"> + <style:drawing-page-properties draw:background-size="full"/> + </style:style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </office:master-styles> + <office:body> + <office:text> + <text:sequence-decls> + <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/> + <text:sequence-decl text:display-outline-level="0" text:name="Table"/> + <text:sequence-decl text:display-outline-level="0" text:name="Text"/> + <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/> + <text:sequence-decl text:display-outline-level="0" text:name="Figure"/> + </text:sequence-decls> + <text:p text:style-name="P1">This paragraph is normal</text:p> + <text:p text:style-name="P1"/> + <text:p text:style-name="P2">This paragraph is auto</text:p> + <text:p text:style-name="P1"/> + <text:p text:style-name="P1">This paragraph is also normal</text:p> + <text:p text:style-name="P1"/> + <text:p text:style-name="AutoStyle">This paragraph is auto by a named style</text:p> + <text:p text:style-name="P1"/> + <text:p text:style-name="P1"><draw:frame text:anchor-type="paragraph" draw:z-index="0" draw:name="Text Frame 1" draw:style-name="gr1" draw:text-style-name="P4" svg:width="15.452cm" svg:height="11.933cm" svg:x="0.804cm" svg:y="1.589cm"> + <draw:text-box> + <text:p>This paragraph is normal too</text:p> + <text:p/> + <text:p text:style-name="P3">But this one is auto</text:p> + <text:p/> + <text:p>And finally, this one is normal</text:p> + </draw:text-box> + </draw:frame></text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/odfexport/odfexport4.cxx b/sw/qa/extras/odfexport/odfexport4.cxx index 087e19731391..e7391f1ed17a 100644 --- a/sw/qa/extras/odfexport/odfexport4.cxx +++ b/sw/qa/extras/odfexport/odfexport4.cxx @@ -1696,6 +1696,19 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf118350StartEndPreserved) "text-align", u"end"); } +CPPUNIT_TEST_FIXTURE(Test, testTdf162120StyleWritingModeAutomaticSerialization) +{ + loadAndReload("tdf162120-style-writing-mode-automatic.fodt"); + + auto pStylesDoc = parseExport(u"styles.xml"_ustr); + assertXPath(pStylesDoc, "//style:paragraph-properties[@style:writing-mode-automatic]", 1); + assertXPath(pStylesDoc, "//style:style[@style:name='AutoStyle']/style:paragraph-properties", + "writing-mode-automatic", u"true"); + + auto pContentDoc = parseExport(u"content.xml"_ustr); + assertXPath(pContentDoc, "//style:paragraph-properties[@style:writing-mode-automatic]", 2); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/uiwriter/data/tdf162120-auto-rtl.fodt b/sw/qa/extras/uiwriter/data/tdf162120-auto-rtl.fodt new file mode 100644 index 000000000000..e5d61c5b3058 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf162120-auto-rtl.fodt @@ -0,0 +1,122 @@ +<?xml version='1.0' encoding='UTF-8'?> +<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:c alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns: meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2025-11-06T11:23:30.187236969</meta:creation-date><dc:date>2025-11-06T11:30:37.198630783</dc:date><meta:editing-duration>PT4M9S</meta:editing-duration><meta:editing-cycles>2</meta:editing-cycles><meta:generator>LibreOfficeDev/26.2.0.0.alpha0$Linux_X86_64 LibreOffice_project/cb898851676f98ce58e79d4ffd1c0081b0003335</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="1" meta:word-count="1" meta:character-count="2" meta:non-whitespace-character-count="2"/></office:meta> + <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"/> + <style:font-face style:name="Noto Sans Arabic" svg:font-family="'Noto Sans Arabic'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/> + <style:font-face style:name="Noto Sans1" svg:font-family="'Noto Sans'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif CJK SC'" style:font-family-generic="system" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="graphic"> + <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="CA" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" loext:hyphenation-keep-line="false" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="CA" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/> + </style:default-style> + <style:default-style style:family="table"> + <style:table-properties table:border-model="collapsing"/> + </style:default-style> + <style:default-style style:family="table-row"> + <style:table-row-properties fo:keep-together="auto"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"> + <style:paragraph-properties fo:text-align="start" style:justify-single-word="false" style:writing-mode-automatic="true"/> + <style:text-properties style:font-name-complex="Noto Sans Arabic" style:font-family-complex="'Noto Sans Arabic'" style:font-style-name-complex="Regular" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:language-complex="ar" style:country-complex="EG"/> + </style:style> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/> + <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/> + <text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard"> + <style:paragraph-properties style:writing-mode="rl-tb"/> + <style:text-properties style:language-complex="ar" style:country-complex="EG"/> + </style:style> + <style:style style:name="T1" style:family="text"> + <style:text-properties fo:language="en" fo:country="US"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="21.59cm" fo:page-height="27.94cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm"> + <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"> + <style:drawing-page-properties draw:background-size="full"/> + </style:style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </office:master-styles> + <office:body> + <office:text> + <text:sequence-decls> + <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/> + <text:sequence-decl text:display-outline-level="0" text:name="Table"/> + <text:sequence-decl text:display-outline-level="0" text:name="Text"/> + <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/> + <text:sequence-decl text:display-outline-level="0" text:name="Figure"/> + </text:sequence-decls> + <text:p text:style-name="P1">ا<text:span text:style-name="T1">a</text:span></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 5e4d2b10e5b9..cc642e56d600 100644 --- a/sw/qa/extras/uiwriter/uiwriter11.cxx +++ b/sw/qa/extras/uiwriter/uiwriter11.cxx @@ -374,6 +374,29 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testTdf108791) } } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testTdf162120AutoRTL) +{ + createSwDoc("tdf162120-auto-rtl.fodt"); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // The initial direction should be RTL + CPPUNIT_ASSERT_EQUAL(short(1), + getProperty<short>(getRun(getParagraph(1), 1), u"WritingMode"_ustr)); + + // Insert a strong LTR character at the start of the paragraph. + // The writing mode should automatically switch to LTR. + pWrtShell->Insert(u"a"_ustr); + CPPUNIT_ASSERT_EQUAL(short(0), + getProperty<short>(getRun(getParagraph(1), 1), u"WritingMode"_ustr)); + + // Delete the leading LTR character. + // The writing mode should switch back to RTL. + pWrtShell->DelLeft(); + CPPUNIT_ASSERT_EQUAL(short(1), + getProperty<short>(getRun(getParagraph(1), 1), u"WritingMode"_ustr)); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/uitest/styleInspector/styleInspector.py b/sw/qa/uitest/styleInspector/styleInspector.py index a9ab1fc940ee..1f044dfc4dec 100644 --- a/sw/qa/uitest/styleInspector/styleInspector.py +++ b/sw/qa/uitest/styleInspector/styleInspector.py @@ -26,7 +26,7 @@ class styleNavigator(UITestCase): # The cursor is on text without formatting and default style self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) self.assertEqual(0, len(xListBox.getChild('3').getChildren())) @@ -36,7 +36,7 @@ class styleNavigator(UITestCase): # The cursor is on text with direct formatting self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) @@ -54,7 +54,7 @@ class styleNavigator(UITestCase): # The cursor is on text with paragraph direct formatting self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) xParDirFormatting = xListBox.getChild('1') self.assertEqual(7, len(xParDirFormatting.getChildren())) @@ -75,7 +75,7 @@ class styleNavigator(UITestCase): xParStyle = xListBox.getChild('0') self.assertEqual(3, len(xParStyle.getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xParStyle.getChild('0'))['Text']) - self.assertEqual(155, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(156, len(xParStyle.getChild('0').getChildren())) self.assertEqual("Heading ", get_state_as_dict(xParStyle.getChild('1'))['Text']) self.assertEqual(28, len(xParStyle.getChild('1').getChildren())) @@ -116,7 +116,7 @@ class styleNavigator(UITestCase): xParStyle = xListBox.getChild('0') self.assertEqual(3, len(xParStyle.getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xParStyle.getChild('0'))['Text']) - self.assertEqual(155, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(156, len(xParStyle.getChild('0').getChildren())) self.assertEqual("Body Text ", get_state_as_dict(xParStyle.getChild('1'))['Text']) self.assertEqual(6, len(xParStyle.getChild('1').getChildren())) @@ -151,7 +151,7 @@ class styleNavigator(UITestCase): # The cursor is on text without metadata self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) self.assertEqual(0, len(xListBox.getChild('3').getChildren())) @@ -161,7 +161,7 @@ class styleNavigator(UITestCase): # The cursor is on text with paragraph metadata showed under direct paragraph formatting self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) xParDirFormatting = xListBox.getChild('1') self.assertEqual(1, len(xParDirFormatting.getChildren())) @@ -214,7 +214,7 @@ class styleNavigator(UITestCase): # The cursor is on text without metadata self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) self.assertEqual(0, len(xListBox.getChild('3').getChildren())) @@ -224,7 +224,7 @@ class styleNavigator(UITestCase): # The cursor is on text with paragraph metadata showed under direct paragraph formatting self.assertEqual(1, len(xListBox.getChild('1').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('1').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('1').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('1').getChild('0').getChildren())) # Outer bookmark xBookmarkFormatting = xListBox.getChild('0') @@ -271,7 +271,7 @@ class styleNavigator(UITestCase): # The cursor is on text without metadata self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) diff --git a/sw/qa/uitest/styleInspector/tdf137513.py b/sw/qa/uitest/styleInspector/tdf137513.py index b615f8af8d2b..b5271f487593 100644 --- a/sw/qa/uitest/styleInspector/tdf137513.py +++ b/sw/qa/uitest/styleInspector/tdf137513.py @@ -35,7 +35,7 @@ class tdf137513(UITestCase): self.assertEqual(2, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) self.assertEqual("Table Contents ", get_state_as_dict(xListBox.getChild('0').getChild('1'))['Text']) - self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(156, len(xListBox.getChild('0').getChild('0').getChildren())) xTableContent = xListBox.getChild('0').getChild('1') self.assertEqual(5, len(xTableContent.getChildren())) diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index 21a5cc10c32f..bfc29059a774 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -26,6 +26,7 @@ #include <comphelper/processfactory.hxx> #include <doc.hxx> #include <editeng/acorrcfg.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/autokernitem.hxx> #include <editeng/blinkitem.hxx> #include <editeng/boxitem.hxx> @@ -376,6 +377,7 @@ std::unique_ptr<ItemInfoPackage> createItemInfoPackageSwAttributes() { RES_PARATR_SNAPTOGRID, new SvxParaGridItem( true, RES_PARATR_SNAPTOGRID ), SID_ATTR_PARA_SNAPTOGRID, SFX_ITEMINFOFLAG_NONE }, { RES_PARATR_CONNECT_BORDER, new SwParaConnectBorderItem, SID_ATTR_BORDER_CONNECT, SFX_ITEMINFOFLAG_NONE }, { RES_PARATR_OUTLINELEVEL, new SfxUInt16Item( RES_PARATR_OUTLINELEVEL, 0 ), SID_ATTR_PARA_OUTLINE_LEVEL, SFX_ITEMINFOFLAG_NONE }, + { RES_PARATR_AUTOFRAMEDIR, new SvxAutoFrameDirectionItem( false, RES_PARATR_AUTOFRAMEDIR ), SID_ATTR_PARA_AUTOFRAMEDIRECTION, SFX_ITEMINFOFLAG_NONE }, { RES_PARATR_RSID, new SvxRsidItem( 0, RES_PARATR_RSID ), 0, SFX_ITEMINFOFLAG_NONE }, { RES_PARATR_GRABBAG, new SfxGrabBagItem( RES_PARATR_GRABBAG ), SID_ATTR_PARA_GRABBAG, SFX_ITEMINFOFLAG_NONE }, diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx index cfb85bb2b291..3e54aa44a8f3 100644 --- a/sw/source/core/doc/dbgoutsw.cxx +++ b/sw/source/core/doc/dbgoutsw.cxx @@ -159,6 +159,7 @@ static std::map<sal_uInt16,OUString> & GetItemWhichMap() { RES_PARATR_VERTALIGN , "PARATR_VERTALIGN" }, { RES_PARATR_SNAPTOGRID , "PARATR_SNAPTOGRID" }, { RES_PARATR_CONNECT_BORDER , "PARATR_CONNECT_BORDER" }, + { RES_PARATR_AUTOFRAMEDIR, "PARATR_AUTOFRAMEDIR" }, { RES_FILL_ORDER , "FILL_ORDER" }, { RES_FRM_SIZE , "FRM_SIZE" }, { RES_PAPER_BIN , "PAPER_BIN" }, diff --git a/sw/source/core/edit/eddel.cxx b/sw/source/core/edit/eddel.cxx index 654b9b0fb547..bcf151df3b37 100644 --- a/sw/source/core/edit/eddel.cxx +++ b/sw/source/core/edit/eddel.cxx @@ -164,6 +164,9 @@ bool SwEditShell::Delete(bool const isArtificialSelection, bool goLeft) DeleteSel(rPaM, isArtificialSelection, goLeft, &bUndo); } + // tdf#162120: Automatically adjust paragraph directions after delete + UpdateSelectionAutoParaDirection(); + // If undo container then close here if( bUndo ) { diff --git a/sw/source/core/edit/editsh.cxx b/sw/source/core/edit/editsh.cxx index 9a69d60e1565..742f4c7e22cf 100644 --- a/sw/source/core/edit/editsh.cxx +++ b/sw/source/core/edit/editsh.cxx @@ -24,6 +24,9 @@ #include <comphelper/processfactory.hxx> #include <comphelper/string.hxx> #include <unotools/transliterationwrapper.hxx> +#include <i18nutil/guessparadirection.hxx> +#include <editeng/autodiritem.hxx> +#include <editeng/frmdiritem.hxx> #include <fmtsrnd.hxx> #include <fmtinfmt.hxx> #include <txtinet.hxx> @@ -59,6 +62,58 @@ using namespace com::sun::star; +void SwEditShell::UpdateSelectionAutoParaDirection() +{ + for (SwPaM& rPaM : getShellCursor(true)->GetRingContainer()) + { + auto* pNode = rPaM.GetPointNode().GetTextNode(); + if (!pNode) + { + continue; + } + + const SvxAutoFrameDirectionItem* pAutoItem + = pNode->GetSwAttrSet().GetItemIfSet(RES_PARATR_AUTOFRAMEDIR); + if (!pAutoItem || !pAutoItem->GetValue()) + { + continue; + } + + Point aPt; + std::pair<Point, bool> const tmp(aPt, false); + const SwTextFrame* pFrame + = static_cast<SwTextFrame*>(pNode->getLayoutFrame(GetLayout(), rPaM.GetPoint(), &tmp)); + + bool bIsAlreadyRtl = pFrame->IsRightToLeft(); + + bool bShouldBeRtl = bIsAlreadyRtl; + switch (i18nutil::GuessParagraphDirection(pNode->GetText())) + { + case i18nutil::ParagraphDirection::Ambiguous: + bShouldBeRtl = bIsAlreadyRtl; + break; + + case i18nutil::ParagraphDirection::LeftToRight: + bShouldBeRtl = false; + break; + + case i18nutil::ParagraphDirection::RightToLeft: + bShouldBeRtl = true; + break; + } + + if (bShouldBeRtl == bIsAlreadyRtl) + { + continue; + } + + SvxFrameDirection eNeeded = bShouldBeRtl ? SvxFrameDirection::Horizontal_RL_TB + : SvxFrameDirection::Horizontal_LR_TB; + rPaM.GetDoc().getIDocumentContentOperations().InsertPoolItem( + rPaM, SvxFrameDirectionItem{ eNeeded, RES_FRAMEDIR }); + } +} + void SwEditShell::Insert( sal_Unicode c, bool bOnlyCurrCursor ) { StartAllAction(); @@ -73,6 +128,9 @@ void SwEditShell::Insert( sal_Unicode c, bool bOnlyCurrCursor ) } + // tdf#162120: Automatically adjust paragraph direction after insert + UpdateSelectionAutoParaDirection(); + EndAllAction(); } @@ -162,6 +220,9 @@ void SwEditShell::Insert2(const OUString &rStr, const bool bForceExpandHints ) SetInFrontOfLabel( false ); // #i27615# + // tdf#162120: Automatically adjust paragraph direction after insert + UpdateSelectionAutoParaDirection(); + EndAllAction(); } @@ -978,6 +1039,9 @@ OUString SwEditShell::DeleteExtTextInput( bool bInsText ) if ( ! bInsText && IsOverwriteCursor() ) *GetCursor()->GetPoint() = aPos; + // tdf#162120: Automatically adjust paragraph direction after insert + UpdateSelectionAutoParaDirection(); + EndAllAction(); } return sRet; diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 0e6ee62e52af..e2dfba35bf15 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -255,6 +255,7 @@ { UNO_NAME_CHAR_SHADING_VALUE, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, \ { UNO_NAME_PARA_INTEROP_GRAB_BAG, RES_PARATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, \ { UNO_NAME_CHAR_SCRIPT_HINT, RES_CHRATR_SCRIPT_HINT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_SCRIPTHINT }, \ + { UNO_NAME_PARA_WRITING_MODE_AUTOMATIC, RES_PARATR_AUTOFRAMEDIR, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ #define COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN \ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01 \ @@ -547,7 +548,8 @@ { UNO_NAME_OUTLINE_LEVEL, RES_PARATR_OUTLINELEVEL,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \ { UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \ { UNO_NAME_STYLE_INTEROP_GRAB_BAG, FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \ - { UNO_NAME_PARA_INTEROP_GRAB_BAG, RES_PARATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + { UNO_NAME_PARA_INTEROP_GRAB_BAG, RES_PARATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \ + { UNO_NAME_PARA_WRITING_MODE_AUTOMATIC, RES_PARATR_AUTOFRAMEDIR, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0}, #define COMMON_ACCESSIBILITY_TEXT_ATTRIBUTE \ { UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, \ diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx index 85d5368a3dfb..fa4f4fec235e 100644 --- a/sw/source/filter/html/css1atr.cxx +++ b/sw/source/filter/html/css1atr.cxx @@ -3484,6 +3484,7 @@ SwAttrFnTab const aCSS1AttrFnTab = { /* RES_PARATR_SNAPTOGRID*/ nullptr, // new /* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new /* RES_PARATR_OUTLINELEVEL */ nullptr, // new since cws outlinelevel +/* RES_PARATR_AUTOFRAMEDIR */ nullptr, /* RES_PARATR_RSID */ nullptr, // new /* RES_PARATR_GRABBAG */ nullptr, diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index 026d6c1060f7..1a5b991efd12 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -3361,6 +3361,7 @@ const SwAttrFnTab aHTMLAttrFnTab = { /* RES_PARATR_SNAPTOGRID*/ nullptr, // new /* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new /* RES_PARATR_OUTLINELEVEL */ nullptr, +/* RES_PARATR_AUTOFRAMEDIR */ nullptr, /* RES_PARATR_RSID */ nullptr, /* RES_PARATR_GRABBAG */ nullptr, diff --git a/sw/source/uibase/shells/txtattr.cxx b/sw/source/uibase/shells/txtattr.cxx index eee4503fb501..8197b5682323 100644 --- a/sw/source/uibase/shells/txtattr.cxx +++ b/sw/source/uibase/shells/txtattr.cxx @@ -26,6 +26,7 @@ #include <sfx2/bindings.hxx> #include <sfx2/request.hxx> #include <sfx2/viewfrm.hxx> +#include <editeng/autodiritem.hxx> #include <editeng/fhgtitem.hxx> #include <editeng/adjustitem.hxx> #include <editeng/lspcitem.hxx> @@ -327,6 +328,7 @@ void SwTextShell::ExecParaAttr(SfxRequest &rReq) // Get both attributes immediately isn't more expensive!! SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<RES_PARATR_LINESPACING, RES_PARATR_ADJUST, RES_PARATR_HYPHENZONE, RES_PARATR_HYPHENZONE, + RES_PARATR_AUTOFRAMEDIR, RES_PARATR_AUTOFRAMEDIR, RES_FRAMEDIR, RES_FRAMEDIR>(GetPool())); sal_uInt16 nSlot = rReq.GetSlot(); switch (nSlot) @@ -406,6 +408,10 @@ SET_LINESPACE: SvxFrameDirection::Horizontal_LR_TB : SvxFrameDirection::Horizontal_RL_TB; aSet.Put( SvxFrameDirectionItem( eFrameDirection, RES_FRAMEDIR ) ); + // tdf#162120: The paragraph direction has been manually set by the user. + // Don't automatically adjust the paragraph direction anymore. + aSet.Put(SvxAutoFrameDirectionItem(false, RES_PARATR_AUTOFRAMEDIR)); + if (bChgAdjust) { SvxAdjust eAdjust = (SID_ATTR_PARA_LEFT_TO_RIGHT == nSlot) ? diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx index 33a27b191cac..e8eb46377abd 100644 --- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx +++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx @@ -339,6 +339,7 @@ static OUString PropertyNametoRID(const OUString& rName) { "UnvisitedCharStyleName", RID_UNVISITED_CHAR_STYLE_NAME }, { "VisitedCharStyleName", RID_VISITED_CHAR_STYLE_NAME }, { "WritingMode", RID_WRITING_MODE }, + { "WritingModeAutomatic", RID_WRITING_MODE_AUTOMATIC }, { "BorderColor", RID_BORDER_COLOR }, { "BorderInnerLineWidth", RID_BORDER_INNER_LINE_WIDTH }, { "BorderLineDistance", RID_BORDER_LINE_DISTANCE }, diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx index 657ddf1eeae8..689220a4819e 100644 --- a/xmloff/inc/xmlprop.hxx +++ b/xmloff/inc/xmlprop.hxx @@ -691,6 +691,7 @@ inline constexpr OUString PROP_WidthType = u"WidthType"_ustr; inline constexpr OUString PROP_WrapInfluenceOnPosition = u"WrapInfluenceOnPosition"_ustr; inline constexpr OUString PROP_WrapTextAtFlyStart = u"WrapTextAtFlyStart"_ustr; inline constexpr OUString PROP_WritingMode = u"WritingMode"_ustr; +inline constexpr OUString PROP_WritingModeAutomatic = u"WritingModeAutomatic"_ustr; inline constexpr OUString PROP_XName = u"XName"_ustr; inline constexpr OUString PROP_YName = u"YName"_ustr; diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 4b8c63d5936c..f8cfdddf335a 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -2227,6 +2227,7 @@ namespace xmloff::token { TOKEN( "wrap-contour-mode", XML_WRAP_CONTOUR_MODE ), TOKEN( "wrap-option", XML_WRAP_OPTION ), TOKEN( "writing-mode", XML_WRITING_MODE ), + TOKEN( "writing-mode-automatic", XML_WRITING_MODE_AUTOMATIC ), TOKEN( "www", XML_WWW ), TOKEN( "x", XML_X ), TOKEN( "x1", XML_X1 ), diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx index 593006cf0ec3..f239a75b32a1 100644 --- a/xmloff/source/text/txtprmap.cxx +++ b/xmloff/source/text/txtprmap.cxx @@ -465,6 +465,9 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] = MP_ED( PROP_WritingMode, XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT, CTF_TEXTWRITINGMODE ), + // RES_PARATR_AUTOFRAMEDIR + MP_E( PROP_WritingModeAutomatic, XML_NAMESPACE_STYLE, XML_WRITING_MODE_AUTOMATIC, XML_TYPE_BOOL, 0 ), + MP_E( PROP_ParaIsConnectBorder, XML_NAMESPACE_STYLE, XML_JOIN_BORDER, XML_TYPE_BOOL, 0 ), MP_E( PROP_DefaultOutlineLevel, XML_NAMESPACE_STYLE, XML_DEFAULT_OUTLINE_LEVEL, XML_TYPE_TEXT_NUMBER8_ONE_BASED|MID_FLAG_SPECIAL_ITEM_EXPORT|MID_FLAG_NO_PROPERTY_IMPORT, CTF_DEFAULT_OUTLINE_LEVEL ), diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 9576187b7522..28317a175325 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -2127,6 +2127,7 @@ wrap-contour wrap-contour-mode wrap-option writing-mode +writing-mode-automatic www x x1
