LibreOffice Module vcl (master) 1
ImplLayoutArgs.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <ImplLayoutArgs.hxx>
21
22#include <unicode/ubidi.h>
23#include <unicode/uchar.h>
24
25#include <algorithm>
26#include <memory>
27#include <utility>
28
29namespace vcl::text
30{
31ImplLayoutArgs::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))
35 , mnFlags(nFlags)
36 , mrStr(rStr)
37 , mnMinCharPos(nMinCharPos)
38 , mnEndCharPos(nEndCharPos)
39 , m_pTextLayoutCache(pLayoutCache)
40 , mpNaturalDXArray(nullptr)
41 , mpKashidaArray(nullptr)
42 , mnLayoutWidth(0)
43 , mnOrientation(0)
44{
46 {
47 // handle strong BiDi mode
48
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
54 }
55 else
56 {
57 // handle weak BiDi mode
58 UBiDiLevel nLevel = (mnFlags & SalLayoutFlags::BiDiRtl) ? 1 : 0;
59
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);
65 if (!pParaBidi)
66 return;
67 ubidi_setPara(pParaBidi, reinterpret_cast<const UChar*>(mrStr.getStr()) + mnMinCharPos,
68 nLength, nLevel, nullptr, &rcI18n);
69
70 // run BiDi algorithm
71 const int nRunCount = ubidi_countRuns(pParaBidi, &rcI18n);
72 for (int i = 0; i < nRunCount; ++i)
73 {
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;
78
79 const bool bRTL = (nDir == UBIDI_RTL);
80 AddRun(nPos0, nPos1, bRTL);
81 }
82
83 // cleanup BiDi engine
84 ubidi_close(pParaBidi);
85 }
86
87 // prepare calls to GetNextPos/GetNextRun
89}
90
92
93void ImplLayoutArgs::SetNaturalDXArray(double const* pDXArray) { mpNaturalDXArray = pDXArray; }
94
95void ImplLayoutArgs::SetKashidaArray(sal_Bool const* pKashidaArray)
96{
97 mpKashidaArray = pKashidaArray;
98}
99
100void ImplLayoutArgs::SetOrientation(Degree10 nOrientation) { mnOrientation = nOrientation; }
101
103
104bool ImplLayoutArgs::GetNextPos(int* nCharPos, bool* bRTL)
105{
106 return maRuns.GetNextPos(nCharPos, bRTL);
107}
108
109void ImplLayoutArgs::AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL)
110{
111 maFallbackRuns.AddRun(nMinRunPos, nEndRunPos, bRTL);
112}
113
115
116static bool IsControlChar(sal_UCS4 cChar)
117{
118 // C0 control characters
119 if ((0x0001 <= cChar) && (cChar <= 0x001F))
120 return true;
121 // formatting characters
122 if ((0x200E <= cChar) && (cChar <= 0x200F))
123 return true;
124 if ((0x2028 <= cChar) && (cChar <= 0x202E))
125 return true;
126 // deprecated formatting characters
127 if ((0x206A <= cChar) && (cChar <= 0x206F))
128 return true;
129 if (0x2060 == cChar)
130 return true;
131 // byte order markers and invalid unicode
132 if ((cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF))
133 return true;
134 // drop null character too, broken documents may contain it (ofz34898-1.doc)
135 if (cChar == 0)
136 return true;
137 return false;
138}
139
140// add a run after splitting it up to get rid of control chars
141void ImplLayoutArgs::AddRun(int nCharPos0, int nCharPos1, bool bRTL)
142{
143 SAL_WARN_IF(nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1");
144
145 // remove control characters from runs by splitting them up
146 if (!bRTL)
147 {
148 for (int i = nCharPos0; i < nCharPos1; ++i)
149 if (IsControlChar(mrStr[i]))
150 {
151 // add run until control char
152 maRuns.AddRun(nCharPos0, i, bRTL);
153 nCharPos0 = i + 1;
154 }
155 }
156 else
157 {
158 for (int i = nCharPos1; --i >= nCharPos0;)
159 if (IsControlChar(mrStr[i]))
160 {
161 // add run until control char
162 maRuns.AddRun(i + 1, nCharPos1, bRTL);
163 nCharPos1 = i;
164 }
165 }
166
167 // add remainder of run
168 maRuns.AddRun(nCharPos0, nCharPos1, bRTL);
169}
170
172{
173 // Generate runs with pre-calculated glyph items instead maFallbackRuns.
174 if (pGlyphsImpl != nullptr)
175 {
176 maRuns.Clear();
178
179 for (auto const& aGlyphItem : *pGlyphsImpl)
180 {
181 for (int i = aGlyphItem.charPos(); i < aGlyphItem.charPos() + aGlyphItem.charCount();
182 ++i)
183 maRuns.AddPos(i, aGlyphItem.IsRTLGlyph());
184 }
185
186 return !maRuns.IsEmpty();
187 }
188
189 // short circuit if no fallback is needed
191 {
192 maRuns.Clear();
193 return false;
194 }
195
196 // convert the fallback requests to layout requests
197 bool bRTL;
198 int nMin, nEnd;
199
200 // get the individual fallback requests
201 std::vector<int> aPosVector;
202 aPosVector.reserve(mrStr.getLength());
204 for (; maFallbackRuns.GetRun(&nMin, &nEnd, &bRTL); maFallbackRuns.NextRun())
205 for (int i = nMin; i < nEnd; ++i)
206 aPosVector.push_back(i);
208
209 // sort the individual fallback requests
210 std::sort(aPosVector.begin(), aPosVector.end());
211
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())
216 {
217 if (!bRTL)
218 {
219 auto it = std::lower_bound(aPosVector.begin(), aPosVector.end(), nMin);
220 for (; (it != aPosVector.end()) && (*it < nEnd); ++it)
221 aNewRuns.AddPos(*it, bRTL);
222 }
223 else
224 {
225 auto it = std::upper_bound(aPosVector.begin(), aPosVector.end(), nEnd);
226 while ((it != aPosVector.begin()) && (*--it >= nMin))
227 aNewRuns.AddPos(*it, bRTL);
228 }
229 }
230
231 maRuns = aNewRuns; // TODO: use vector<>::swap()
233 return true;
234}
235
236bool ImplLayoutArgs::GetNextRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL)
237{
238 bool bValid = maRuns.GetRun(nMinRunPos, nEndRunPos, bRTL);
239 maRuns.NextRun();
240 return bValid;
241}
242}
243
244std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs)
245{
246#ifndef SAL_LOG_INFO
247 (void)rArgs;
248#else
249 s << "ImplLayoutArgs{";
250
251 s << "Flags=";
252 if (rArgs.mnFlags == SalLayoutFlags::NONE)
253 s << 0;
254 else
255 {
256 bool need_or = false;
257 s << "{";
258#define TEST(x) \
259 if (rArgs.mnFlags & SalLayoutFlags::x) \
260 { \
261 if (need_or) \
262 s << "|"; \
263 s << #x; \
264 need_or = true; \
265 }
266 TEST(BiDiRtl);
271 TEST(Vertical);
274#undef TEST
275 s << "}";
276 }
277
278 const int nLength = rArgs.mrStr.getLength();
279
280 s << ",Length=" << nLength;
281 s << ",MinCharPos=" << rArgs.mnMinCharPos;
282 s << ",EndCharPos=" << rArgs.mnEndCharPos;
283
284 s << ",Str=\"";
285 int lim = nLength;
286 if (lim > 10)
287 lim = 7;
288 for (int i = 0; i < lim; i++)
289 {
290 if (rArgs.mrStr[i] == '\n')
291 s << "\\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]);
297 else
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;
300 }
301 if (nLength > lim)
302 s << "...";
303 s << "\"";
304
305 s << ",DXArray=";
306 if (rArgs.mpNaturalDXArray)
307 {
308 s << "[";
309 int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
310 lim = count;
311 if (lim > 10)
312 lim = 7;
313 for (int i = 0; i < lim; i++)
314 {
315 s << rArgs.mpNaturalDXArray[i];
316 if (i < lim - 1)
317 s << ",";
318 }
319 if (count > lim)
320 {
321 if (count > lim + 1)
322 s << "...";
323 s << rArgs.mpNaturalDXArray[count - 1];
324 }
325 s << "]";
326 }
327 else
328 s << "NULL";
329
330 s << ",KashidaArray=";
331 if (rArgs.mpKashidaArray)
332 {
333 s << "[";
334 int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
335 lim = count;
336 if (lim > 10)
337 lim = 7;
338 for (int i = 0; i < lim; i++)
339 {
340 s << rArgs.mpKashidaArray[i];
341 if (i < lim - 1)
342 s << ",";
343 }
344 if (count > lim)
345 {
346 if (count > lim + 1)
347 s << "...";
348 s << rArgs.mpKashidaArray[count - 1];
349 }
350 s << "]";
351 }
352 else
353 s << "NULL";
354
355 s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
356
357 s << "}";
358
359#endif
360 return s;
361}
362
363/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SalLayoutFlags
void AddPos(int nCharPos, bool bRTL)
bool IsEmpty() const
bool GetNextPos(int *nCharPos, bool *bRTL)
bool GetRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL) const
void AddRun(int nMinRunPos, int nEndRunPos, bool bRTL)
bool GetNextPos(int *nCharPos, bool *bRTL)
void SetOrientation(Degree10 nOrientation)
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
void AddRun(int nMinCharPos, int nEndCharPos, bool bRTL)
ImplLayoutArgs(OUString const &rStr, int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, LanguageTag aLanguageTag, vcl::text::TextLayoutCache const *pLayoutCache)
void SetKashidaArray(const sal_Bool *pKashidaArray)
void SetLayoutWidth(DeviceCoordinate nWidth)
DeviceCoordinate mnLayoutWidth
const sal_Bool * mpKashidaArray
const double * mpNaturalDXArray
bool PrepareFallback(const SalLayoutGlyphsImpl *pGlyphsImpl)
void SetNaturalDXArray(const double *pDXArray)
void AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL)
sal_Int32 DeviceCoordinate
FuncFlags mnFlags
#define SAL_WARN_IF(condition, area, stream)
LanguageTag maLanguageTag
int i
TEST
static bool IsControlChar(sal_UCS4 cChar)
std::basic_ostream< charT, traits > & operator<<(std::basic_ostream< charT, traits > &stream, const Region &rRegion)
Definition: region.hxx:136
double mnLayoutWidth
unsigned char sal_Bool
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:172
sal_Int32 nLength