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 #include <ImplLayoutArgs.hxx>
22 #include <unicode/ubidi.h>
23 #include <unicode/uchar.h>
31 ImplLayoutArgs::ImplLayoutArgs(const OUString
& rStr
, int nMinCharPos
, int nEndCharPos
,
32 SalLayoutFlags nFlags
, LanguageTag aLanguageTag
,
33 vcl::text::TextLayoutCache
const* const pLayoutCache
)
34 : maLanguageTag(std::move(aLanguageTag
))
37 , mnMinCharPos(nMinCharPos
)
38 , mnEndCharPos(nEndCharPos
)
39 , m_pTextLayoutCache(pLayoutCache
)
43 if (mnFlags
& SalLayoutFlags::BiDiStrong
)
45 // handle strong BiDi mode
47 // do not bother to BiDi analyze strong LTR/RTL
48 // TODO: can we assume these strings do not have unicode control chars?
49 // if not remove the control characters from the runs
50 bool bRTL(mnFlags
& SalLayoutFlags::BiDiRtl
);
51 AddRun(mnMinCharPos
, mnEndCharPos
, bRTL
);
55 // handle weak BiDi mode
56 UBiDiLevel nLevel
= (mnFlags
& SalLayoutFlags::BiDiRtl
) ? 1 : 0;
58 // prepare substring for BiDi analysis
59 // TODO: reuse allocated pParaBidi
60 UErrorCode rcI18n
= U_ZERO_ERROR
;
61 const int nLength
= mnEndCharPos
- mnMinCharPos
;
62 UBiDi
* pParaBidi
= ubidi_openSized(nLength
, 0, &rcI18n
);
65 ubidi_setPara(pParaBidi
, reinterpret_cast<const UChar
*>(mrStr
.getStr()) + mnMinCharPos
,
66 nLength
, nLevel
, nullptr, &rcI18n
);
69 const int nRunCount
= ubidi_countRuns(pParaBidi
, &rcI18n
);
70 for (int i
= 0; i
< nRunCount
; ++i
)
72 int32_t nMinPos
, nRunLength
;
73 const UBiDiDirection nDir
= ubidi_getVisualRun(pParaBidi
, i
, &nMinPos
, &nRunLength
);
74 const int nPos0
= nMinPos
+ mnMinCharPos
;
75 const int nPos1
= nPos0
+ nRunLength
;
77 const bool bRTL
= (nDir
== UBIDI_RTL
);
78 AddRun(nPos0
, nPos1
, bRTL
);
81 // cleanup BiDi engine
82 ubidi_close(pParaBidi
);
85 // prepare calls to GetNextPos/GetNextRun
89 void ImplLayoutArgs::SetLayoutWidth(double nWidth
) { mnLayoutWidth
= nWidth
; }
91 void ImplLayoutArgs::SetJustificationData(JustificationData stJustification
)
93 mstJustification
= std::move(stJustification
);
96 void ImplLayoutArgs::SetOrientation(Degree10 nOrientation
) { mnOrientation
= nOrientation
; }
98 void ImplLayoutArgs::ResetPos() { maRuns
.ResetPos(); }
100 bool ImplLayoutArgs::GetNextPos(int* nCharPos
, bool* bRTL
)
102 return maRuns
.GetNextPos(nCharPos
, bRTL
);
105 void ImplLayoutArgs::AddFallbackRun(int nMinRunPos
, int nEndRunPos
, bool bRTL
)
107 maFallbackRuns
.AddRun(nMinRunPos
, nEndRunPos
, bRTL
);
110 bool ImplLayoutArgs::HasFallbackRun() const { return !maFallbackRuns
.IsEmpty(); }
112 static bool IsControlChar(sal_UCS4 cChar
)
114 // C0 control characters
115 if ((0x0001 <= cChar
) && (cChar
<= 0x001F))
117 // formatting characters
118 if ((0x200E <= cChar
) && (cChar
<= 0x200F))
120 if ((0x2028 <= cChar
) && (cChar
<= 0x202E))
122 // deprecated formatting characters
123 if ((0x206A <= cChar
) && (cChar
<= 0x206F))
127 // byte order markers and invalid unicode
128 if ((cChar
== 0xFEFF) || (cChar
== 0xFFFE) || (cChar
== 0xFFFF))
130 // drop null character too, broken documents may contain it (ofz34898-1.doc)
136 // add a run after splitting it up to get rid of control chars
137 void ImplLayoutArgs::AddRun(int nCharPos0
, int nCharPos1
, bool bRTL
)
139 SAL_WARN_IF(nCharPos0
> nCharPos1
, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1");
141 // remove control characters from runs by splitting them up
144 for (int i
= nCharPos0
; i
< nCharPos1
; ++i
)
145 if (IsControlChar(mrStr
[i
]))
147 // add run until control char
148 maRuns
.AddRun(nCharPos0
, i
, bRTL
);
154 for (int i
= nCharPos1
; --i
>= nCharPos0
;)
155 if (IsControlChar(mrStr
[i
]))
157 // add run until control char
158 maRuns
.AddRun(i
+ 1, nCharPos1
, bRTL
);
163 // add remainder of run
164 maRuns
.AddRun(nCharPos0
, nCharPos1
, bRTL
);
167 bool ImplLayoutArgs::PrepareFallback(const SalLayoutGlyphsImpl
* pGlyphsImpl
)
169 // Generate runs with pre-calculated glyph items instead maFallbackRuns.
170 if (pGlyphsImpl
!= nullptr)
173 maFallbackRuns
.Clear();
175 for (auto const& aGlyphItem
: *pGlyphsImpl
)
177 for (int i
= aGlyphItem
.charPos(); i
< aGlyphItem
.charPos() + aGlyphItem
.charCount();
179 maRuns
.AddPos(i
, aGlyphItem
.IsRTLGlyph());
182 return !maRuns
.IsEmpty();
185 // short circuit if no fallback is needed
186 if (maFallbackRuns
.IsEmpty())
192 ImplLayoutRuns::PrepareFallbackRuns(&maRuns
, &maFallbackRuns
);
197 bool ImplLayoutArgs::GetNextRun(int* nMinRunPos
, int* nEndRunPos
, bool* bRTL
)
199 bool bValid
= maRuns
.GetRun(nMinRunPos
, nEndRunPos
, bRTL
);
205 std::ostream
& operator<<(std::ostream
& s
, vcl::text::ImplLayoutArgs
const& rArgs
)
210 s
<< "ImplLayoutArgs{";
213 if (rArgs
.mnFlags
== SalLayoutFlags::NONE
)
217 bool need_or
= false;
220 if (rArgs.mnFlags & SalLayoutFlags::x) \
230 TEST(DisableKerning
);
233 TEST(DisableLigatures
);
239 const int nLength
= rArgs
.mrStr
.getLength();
241 s
<< ",Length=" << nLength
;
242 s
<< ",MinCharPos=" << rArgs
.mnMinCharPos
;
243 s
<< ",EndCharPos=" << rArgs
.mnEndCharPos
;
249 for (int i
= 0; i
< lim
; i
++)
251 if (rArgs
.mrStr
[i
] == '\n')
253 else if (rArgs
.mrStr
[i
] < ' ' || (rArgs
.mrStr
[i
] >= 0x7F && rArgs
.mrStr
[i
] <= 0xFF))
254 s
<< "\\0x" << std::hex
<< std::setw(2) << std::setfill('0')
255 << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
256 else if (rArgs
.mrStr
[i
] < 0x7F)
257 s
<< static_cast<char>(rArgs
.mrStr
[i
]);
259 s
<< "\\u" << std::hex
<< std::setw(4) << std::setfill('0')
260 << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
267 if (!rArgs
.mstJustification
.empty())
270 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
274 for (int i
= 0; i
< lim
; i
++)
276 s
<< rArgs
.mstJustification
.GetTotalAdvance(rArgs
.mnMinCharPos
+ i
);
284 s
<< rArgs
.mstJustification
.GetTotalAdvance(rArgs
.mnMinCharPos
+ count
- 1);
291 s
<< ",KashidaArray=";
292 if (!rArgs
.mstJustification
.empty() && rArgs
.mstJustification
.ContainsKashidaPositions())
295 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
299 for (int i
= 0; i
< lim
; i
++)
301 s
<< rArgs
.mstJustification
.GetPositionHasKashida(rArgs
.mnMinCharPos
+ i
)
310 s
<< rArgs
.mstJustification
.GetPositionHasKashida(rArgs
.mnMinCharPos
+ count
- 1)
318 s
<< ",LayoutWidth=" << rArgs
.mnLayoutWidth
;
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */