1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #ifndef INCLUDED_SW_SOURCE_CORE_INC_SCRIPTINFO_HXX
21 #define INCLUDED_SW_SOURCE_CORE_INC_SCRIPTINFO_HXX
25 #include <unordered_set>
26 #include <rtl/ustrbuf.hxx>
27 #include <o3tl/typed_flags_set.hxx>
28 #include <i18nlangtag/lang.h>
29 #include <tools/long.hxx>
30 #include "TextFrameIndex.hxx"
37 enum class SwFontScript
;
38 namespace sw
{ struct MergedPara
; }
39 namespace sw::mark
{ class IBookmark
; }
41 #define SPACING_PRECISION_FACTOR 100
43 // encapsulates information about script changes
47 enum CompType
{ KANA
, SPECIAL_LEFT
, SPECIAL_RIGHT
, NONE
, SPECIAL_MIDDLE
};
48 enum class MarkKind
{ Start
= (1<<0), End
= (1<<1), Point
= (1<<2) };
51 //! Records a single change in script type.
52 struct ScriptChangeInfo
54 TextFrameIndex position
; //!< Character position at which we change script
55 sal_uInt8 type
; //!< Script type (Latin/Asian/Complex) that we change to.
56 ScriptChangeInfo(TextFrameIndex pos
, sal_uInt8 typ
) : position(pos
), type(typ
) {};
58 //TODO - This is sorted, so should probably be a std::set rather than vector.
59 // But we also use random access (probably unnecessarily).
60 std::vector
<ScriptChangeInfo
> m_ScriptChanges
;
61 //! Records a single change in direction.
62 struct DirectionChangeInfo
64 TextFrameIndex position
; //!< Character position at which we change direction.
65 sal_uInt8 type
; //!< Direction that we change to.
66 DirectionChangeInfo(TextFrameIndex pos
, sal_uInt8 typ
) : position(pos
), type(typ
) {};
68 std::vector
<DirectionChangeInfo
> m_DirectionChanges
;
69 std::deque
<TextFrameIndex
> m_Kashida
;
70 /// indexes into m_Kashida
71 std::unordered_set
<size_t> m_KashidaInvalid
;
72 std::deque
<TextFrameIndex
> m_NoKashidaLine
;
73 std::deque
<TextFrameIndex
> m_NoKashidaLineEnd
;
74 std::vector
<TextFrameIndex
> m_HiddenChg
;
75 std::vector
<std::tuple
<TextFrameIndex
, MarkKind
, Color
, OUString
>> m_Bookmarks
;
76 //! Records a single change in compression.
77 struct CompressionChangeInfo
79 TextFrameIndex position
; //!< Character position where the change occurs.
80 TextFrameIndex length
; //!< Length of the segment.
81 CompType type
; //!< Type of compression that we change to.
82 CompressionChangeInfo(TextFrameIndex pos
, TextFrameIndex len
, CompType typ
) : position(pos
), length(len
), type(typ
) {};
84 std::vector
<CompressionChangeInfo
> m_CompressionChanges
;
86 CompType
DbgCompType(const TextFrameIndex nPos
) const;
89 TextFrameIndex m_nInvalidityPos
;
90 sal_uInt8 m_nDefaultDir
;
92 void UpdateBidiInfo( const OUString
& rText
);
93 bool IsKashidaValid(size_t nKashPos
) const;
94 // returns true if nKashPos is newly marked invalid
95 bool MarkKashidaInvalid(size_t nKashPos
);
96 void ClearKashidaInvalid(size_t nKashPos
);
97 bool MarkOrClearKashidaInvalid(TextFrameIndex nStt
, TextFrameIndex nLen
,
98 bool bMark
, sal_Int32 nMarkCount
);
99 bool IsKashidaLine(TextFrameIndex nCharIdx
) const;
100 // examines the range [ nStart, nStart + nEnd ] if there are kanas
101 // returns start index of kana entry in array, otherwise SAL_MAX_SIZE
102 size_t HasKana(TextFrameIndex nStart
, TextFrameIndex nEnd
) const;
109 // determines script changes
110 void InitScriptInfo(const SwTextNode
& rNode
, sw::MergedPara
const* pMerged
, bool bRTL
);
111 void InitScriptInfo(const SwTextNode
& rNode
, sw::MergedPara
const* pMerged
);
113 // set/get position from which data is invalid
114 void SetInvalidityA(const TextFrameIndex nPos
)
116 if (nPos
< m_nInvalidityPos
)
117 m_nInvalidityPos
= nPos
;
119 TextFrameIndex
GetInvalidityA() const
121 return m_nInvalidityPos
;
124 // get default direction for paragraph
125 sal_uInt8
GetDefaultDir() const { return m_nDefaultDir
; };
127 // array operations, nCnt refers to array position
128 size_t CountScriptChg() const { return m_ScriptChanges
.size(); }
129 TextFrameIndex
GetScriptChg(const size_t nCnt
) const
131 assert(nCnt
< m_ScriptChanges
.size());
132 return m_ScriptChanges
[nCnt
].position
;
134 sal_uInt8
GetScriptType( const size_t nCnt
) const
136 assert( nCnt
< m_ScriptChanges
.size());
137 return m_ScriptChanges
[nCnt
].type
;
140 size_t CountDirChg() const { return m_DirectionChanges
.size(); }
141 TextFrameIndex
GetDirChg(const size_t nCnt
) const
143 assert(nCnt
< m_DirectionChanges
.size());
144 return m_DirectionChanges
[ nCnt
].position
;
146 sal_uInt8
GetDirType( const size_t nCnt
) const
148 assert(nCnt
< m_DirectionChanges
.size());
149 return m_DirectionChanges
[ nCnt
].type
;
152 size_t CountKashida() const
154 return m_Kashida
.size();
157 TextFrameIndex
GetKashida(const size_t nCnt
) const
159 assert(nCnt
< m_Kashida
.size());
160 return m_Kashida
[nCnt
];
163 size_t CountCompChg() const { return m_CompressionChanges
.size(); };
164 TextFrameIndex
GetCompStart(const size_t nCnt
) const
166 assert(nCnt
< m_CompressionChanges
.size());
167 return m_CompressionChanges
[ nCnt
].position
;
169 TextFrameIndex
GetCompLen(const size_t nCnt
) const
171 assert(nCnt
< m_CompressionChanges
.size());
172 return m_CompressionChanges
[ nCnt
].length
;
174 CompType
GetCompType( const size_t nCnt
) const
176 assert(nCnt
< m_CompressionChanges
.size());
177 return m_CompressionChanges
[ nCnt
].type
;
180 size_t CountHiddenChg() const { return m_HiddenChg
.size(); };
181 TextFrameIndex
GetHiddenChg(const size_t nCnt
) const
183 assert(nCnt
< m_HiddenChg
.size());
184 return m_HiddenChg
[ nCnt
];
186 TextFrameIndex
NextHiddenChg(TextFrameIndex nPos
) const;
187 TextFrameIndex
NextBookmark(TextFrameIndex nPos
) const;
188 std::vector
<std::tuple
<MarkKind
, Color
, OUString
>>
189 GetBookmarks(TextFrameIndex
const nPos
);
190 static void CalcHiddenRanges(const SwTextNode
& rNode
,
191 MultiSelection
& rHiddenMulti
,
192 std::vector
<std::pair
<sw::mark::IBookmark
const*, MarkKind
>> * pBookmarks
);
193 static void selectHiddenTextProperty(const SwTextNode
& rNode
,
194 MultiSelection
&rHiddenMulti
,
195 std::vector
<std::pair
<sw::mark::IBookmark
const*, MarkKind
>> * pBookmarks
);
196 static void selectRedLineDeleted(const SwTextNode
& rNode
, MultiSelection
&rHiddenMulti
, bool bSelect
=true);
198 // "high" level operations, nPos refers to string position
199 TextFrameIndex
NextScriptChg(TextFrameIndex nPos
) const;
200 sal_Int16
ScriptType(const TextFrameIndex nPos
) const;
202 // Returns the position of the next direction level change.
203 // If bLevel is set, the position of the next level which is smaller
204 // than the level at position nPos is returned. This is required to
205 // obtain the end of a SwBidiPortion
206 TextFrameIndex
NextDirChg(const TextFrameIndex nPos
,
207 const sal_uInt8
* pLevel
= nullptr) const;
208 sal_uInt8
DirType(const TextFrameIndex nPos
) const;
210 // HIDDEN TEXT STUFF START
212 /** Hidden text range information - static and non-version
214 @descr Determines if a given position is inside a hidden text range. The
215 static version tries to obtain a valid SwScriptInfo object
216 via the SwTextNode, otherwise it calculates the values from scratch.
217 The non-static version uses the internally cached information
223 The given position that should be checked.
225 Return parameter for the start position of the hidden range.
226 COMPLETE_STRING if nPos is not inside a hidden range.
228 Return parameter for the end position of the hidden range.
229 0 if nPos is not inside a hidden range.
231 Return parameter that contains all the hidden text ranges. Optional.
233 returns true if there are any hidden characters in this paragraph.
236 static bool GetBoundsOfHiddenRange( const SwTextNode
& rNode
, sal_Int32 nPos
,
237 sal_Int32
& rnStartPos
, sal_Int32
& rnEndPos
,
238 std::vector
<sal_Int32
>* pList
= nullptr );
239 bool GetBoundsOfHiddenRange(TextFrameIndex nPos
, TextFrameIndex
& rnStartPos
,
240 TextFrameIndex
& rnEndPos
) const;
242 static bool IsInHiddenRange( const SwTextNode
& rNode
, sal_Int32 nPos
);
244 /** Hidden text attribute handling
246 @descr Takes a string and either deletes the hidden ranges or sets
247 a given character in place of the hidden characters.
252 The string to modify.
254 The character that should replace the hidden characters.
256 If set, the hidden ranges will be deleted from the text node.
258 static sal_Int32
MaskHiddenRanges(
259 const SwTextNode
& rNode
, OUStringBuffer
& rText
,
260 const sal_Int32 nStt
, const sal_Int32 nEnd
,
261 const sal_Unicode cChar
);
263 /** Hidden text attribute handling
265 @descr Takes a SwTextNode and deletes the hidden ranges from the node.
270 static void DeleteHiddenRanges( SwTextNode
& rNode
);
272 // HIDDEN TEXT STUFF END
274 // modifies the kerning array according to a given compress value
275 tools::Long
Compress( KernArray
& rKernArray
, TextFrameIndex nIdx
, TextFrameIndex nLen
,
276 const sal_uInt16 nCompress
, const sal_uInt16 nFontHeight
,
277 const bool bCentered
,
278 Point
* pPoint
= nullptr ) const;
280 /** Performs a kashida justification on the kerning array
282 @descr Add some extra space for kashida justification to the
283 positions in the kerning array.
285 The printers kerning array. Optional.
287 Start referring to the paragraph.
289 The number of characters to be considered.
291 The value which has to be added to a kashida opportunity.
292 @return The number of kashida opportunities in the given range
294 sal_Int32
KashidaJustify( KernArray
* pKernArray
, sal_Bool
* pKashidaArray
,
295 TextFrameIndex nStt
, TextFrameIndex nLen
, tools::Long nSpaceAdd
= 0) const;
297 /** Clears array of kashidas marked as invalid
299 void ClearKashidaInvalid(TextFrameIndex
const nStt
, TextFrameIndex
const nLen
)
301 MarkOrClearKashidaInvalid(nStt
, nLen
, false, 0);
304 /** Marks nCnt kashida positions as invalid
305 pKashidaPositions: array of char indices relative to the paragraph
307 void MarkKashidasInvalid(sal_Int32 nCnt
, const TextFrameIndex
* pKashidaPositions
);
309 /** Marks nCnt kashida positions as invalid
310 in the given text range
312 bool MarkKashidasInvalid(sal_Int32
const nCnt
,
313 TextFrameIndex
const nStt
, TextFrameIndex
const nLen
)
315 return MarkOrClearKashidaInvalid(nStt
, nLen
, true, nCnt
);
318 /** retrieves kashida opportunities for a given text range.
320 rKashidaPositions: buffer to receive the char indices of the
321 kashida opportunities relative to the paragraph
323 void GetKashidaPositions(TextFrameIndex nStt
, TextFrameIndex nLen
,
324 std::vector
<TextFrameIndex
>& rKashidaPosition
);
326 /** Use regular blank justification instead of kashdida justification for the given line of text.
327 nStt Start char index of the line referring to the paragraph.
328 nLen Number of characters in the line
330 void SetNoKashidaLine(TextFrameIndex nStt
, TextFrameIndex nLen
);
332 /** Clear forced blank justification for a given line.
333 nStt Start char index of the line referring to the paragraph.
334 nLen Number of characters in the line
336 void ClearNoKashidaLine(TextFrameIndex nStt
, TextFrameIndex nLen
);
338 /** Checks if text is Arabic text.
340 @descr Checks if text is Arabic text.
344 Start index of the text
345 @return Returns if the language is an Arabic language
347 static bool IsArabicText(const OUString
& rText
, TextFrameIndex nStt
, TextFrameIndex nLen
);
349 /** Performs a thai justification on the kerning array
351 @descr Add some extra space for thai justification to the
352 positions in the kerning array.
356 The printers kerning array. Optional.
358 Start referring to the paragraph.
360 The number of characters to be considered.
362 The value which has to be added to the cells.
363 @return The number of extra spaces in the given range
365 static TextFrameIndex
ThaiJustify( std::u16string_view aText
, KernArray
* pKernArray
,
368 TextFrameIndex nNumberOfBlanks
= TextFrameIndex(0),
369 tools::Long nSpaceAdd
= 0 );
371 static TextFrameIndex
CountCJKCharacters(const OUString
&rText
,
372 TextFrameIndex nPos
, TextFrameIndex nEnd
, LanguageType aLang
);
374 static void CJKJustify( const OUString
& rText
, KernArray
& rKernArray
,
376 TextFrameIndex nLen
, LanguageType aLang
,
377 tools::Long nSpaceAdd
, bool bIsSpaceStop
);
379 /// return a frame for the node, ScriptInfo is its member...
380 /// (many clients need both frame and SI, and both have to match)
381 static SwScriptInfo
* GetScriptInfo( const SwTextNode
& rNode
,
382 SwTextFrame
const** o_pFrame
= nullptr,
383 bool bAllowInvalid
= false);
385 SwFontScript
WhichFont(TextFrameIndex nIdx
) const;
386 static SwFontScript
WhichFont(sal_Int32 nIdx
, OUString
const & rText
);
392 template<> struct typed_flags
<SwScriptInfo::MarkKind
> : is_typed_flags
<SwScriptInfo::MarkKind
, 0x07> {};
398 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */