tdf#i165354 sw offapi DOCX: implement HyphenationKeepLine – part 1
ODF 1.0 feature hyphenation-keep, i.e. disabling hyphenation across page etc. was implemented by shifting the full hyphenated line (fixing also the DOCX layout interoperability, when value of the useWord2013TrackBottomHyphenation MSO DOCX compatibility option is true). To shift only the last word, add UNO paragraph property com::sun:⭐:text::ParagraphHyphenationKeepLine and the associated layout in Writer core, i.e. disabling the hyphenation in the last line, instead of line shifting, if the value of ParagraphHyphenationKeepLine is true. Fix missing DOCX layout interoperability by mapping the unhandled useWord2013TrackBottomHyphenation = false case to the new layout. Note: shifting lines may be better for languages with long words, but only with 1) typesetting with line-level (greedy) line breaking instead of the desirable paragraph-level, 2) and with missing optimized hyphenation at boundaries of compound word constituents, and 3) forgotting the following: Shifting only words or word constituents are better for 1) equal line count on pages and spreads (very basic typographical requirement), 2) disabling hyphenation really (shifting doesn't guarantee this, because shifting is limited in a single line, but the previous line can be hyphenated, too) and 3) more compact typesetting. Both hyphenation-keep algorithms are important for interoperability, but the new HyphenationKeepLine can create state-of-the-art automatic typesetting with the planned improvements. Follow-up to commit9574a62add
"tdf#132599 cui offapi sw xmloff: implement hyphenate-keep". and commitc8ee0e8f58
"tdf160518 DOCX: import hyphenation-keep to fix layout". Change-Id: Iafa352ec91bbd5d1d227b8480337fa2b9acb5993 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181954 Reviewed-by: László Németh <nemeth@numbertext.org> Tested-by: Jenkins
This commit is contained in:
@@ -594,6 +594,7 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, const sal_uInt16 nId ) :
|
||||
nMinWordLength(0),
|
||||
nTextHyphenZone(0),
|
||||
nKeepType(css::text::ParagraphHyphenationKeepType::COLUMN),
|
||||
bKeepLine(false),
|
||||
nCompoundMinLead(0)
|
||||
{
|
||||
}
|
||||
@@ -634,6 +635,9 @@ bool SvxHyphenZoneItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) con
|
||||
case MID_HYPHEN_KEEP_TYPE:
|
||||
rVal <<= static_cast<sal_Int16>(nKeepType);
|
||||
break;
|
||||
case MID_HYPHEN_KEEP_LINE:
|
||||
rVal <<= bKeepLine;
|
||||
break;
|
||||
case MID_HYPHEN_COMPOUND_MIN_LEAD:
|
||||
rVal <<= static_cast<sal_Int16>(nCompoundMinLead);
|
||||
break;
|
||||
@@ -647,7 +651,8 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
|
||||
sal_Int32 nNewVal = 0; // sal_Int32 needs for MID_HYPHEN_KEEP_TYPE
|
||||
|
||||
if( nMemberId != MID_IS_HYPHEN && nMemberId != MID_HYPHEN_NO_CAPS &&
|
||||
nMemberId != MID_HYPHEN_NO_LAST_WORD && nMemberId != MID_HYPHEN_KEEP )
|
||||
nMemberId != MID_HYPHEN_NO_LAST_WORD && nMemberId != MID_HYPHEN_KEEP &&
|
||||
nMemberId != MID_HYPHEN_KEEP_LINE )
|
||||
{
|
||||
if(!(rVal >>= nNewVal))
|
||||
return false;
|
||||
@@ -685,6 +690,9 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
|
||||
case MID_HYPHEN_KEEP_TYPE:
|
||||
nKeepType = static_cast<sal_uInt8>(nNewVal);
|
||||
break;
|
||||
case MID_HYPHEN_KEEP_LINE:
|
||||
bKeepLine = Any2Bool(rVal);
|
||||
break;
|
||||
case MID_HYPHEN_COMPOUND_MIN_LEAD:
|
||||
nCompoundMinLead = static_cast<sal_uInt8>(nNewVal);
|
||||
break;
|
||||
@@ -708,6 +716,7 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& rAttr ) const
|
||||
&& rItem.nMaxHyphens == nMaxHyphens
|
||||
&& rItem.nMinWordLength == nMinWordLength
|
||||
&& rItem.nTextHyphenZone == nTextHyphenZone
|
||||
&& rItem.bKeepLine == bKeepLine
|
||||
&& rItem.nKeepType == nKeepType );
|
||||
}
|
||||
|
||||
@@ -748,10 +757,18 @@ bool SvxHyphenZoneItem::GetPresentation
|
||||
rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE);
|
||||
|
||||
if ( bKeep )
|
||||
{
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_TRUE) +
|
||||
cpDelimTmp + OUString::number( nKeepType );
|
||||
if ( bKeepLine )
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_LINE_TRUE);
|
||||
else
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_LINE_FALSE);
|
||||
}
|
||||
else
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_FALSE);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
case SfxItemPresentation::Complete:
|
||||
@@ -788,6 +805,7 @@ bool SvxHyphenZoneItem::GetPresentation
|
||||
if ( bKeep )
|
||||
{
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_TRUE) + cpDelimTmp;
|
||||
|
||||
switch ( nKeepType )
|
||||
{
|
||||
case 0:
|
||||
@@ -806,6 +824,11 @@ bool SvxHyphenZoneItem::GetPresentation
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_ALWAYS);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( bKeepLine )
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_LINE_TRUE);
|
||||
else
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_LINE_FALSE);
|
||||
}
|
||||
else
|
||||
rText += EditResId(RID_SVXITEMS_HYPHEN_KEEP_FALSE);
|
||||
|
@@ -201,6 +201,8 @@
|
||||
#define RID_SVXITEMS_HYPHEN_FALSE NC_("RID_SVXITEMS_HYPHEN_FALSE", "No hyphenation")
|
||||
#define RID_SVXITEMS_HYPHEN_KEEP_TRUE NC_("RID_SVXITEMS_HYPHEN_KEEP_TRUE", "No hyphenation across")
|
||||
#define RID_SVXITEMS_HYPHEN_KEEP_FALSE NC_("RID_SVXITEMS_HYPHEN_KEEP_FALSE", "Hyphenation across page")
|
||||
#define RID_SVXITEMS_HYPHEN_KEEP_LINE_TRUE NC_("RID_SVXITEMS_HYPHEN_KEEP_LINE_TRUE", "by shifting the last hyphenated word")
|
||||
#define RID_SVXITEMS_HYPHEN_KEEP_LINE_FALSE NC_("RID_SVXITEMS_HYPHEN_KEEP_LINE_FALSE", "by shifting the last hyphenated line")
|
||||
#define RID_SVXITEMS_SIZE_WIDTH NC_("RID_SVXITEMS_SIZE_WIDTH", "Width: ")
|
||||
#define RID_SVXITEMS_SIZE_HEIGHT NC_("RID_SVXITEMS_SIZE_HEIGHT", "Height: ")
|
||||
#define RID_SVXITEMS_LRSPACE_LEFT NC_("RID_SVXITEMS_LRSPACE_LEFT", "Indent left ")
|
||||
|
@@ -42,6 +42,7 @@ class EDITENG_DLLPUBLIC SvxHyphenZoneItem final : public SfxPoolItem
|
||||
sal_uInt8 nMinWordLength; // hyphenate only words with at least nMinWordLength characters
|
||||
sal_uInt16 nTextHyphenZone; // don't force hyphenation at line end, allow this extra white space
|
||||
sal_uInt8 nKeepType; // avoid hyphenation across page etc., see ParagraphHyphenationKeep
|
||||
bool bKeepLine : 1; // if bKeep, shift the hyphenated word (true), or the full line
|
||||
sal_uInt8 nCompoundMinLead; // min. characters between compound word boundary and hyphenation
|
||||
|
||||
public:
|
||||
@@ -94,6 +95,9 @@ public:
|
||||
|
||||
sal_uInt8 &GetKeepType() { return nKeepType; }
|
||||
sal_uInt8 GetKeepType() const { return nKeepType; }
|
||||
|
||||
void SetKeepLine( const bool bNew ) { bKeepLine = bNew; }
|
||||
bool IsKeepLine() const { return bKeepLine; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -54,6 +54,7 @@
|
||||
#define MID_HYPHEN_KEEP 8
|
||||
#define MID_HYPHEN_KEEP_TYPE 9
|
||||
#define MID_HYPHEN_COMPOUND_MIN_LEAD 10
|
||||
#define MID_HYPHEN_KEEP_LINE 11
|
||||
|
||||
// SvxBoxInfoItem
|
||||
#define MID_HORIZONTAL 1
|
||||
|
@@ -44,6 +44,7 @@ inline constexpr OUString UPN_HYPH_NO_LAST_WORD = u"HyphNoLastWord
|
||||
inline constexpr OUString UPN_HYPH_ZONE = u"HyphZone"_ustr;
|
||||
inline constexpr OUString UPN_HYPH_KEEP = u"HyphKeep"_ustr;
|
||||
inline constexpr OUString UPN_HYPH_KEEP_TYPE = u"HyphKeepType"_ustr;
|
||||
inline constexpr OUString UPN_HYPH_KEEP_LINE = u"HyphKeepLine"_ustr;
|
||||
|
||||
// UNO property names for Lingu
|
||||
// (those not covered by the SpellChecker and Hyphenator
|
||||
@@ -113,6 +114,7 @@ inline constexpr OUString UPN_IS_GRAMMAR_INTERACTIVE = u"IsInteractiveG
|
||||
#define UPH_HYPH_KEEP 34
|
||||
#define UPH_HYPH_KEEP_TYPE 35
|
||||
#define UPH_HYPH_COMPOUND_MIN_LEADING 36
|
||||
#define UPH_HYPH_KEEP_LINE 37
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
|
@@ -454,6 +454,18 @@ published service ParagraphProperties
|
||||
@since LibreOffice 24.8
|
||||
*/
|
||||
[optional, property] short ParaHyphenationCompoundMinLeadingChars;
|
||||
|
||||
/** This value is `TRUE` if ParaHyphenationKeep = TRUE solved by
|
||||
shifting the hyphenated word, and FALSE, if it is solved by shifting
|
||||
(only a single) full hyphenated line.
|
||||
|
||||
@see ParaHyphenationKeep
|
||||
|
||||
@see ParaHyphenationKeepType
|
||||
|
||||
@since LibreOffice 25.8
|
||||
*/
|
||||
[optional, property] boolean ParaHyphenationKeepLine;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -277,6 +277,7 @@ struct SvxHyphenZone
|
||||
INT16 HyphenZone MID_HYPHEN_ZONE;
|
||||
BOOL HyphenKeep MID_HYPHEN_KEEP;
|
||||
INT16 HyphenKeepType MID_HYPHEN_KEEP_TYPE;
|
||||
BOOL HyphenKeepLine MID_HYPHEN_KEEP_LINE;
|
||||
INT16 MinLead MID_HYPHEN_COMPOUND_MIN_LEAD;
|
||||
};
|
||||
item SvxHyphenZone SvxHyphenZoneItem;
|
||||
|
@@ -212,6 +212,7 @@
|
||||
#define RID_PARA_HYPHENATION_ZONE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Zone")
|
||||
#define RID_PARA_HYPHENATION_KEEP NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep")
|
||||
#define RID_PARA_HYPHENATION_KEEP_TYPE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep Type")
|
||||
#define RID_PARA_HYPHENATION_KEEP_LINE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep Line")
|
||||
#define RID_PARA_INTEROP_GRAB_BAG NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Interop Grab Bag")
|
||||
#define RID_PARA_IS_AUTO_FIRST_LINE_INDENT NC_("RID_ATTRIBUTE_NAMES_MAP", "Para is Auto First Line Indent")
|
||||
#define RID_PARA_IS_CHARACTER_DISTANCE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para is Character Distance")
|
||||
|
@@ -83,6 +83,7 @@ inline constexpr OUString UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD
|
||||
= u"ParaHyphenationNoLastWord"_ustr;
|
||||
inline constexpr OUString UNO_NAME_PARA_HYPHENATION_KEEP = u"ParaHyphenationKeep"_ustr;
|
||||
inline constexpr OUString UNO_NAME_PARA_HYPHENATION_KEEP_TYPE = u"ParaHyphenationKeepType"_ustr;
|
||||
inline constexpr OUString UNO_NAME_PARA_HYPHENATION_KEEP_LINE = u"ParaHyphenationKeepLine"_ustr;
|
||||
inline constexpr OUString UNO_NAME_LEFT_MARGIN = u"LeftMargin"_ustr;
|
||||
inline constexpr OUString UNO_NAME_RIGHT_MARGIN = u"RightMargin"_ustr;
|
||||
inline constexpr OUString UNO_NAME_GUTTER_MARGIN = u"GutterMargin"_ustr;
|
||||
|
BIN
sw/qa/extras/ooxmlexport/data/tdf165354.docx
Normal file
BIN
sw/qa/extras/ooxmlexport/data/tdf165354.docx
Normal file
Binary file not shown.
@@ -1496,13 +1496,20 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf160518_page_in_text_body_style)
|
||||
assertXPath(pXmlDoc, "/w:settings/w:compat/w:compatSetting[@w:name='allowHyphenationAtTrackBottom']", 0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testTdf160518_auto_in_text_body_style)
|
||||
CPPUNIT_TEST_FIXTURE(Test, testTdf165354)
|
||||
{
|
||||
// text body style contains hyphenation settings
|
||||
loadAndReload("tdf160518_auto_in_text_body_style.fodt");
|
||||
xmlDocUniquePtr pXmlDoc = parseExport(u"word/settings.xml"_ustr);
|
||||
assertXPath(pXmlDoc, "/w:settings/w:compat/w:compatSetting[@w:name='useWord2013TrackBottomHyphenation']", "val", u"1");
|
||||
assertXPath(pXmlDoc, "/w:settings/w:compat/w:compatSetting[@w:name='allowHyphenationAtTrackBottom']", "val", u"1");
|
||||
uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator();
|
||||
if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, OUString())))
|
||||
return;
|
||||
|
||||
loadAndReload("tdf165354.docx");
|
||||
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
|
||||
// This was "except that it has an at" (hyphenation at the end of the page)
|
||||
assertXPath(pXmlDoc, "//page[1]/body/txt[2]/SwParaPortion/SwLineLayout[9]", "portion", u"except that it has an ");
|
||||
// This started with "mosphere" (hyphenation at the end of the previous page)
|
||||
assertXPath(pXmlDoc, "//page[2]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion", u"atmosphere. The Earth ");
|
||||
// The same word is still hyphenated in the same paragraph, but not at the bottom of the page
|
||||
assertXPath(pXmlDoc, "//page[2]/body/txt[1]/SwParaPortion/SwLineLayout[9]", "portion", u"except that it has an at");
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testHyphenationAuto)
|
||||
|
@@ -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\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, 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\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, 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\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, 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\t", get_state_as_dict(xParStyle.getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xParStyle.getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xParStyle.getChild('0').getChildren()))
|
||||
self.assertEqual("Heading\t", get_state_as_dict(xParStyle.getChild('1'))['Text'])
|
||||
self.assertEqual(28, len(xParStyle.getChild('1').getChildren()))
|
||||
|
||||
@@ -109,7 +109,7 @@ class styleNavigator(UITestCase):
|
||||
xParStyle = xListBox.getChild('0')
|
||||
self.assertEqual(3, len(xParStyle.getChildren()))
|
||||
self.assertEqual("Default Paragraph Style\t", get_state_as_dict(xParStyle.getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xParStyle.getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xParStyle.getChild('0').getChildren()))
|
||||
self.assertEqual("Body Text\t", get_state_as_dict(xParStyle.getChild('1'))['Text'])
|
||||
self.assertEqual(6, len(xParStyle.getChild('1').getChildren()))
|
||||
|
||||
@@ -144,7 +144,7 @@ class styleNavigator(UITestCase):
|
||||
# The cursor is on text without metadata
|
||||
self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
|
||||
self.assertEqual("Default Paragraph Style\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, 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()))
|
||||
@@ -154,7 +154,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\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
|
||||
xParDirFormatting = xListBox.getChild('1')
|
||||
self.assertEqual(1, len(xParDirFormatting.getChildren()))
|
||||
@@ -207,7 +207,7 @@ class styleNavigator(UITestCase):
|
||||
# The cursor is on text without metadata
|
||||
self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
|
||||
self.assertEqual("Default Paragraph Style\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, 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()))
|
||||
@@ -217,7 +217,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\t", get_state_as_dict(xListBox.getChild('1').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('1').getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xListBox.getChild('1').getChild('0').getChildren()))
|
||||
|
||||
# Outer bookmark
|
||||
xBookmarkFormatting = xListBox.getChild('0')
|
||||
@@ -264,7 +264,7 @@ class styleNavigator(UITestCase):
|
||||
# The cursor is on text without metadata
|
||||
self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
|
||||
self.assertEqual("Default Paragraph Style\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
|
||||
self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
|
||||
|
||||
|
@@ -35,7 +35,7 @@ class tdf137513(UITestCase):
|
||||
self.assertEqual(2, len(xListBox.getChild('0').getChildren()))
|
||||
self.assertEqual("Default Paragraph Style\t", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
|
||||
self.assertEqual("Table Contents\t", get_state_as_dict(xListBox.getChild('0').getChild('1'))['Text'])
|
||||
self.assertEqual(143, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
self.assertEqual(144, len(xListBox.getChild('0').getChild('0').getChildren()))
|
||||
|
||||
xTableContent = xListBox.getChild('0').getChild('1')
|
||||
self.assertEqual(5, len(xTableContent.getChildren()))
|
||||
|
@@ -208,6 +208,7 @@ class SW_DLLPUBLIC SwTextFrame final : public SwContentFrame
|
||||
std::unique_ptr<sw::MergedPara> m_pMergedPara;
|
||||
|
||||
TextFrameIndex mnOffset; // Is the offset in the Content (character count)
|
||||
TextFrameIndex mnNoHyphOffset; // Is the offset of the last line to disable its hyphenation
|
||||
|
||||
sal_uInt16 mnCacheIndex; // Index into the cache, USHRT_MAX if there's definitely no fitting object in the cache
|
||||
|
||||
@@ -454,6 +455,8 @@ public:
|
||||
TextFrameIndex GetOffset() const { return mnOffset; }
|
||||
void SetOffset_(TextFrameIndex nNewOfst);
|
||||
inline void SetOffset (TextFrameIndex nNewOfst);
|
||||
TextFrameIndex GetNoHyphOffset() const { return mnNoHyphOffset; }
|
||||
void SetNoHyphOffset(TextFrameIndex const nNewOfst) { mnNoHyphOffset = nNewOfst; }
|
||||
void ManipOfst(TextFrameIndex const nNewOfst) { mnOffset = nNewOfst; }
|
||||
SwTextFrame *GetFrameAtPos ( const SwPosition &rPos);
|
||||
inline const SwTextFrame *GetFrameAtPos ( const SwPosition &rPos) const;
|
||||
|
@@ -286,7 +286,11 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
||||
}
|
||||
}
|
||||
|
||||
bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud();
|
||||
bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud() &&
|
||||
// disable hyphenation in the last line, when hyphenation-keep-line is enabled
|
||||
( rInf.GetTextFrame()->GetNoHyphOffset() == TextFrameIndex(COMPLETE_STRING) ||
|
||||
// when there is a portion in the last line, rInf.GetIdx() > GetNoHyphOffset()
|
||||
rInf.GetTextFrame()->GetNoHyphOffset() > rInf.GetIdx() );
|
||||
TextFrameIndex nHyphPos(0);
|
||||
|
||||
// nCutPos is the first character not fitting to the current line
|
||||
|
@@ -1563,13 +1563,13 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
|
||||
sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
|
||||
bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
|
||||
sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone, bool bKeep, sal_Int16 nKeepType,
|
||||
sal_Int16 nCompoundMinLeading )
|
||||
bool bKeepLine, sal_Int16 nCompoundMinLeading )
|
||||
{
|
||||
sal_Int32 nLen = rVals.getLength();
|
||||
|
||||
if (0 == nLen) // yet to be initialized?
|
||||
{
|
||||
rVals.realloc( 9 );
|
||||
rVals.realloc( 10 );
|
||||
PropertyValue *pVal = rVals.getArray();
|
||||
|
||||
pVal[0].Name = UPN_HYPH_MIN_LEADING;
|
||||
@@ -1607,8 +1607,12 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
|
||||
pVal[8].Name = UPN_HYPH_KEEP;
|
||||
pVal[8].Handle = UPH_HYPH_KEEP;
|
||||
pVal[8].Value <<= bKeep;
|
||||
|
||||
pVal[9].Name = UPN_HYPH_KEEP_LINE;
|
||||
pVal[9].Handle = UPH_HYPH_KEEP_LINE;
|
||||
pVal[9].Value <<= bKeepLine;
|
||||
}
|
||||
else if (9 == nLen) // already initialized once?
|
||||
else if (10 == nLen) // already initialized once?
|
||||
{
|
||||
PropertyValue *pVal = rVals.getArray();
|
||||
pVal[0].Value <<= nMinLeading;
|
||||
@@ -1620,6 +1624,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
|
||||
pVal[6].Value <<= nKeepType;
|
||||
pVal[7].Value <<= nCompoundMinLeading;
|
||||
pVal[8].Value <<= bKeep;
|
||||
pVal[9].Value <<= bKeepLine;
|
||||
}
|
||||
else {
|
||||
OSL_FAIL( "unexpected size of sequence" );
|
||||
@@ -1628,7 +1633,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
|
||||
|
||||
const PropertyValues & SwTextFormatInfo::GetHyphValues() const
|
||||
{
|
||||
OSL_ENSURE( 9 == m_aHyphVals.getLength(),
|
||||
OSL_ENSURE( 10 == m_aHyphVals.getLength(),
|
||||
"hyphenation values not yet initialized" );
|
||||
return m_aHyphVals;
|
||||
}
|
||||
@@ -1652,10 +1657,12 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
|
||||
const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone();
|
||||
const bool bKeep = rAttr.IsKeep();
|
||||
const sal_Int16 nKeepType = rAttr.GetKeepType();
|
||||
const bool bKeepLine = rAttr.IsKeepLine();
|
||||
const sal_Int16 nCompoundMinimalLeading = std::max(rAttr.GetCompoundMinLead(), sal_uInt8(2));
|
||||
lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
|
||||
bNoCapsHyphenation, bNoLastWordHyphenation,
|
||||
nMinimalWordLength, nTextHyphZone, bKeep, nKeepType, nCompoundMinimalLeading );
|
||||
nMinimalWordLength, nTextHyphZone, bKeep, nKeepType,
|
||||
bKeepLine, nCompoundMinimalLeading );
|
||||
}
|
||||
return bAuto;
|
||||
}
|
||||
|
@@ -773,6 +773,7 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib,
|
||||
, mnHeightOfLastLine( 0 )
|
||||
, mnAdditionalFirstLineOffset( 0 )
|
||||
, mnOffset( 0 )
|
||||
, mnNoHyphOffset( COMPLETE_STRING )
|
||||
, mnCacheIndex( USHRT_MAX )
|
||||
, mbLocked( false )
|
||||
, mbWidow( false )
|
||||
|
@@ -469,6 +469,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
|
||||
|
||||
// hyphenation-keep: truncate a hyphenated line at the end of
|
||||
// the column, page or spread (but not more)
|
||||
// hyphenation-keep-line: disable hyphenation in the last line instead of truncating it
|
||||
int nExtraWidLines = 0;
|
||||
if( rLine.GetLineNr() >= m_nWidLines && pMaster->HasPara() &&
|
||||
( rLine.GetLineNr() == m_nWidLines || !rLine.GetCurr()->IsEndHyph() ) )
|
||||
@@ -478,6 +479,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
|
||||
const SvxHyphenZoneItem &rAttr = rSet.GetHyphenZone();
|
||||
|
||||
bool bKeep = rAttr.IsHyphen() && rAttr.IsKeep() && rAttr.GetKeepType();
|
||||
bool bKeepLine = bKeep && rAttr.IsKeepLine();
|
||||
|
||||
// if PAGE or SPREAD, allow hyphenation in the not last column or in the
|
||||
// not last linked frame on the same page
|
||||
@@ -499,7 +501,10 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
|
||||
|
||||
if ( bKeep && pMasterPara && pMasterPara->GetNext() )
|
||||
{
|
||||
// calculate the beginning of last hyphenated line
|
||||
TextFrameIndex nIdx(pMasterPara->GetLen());
|
||||
SwLineLayout * pNext = pMasterPara->GetNext();
|
||||
nIdx += pNext->GetLen();
|
||||
SwLineLayout * pCurr = pNext;
|
||||
SwLineLayout * pPrev = pNext;
|
||||
while ( pNext->GetNext() )
|
||||
@@ -507,21 +512,37 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
|
||||
pPrev = pCurr;
|
||||
pCurr = pNext;
|
||||
pNext = pNext->GetNext();
|
||||
nIdx += pNext->GetLen();
|
||||
}
|
||||
nIdx -= pNext->GetLen();
|
||||
// hyphenated line, but not the last remaining one
|
||||
if ( pNext->IsEndHyph() && !pNext->IsLastHyph() )
|
||||
// in the case of shifting full line (bKeepLine = false)
|
||||
if ( pNext->IsEndHyph() && ( bKeepLine || !pNext->IsLastHyph() ) )
|
||||
{
|
||||
nExtraWidLines = rLine.GetLineNr() - m_nWidLines + 1;
|
||||
// shift only a word: disable hyphenation in the line, if needed
|
||||
if ( bKeepLine && nExtraWidLines )
|
||||
{
|
||||
pMaster->SetNoHyphOffset(nIdx);
|
||||
nExtraWidLines = 0; // no need to shift the full line
|
||||
}
|
||||
// shift full line:
|
||||
// set remaining line to "last remaining hyphenated line"
|
||||
// to avoid truncating multiple hyphenated lines instead
|
||||
// of a single one
|
||||
if ( pCurr->IsEndHyph() )
|
||||
else if ( !bKeepLine && pCurr->IsEndHyph() )
|
||||
pCurr->SetLastHyph( true );
|
||||
// also unset the line before the remaining one
|
||||
// TODO: check also the line after the truncated line?
|
||||
if ( pPrev->IsLastHyph() )
|
||||
pPrev->SetLastHyph( false );
|
||||
}
|
||||
else if ( !pNext->IsEndHyph() && pMaster->GetNoHyphOffset() != nIdx )
|
||||
{
|
||||
// not hyphenated and not a last line with forbidden hyphenation:
|
||||
// enable hyphenation for all the lines in the TextFrame again
|
||||
pMaster->SetNoHyphOffset(TextFrameIndex(COMPLETE_STRING));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -123,6 +123,7 @@
|
||||
{ UNO_NAME_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE}, \
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP }, \
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP_TYPE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP_TYPE }, \
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP_LINE, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP_LINE }, \
|
||||
{ UNO_NAME_CHAR_AUTO_KERNING, RES_CHRATR_AUTOKERN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
|
||||
{ UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \
|
||||
{ UNO_NAME_CHAR_BACKGROUND_COMPLEX_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<css::util::XComplexColor>::get(), PROPERTY_NONE, MID_BACKGROUND_COMPLEX_COLOR },\
|
||||
@@ -493,6 +494,7 @@
|
||||
{ UNO_NAME_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE},\
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP},\
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP_TYPE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP_TYPE},\
|
||||
{ UNO_NAME_PARA_HYPHENATION_KEEP_LINE, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP_LINE},\
|
||||
{ UNO_NAME_NUMBERING_STYLE_NAME, RES_PARATR_NUMRULE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\
|
||||
{ UNO_NAME_NUMBERING_LEVEL, RES_PARATR_LIST_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0},\
|
||||
{ UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },\
|
||||
|
@@ -275,6 +275,7 @@ static OUString PropertyNametoRID(const OUString& rName)
|
||||
{ "ParaHyphenationZone", RID_PARA_HYPHENATION_ZONE },
|
||||
{ "ParaHyphenationKeep", RID_PARA_HYPHENATION_KEEP },
|
||||
{ "ParaHyphenationKeepType", RID_PARA_HYPHENATION_KEEP_TYPE },
|
||||
{ "ParaHyphenationKeepLine", RID_PARA_HYPHENATION_KEEP_LINE },
|
||||
{ "ParaInteropGrabBag", RID_PARA_INTEROP_GRAB_BAG },
|
||||
{ "ParaIsAutoFirstLineIndent", RID_PARA_IS_AUTO_FIRST_LINE_INDENT },
|
||||
{ "ParaIsCharacterDistance", RID_PARA_IS_CHARACTER_DISTANCE },
|
||||
|
@@ -102,6 +102,7 @@ const OUString & getPropertyName( PropertyIds eId )
|
||||
{ PROP_PARA_HYPHENATION_ZONE, u"ParaHyphenationZone"_ustr},
|
||||
{ PROP_PARA_HYPHENATION_KEEP, u"ParaHyphenationKeep"_ustr},
|
||||
{ PROP_PARA_HYPHENATION_KEEP_TYPE, u"ParaHyphenationKeepType"_ustr},
|
||||
{ PROP_PARA_HYPHENATION_KEEP_LINE, u"ParaHyphenationKeepLine"_ustr},
|
||||
{ PROP_PARA_LINE_NUMBER_COUNT, u"ParaLineNumberCount"_ustr},
|
||||
{ PROP_PARA_IS_HANGING_PUNCTUATION, u"ParaIsHangingPunctuation"_ustr},
|
||||
{ PROP_PARA_LINE_SPACING, u"ParaLineSpacing"_ustr},
|
||||
|
@@ -250,6 +250,7 @@ enum PropertyIds
|
||||
,PROP_PARA_HYPHENATION_ZONE
|
||||
,PROP_PARA_HYPHENATION_KEEP
|
||||
,PROP_PARA_HYPHENATION_KEEP_TYPE
|
||||
,PROP_PARA_HYPHENATION_KEEP_LINE
|
||||
,PROP_PARA_KEEP_TOGETHER
|
||||
,PROP_PARA_LAST_LINE_ADJUST
|
||||
,PROP_PARA_LEFT_MARGIN
|
||||
|
@@ -572,11 +572,20 @@ sal_Int16 SettingsTable::GetConsecutiveHyphenLimit() const
|
||||
}
|
||||
|
||||
bool SettingsTable::GetHyphenationKeep() const
|
||||
{
|
||||
// if allowHyphenationAtTrackBottom is false, also if it is not defined
|
||||
// (which means the same) hyphenation is not allowed in the last line, so
|
||||
// set ParaHyphenationKeep to TRUE, and set ParaHyphenationKeepType to COLUMN
|
||||
return m_pImpl->m_nAllowHyphenationAtTrackBottom != 1;
|
||||
}
|
||||
|
||||
bool SettingsTable::GetHyphenationKeepLine() const
|
||||
{
|
||||
// if allowHyphenationAtTrackBottom is not true and useWord2013TrackBottomHyphenation is
|
||||
// not present or it is true, set ParaHyphenationKeep to COLUMN
|
||||
return m_pImpl->m_nAllowHyphenationAtTrackBottom != 1 &&
|
||||
m_pImpl->m_nUseWord2013TrackBottomHyphenation != 0;
|
||||
// not present or it is false, shift only the hyphenated word to the next column or page,
|
||||
// not the full line, so set ParaHyphenationKeepLine to TRUE
|
||||
return GetHyphenationKeep() &&
|
||||
m_pImpl->m_nUseWord2013TrackBottomHyphenation != 1;
|
||||
}
|
||||
|
||||
const OUString & SettingsTable::GetDecimalSymbol() const
|
||||
@@ -746,6 +755,7 @@ void SettingsTable::ApplyProperties(rtl::Reference<SwXTextDocument> const& xDoc)
|
||||
{
|
||||
xDefault->setPropertyValue(u"ParaHyphenationKeep"_ustr, uno::Any(true));
|
||||
xDefault->setPropertyValue(u"ParaHyphenationKeepType"_ustr, uno::Any(text::ParagraphHyphenationKeepType::COLUMN));
|
||||
xDefault->setPropertyValue(u"ParaHyphenationKeepLine"_ustr, uno::Any(GetHyphenationKeepLine()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -81,6 +81,7 @@ public:
|
||||
sal_Int16 GetHyphenationZone() const;
|
||||
sal_Int16 GetConsecutiveHyphenLimit() const;
|
||||
bool GetHyphenationKeep() const;
|
||||
bool GetHyphenationKeepLine() const;
|
||||
|
||||
const OUString& GetDecimalSymbol() const;
|
||||
const OUString& GetListSeparator() const;
|
||||
|
Reference in New Issue
Block a user