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
)
40 , mpNaturalDXArray(nullptr)
41 , mpKashidaArray(nullptr)
45 if (mnFlags
& SalLayoutFlags::BiDiStrong
)
47 // handle strong BiDi mode
49 // do not bother to BiDi analyze strong LTR/RTL
50 // TODO: can we assume these strings do not have unicode control chars?
51 // if not remove the control characters from the runs
52 bool bRTL(mnFlags
& SalLayoutFlags::BiDiRtl
);
53 AddRun(mnMinCharPos
, mnEndCharPos
, bRTL
);
57 // handle weak BiDi mode
58 UBiDiLevel nLevel
= (mnFlags
& SalLayoutFlags::BiDiRtl
) ? 1 : 0;
60 // prepare substring for BiDi analysis
61 // TODO: reuse allocated pParaBidi
62 UErrorCode rcI18n
= U_ZERO_ERROR
;
63 const int nLength
= mnEndCharPos
- mnMinCharPos
;
64 UBiDi
* pParaBidi
= ubidi_openSized(nLength
, 0, &rcI18n
);
67 ubidi_setPara(pParaBidi
, reinterpret_cast<const UChar
*>(mrStr
.getStr()) + mnMinCharPos
,
68 nLength
, nLevel
, nullptr, &rcI18n
);
71 const int nRunCount
= ubidi_countRuns(pParaBidi
, &rcI18n
);
72 for (int i
= 0; i
< nRunCount
; ++i
)
74 int32_t nMinPos
, nRunLength
;
75 const UBiDiDirection nDir
= ubidi_getVisualRun(pParaBidi
, i
, &nMinPos
, &nRunLength
);
76 const int nPos0
= nMinPos
+ mnMinCharPos
;
77 const int nPos1
= nPos0
+ nRunLength
;
79 const bool bRTL
= (nDir
== UBIDI_RTL
);
80 AddRun(nPos0
, nPos1
, bRTL
);
83 // cleanup BiDi engine
84 ubidi_close(pParaBidi
);
87 // prepare calls to GetNextPos/GetNextRun
91 void ImplLayoutArgs::SetLayoutWidth(DeviceCoordinate nWidth
) { mnLayoutWidth
= nWidth
; }
93 void ImplLayoutArgs::SetNaturalDXArray(double const* pDXArray
) { mpNaturalDXArray
= pDXArray
; }
95 void ImplLayoutArgs::SetKashidaArray(sal_Bool
const* pKashidaArray
)
97 mpKashidaArray
= pKashidaArray
;
100 void ImplLayoutArgs::SetOrientation(Degree10 nOrientation
) { mnOrientation
= nOrientation
; }
102 void ImplLayoutArgs::ResetPos() { maRuns
.ResetPos(); }
104 bool ImplLayoutArgs::GetNextPos(int* nCharPos
, bool* bRTL
)
106 return maRuns
.GetNextPos(nCharPos
, bRTL
);
109 void ImplLayoutArgs::AddFallbackRun(int nMinRunPos
, int nEndRunPos
, bool bRTL
)
111 maFallbackRuns
.AddRun(nMinRunPos
, nEndRunPos
, bRTL
);
114 bool ImplLayoutArgs::HasFallbackRun() const { return !maFallbackRuns
.IsEmpty(); }
116 static bool IsControlChar(sal_UCS4 cChar
)
118 // C0 control characters
119 if ((0x0001 <= cChar
) && (cChar
<= 0x001F))
121 // formatting characters
122 if ((0x200E <= cChar
) && (cChar
<= 0x200F))
124 if ((0x2028 <= cChar
) && (cChar
<= 0x202E))
126 // deprecated formatting characters
127 if ((0x206A <= cChar
) && (cChar
<= 0x206F))
131 // byte order markers and invalid unicode
132 if ((cChar
== 0xFEFF) || (cChar
== 0xFFFE) || (cChar
== 0xFFFF))
134 // drop null character too, broken documents may contain it (ofz34898-1.doc)
140 // add a run after splitting it up to get rid of control chars
141 void ImplLayoutArgs::AddRun(int nCharPos0
, int nCharPos1
, bool bRTL
)
143 SAL_WARN_IF(nCharPos0
> nCharPos1
, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1");
145 // remove control characters from runs by splitting them up
148 for (int i
= nCharPos0
; i
< nCharPos1
; ++i
)
149 if (IsControlChar(mrStr
[i
]))
151 // add run until control char
152 maRuns
.AddRun(nCharPos0
, i
, bRTL
);
158 for (int i
= nCharPos1
; --i
>= nCharPos0
;)
159 if (IsControlChar(mrStr
[i
]))
161 // add run until control char
162 maRuns
.AddRun(i
+ 1, nCharPos1
, bRTL
);
167 // add remainder of run
168 maRuns
.AddRun(nCharPos0
, nCharPos1
, bRTL
);
171 bool ImplLayoutArgs::PrepareFallback(const SalLayoutGlyphsImpl
* pGlyphsImpl
)
173 // Generate runs with pre-calculated glyph items instead maFallbackRuns.
174 if (pGlyphsImpl
!= nullptr)
177 maFallbackRuns
.Clear();
179 for (auto const& aGlyphItem
: *pGlyphsImpl
)
181 for (int i
= aGlyphItem
.charPos(); i
< aGlyphItem
.charPos() + aGlyphItem
.charCount();
183 maRuns
.AddPos(i
, aGlyphItem
.IsRTLGlyph());
186 return !maRuns
.IsEmpty();
189 // short circuit if no fallback is needed
190 if (maFallbackRuns
.IsEmpty())
196 // convert the fallback requests to layout requests
200 // get the individual fallback requests
201 std::vector
<int> aPosVector
;
202 aPosVector
.reserve(mrStr
.getLength());
203 maFallbackRuns
.ResetPos();
204 for (; maFallbackRuns
.GetRun(&nMin
, &nEnd
, &bRTL
); maFallbackRuns
.NextRun())
205 for (int i
= nMin
; i
< nEnd
; ++i
)
206 aPosVector
.push_back(i
);
207 maFallbackRuns
.Clear();
209 // sort the individual fallback requests
210 std::sort(aPosVector
.begin(), aPosVector
.end());
212 // adjust fallback runs to have the same order and limits of the original runs
213 ImplLayoutRuns aNewRuns
;
215 for (; maRuns
.GetRun(&nMin
, &nEnd
, &bRTL
); maRuns
.NextRun())
219 auto it
= std::lower_bound(aPosVector
.begin(), aPosVector
.end(), nMin
);
220 for (; (it
!= aPosVector
.end()) && (*it
< nEnd
); ++it
)
221 aNewRuns
.AddPos(*it
, bRTL
);
225 auto it
= std::upper_bound(aPosVector
.begin(), aPosVector
.end(), nEnd
);
226 while ((it
!= aPosVector
.begin()) && (*--it
>= nMin
))
227 aNewRuns
.AddPos(*it
, bRTL
);
231 maRuns
= aNewRuns
; // TODO: use vector<>::swap()
236 bool ImplLayoutArgs::GetNextRun(int* nMinRunPos
, int* nEndRunPos
, bool* bRTL
)
238 bool bValid
= maRuns
.GetRun(nMinRunPos
, nEndRunPos
, bRTL
);
244 std::ostream
& operator<<(std::ostream
& s
, vcl::text::ImplLayoutArgs
const& rArgs
)
249 s
<< "ImplLayoutArgs{";
252 if (rArgs
.mnFlags
== SalLayoutFlags::NONE
)
256 bool need_or
= false;
259 if (rArgs.mnFlags & SalLayoutFlags::x) \
269 TEST(DisableKerning
);
272 TEST(DisableLigatures
);
278 const int nLength
= rArgs
.mrStr
.getLength();
280 s
<< ",Length=" << nLength
;
281 s
<< ",MinCharPos=" << rArgs
.mnMinCharPos
;
282 s
<< ",EndCharPos=" << rArgs
.mnEndCharPos
;
288 for (int i
= 0; i
< lim
; i
++)
290 if (rArgs
.mrStr
[i
] == '\n')
292 else if (rArgs
.mrStr
[i
] < ' ' || (rArgs
.mrStr
[i
] >= 0x7F && rArgs
.mrStr
[i
] <= 0xFF))
293 s
<< "\\0x" << std::hex
<< std::setw(2) << std::setfill('0')
294 << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
295 else if (rArgs
.mrStr
[i
] < 0x7F)
296 s
<< static_cast<char>(rArgs
.mrStr
[i
]);
298 s
<< "\\u" << std::hex
<< std::setw(4) << std::setfill('0')
299 << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
306 if (rArgs
.mpNaturalDXArray
)
309 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
313 for (int i
= 0; i
< lim
; i
++)
315 s
<< rArgs
.mpNaturalDXArray
[i
];
323 s
<< rArgs
.mpNaturalDXArray
[count
- 1];
330 s
<< ",KashidaArray=";
331 if (rArgs
.mpKashidaArray
)
334 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
338 for (int i
= 0; i
< lim
; i
++)
340 s
<< rArgs
.mpKashidaArray
[i
];
348 s
<< rArgs
.mpKashidaArray
[count
- 1];
355 s
<< ",LayoutWidth=" << rArgs
.mnLayoutWidth
;
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */