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 
28 namespace vcl::text
29 {
30 ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr, int nMinCharPos, int nEndCharPos,
31  SalLayoutFlags nFlags, const LanguageTag& rLanguageTag,
32  vcl::text::TextLayoutCache const* const pLayoutCache)
33  : maLanguageTag(rLanguageTag)
34  , mnFlags(nFlags)
35  , mrStr(rStr)
36  , mnMinCharPos(nMinCharPos)
37  , mnEndCharPos(nEndCharPos)
38  , m_pTextLayoutCache(pLayoutCache)
39  , mpDXArray(nullptr)
40  , mpAltNaturalDXArray(nullptr)
41  , mnLayoutWidth(0)
42  , mnOrientation(0)
43 {
45  {
46  // handle strong BiDi mode
47 
48  // do not bother to BiDi analyze strong LTR/RTL
49  // TODO: can we assume these strings do not have unicode control chars?
50  // if not remove the control characters from the runs
51  bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl);
53  }
54  else
55  {
56  // handle weak BiDi mode
57  UBiDiLevel nLevel = (mnFlags & SalLayoutFlags::BiDiRtl) ? 1 : 0;
58 
59  // prepare substring for BiDi analysis
60  // TODO: reuse allocated pParaBidi
61  UErrorCode rcI18n = U_ZERO_ERROR;
62  const int nLength = mrStr.getLength();
63  UBiDi* pParaBidi = ubidi_openSized(nLength, 0, &rcI18n);
64  if (!pParaBidi)
65  return;
66  ubidi_setPara(pParaBidi, reinterpret_cast<const UChar*>(mrStr.getStr()), nLength, nLevel,
67  nullptr, &rcI18n);
68 
69  UBiDi* pLineBidi = pParaBidi;
70  int nSubLength = mnEndCharPos - mnMinCharPos;
71  if (nSubLength != nLength)
72  {
73  pLineBidi = ubidi_openSized(nSubLength, 0, &rcI18n);
74  ubidi_setLine(pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n);
75  }
76 
77  // run BiDi algorithm
78  const int nRunCount = ubidi_countRuns(pLineBidi, &rcI18n);
79  //maRuns.resize( 2 * nRunCount );
80  for (int i = 0; i < nRunCount; ++i)
81  {
82  int32_t nMinPos, nRunLength;
83  const UBiDiDirection nDir = ubidi_getVisualRun(pLineBidi, i, &nMinPos, &nRunLength);
84  const int nPos0 = nMinPos + mnMinCharPos;
85  const int nPos1 = nPos0 + nRunLength;
86 
87  const bool bRTL = (nDir == UBIDI_RTL);
88  AddRun(nPos0, nPos1, bRTL);
89  }
90 
91  // cleanup BiDi engine
92  if (pLineBidi != pParaBidi)
93  ubidi_close(pLineBidi);
94  ubidi_close(pParaBidi);
95  }
96 
97  // prepare calls to GetNextPos/GetNextRun
98  maRuns.ResetPos();
99 }
100 
102 
103 void ImplLayoutArgs::SetDXArray(DeviceCoordinate const* pDXArray) { mpDXArray = pDXArray; }
104 
105 void ImplLayoutArgs::SetAltNaturalDXArray(double const* pDXArray)
106 {
107  mpAltNaturalDXArray = pDXArray;
108 }
109 
110 void ImplLayoutArgs::SetOrientation(Degree10 nOrientation) { mnOrientation = nOrientation; }
111 
113 
114 bool ImplLayoutArgs::GetNextPos(int* nCharPos, bool* bRTL)
115 {
116  return maRuns.GetNextPos(nCharPos, bRTL);
117 }
118 
119 void ImplLayoutArgs::AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL)
120 {
121  maFallbackRuns.AddRun(nMinRunPos, nEndRunPos, bRTL);
122 }
123 
125 
126 static bool IsControlChar(sal_UCS4 cChar)
127 {
128  // C0 control characters
129  if ((0x0001 <= cChar) && (cChar <= 0x001F))
130  return true;
131  // formatting characters
132  if ((0x200E <= cChar) && (cChar <= 0x200F))
133  return true;
134  if ((0x2028 <= cChar) && (cChar <= 0x202E))
135  return true;
136  // deprecated formatting characters
137  if ((0x206A <= cChar) && (cChar <= 0x206F))
138  return true;
139  if (0x2060 == cChar)
140  return true;
141  // byte order markers and invalid unicode
142  if ((cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF))
143  return true;
144  // drop null character too, broken documents may contain it (ofz34898-1.doc)
145  if (cChar == 0)
146  return true;
147  return false;
148 }
149 
150 // add a run after splitting it up to get rid of control chars
151 void ImplLayoutArgs::AddRun(int nCharPos0, int nCharPos1, bool bRTL)
152 {
153  SAL_WARN_IF(nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1");
154 
155  // remove control characters from runs by splitting them up
156  if (!bRTL)
157  {
158  for (int i = nCharPos0; i < nCharPos1; ++i)
159  if (IsControlChar(mrStr[i]))
160  {
161  // add run until control char
162  maRuns.AddRun(nCharPos0, i, bRTL);
163  nCharPos0 = i + 1;
164  }
165  }
166  else
167  {
168  for (int i = nCharPos1; --i >= nCharPos0;)
169  if (IsControlChar(mrStr[i]))
170  {
171  // add run until control char
172  maRuns.AddRun(i + 1, nCharPos1, bRTL);
173  nCharPos1 = i;
174  }
175  }
176 
177  // add remainder of run
178  maRuns.AddRun(nCharPos0, nCharPos1, bRTL);
179 }
180 
182 {
183  // Generate runs with pre-calculated glyph items instead maFallbackRuns.
184  if (pGlyphsImpl != nullptr)
185  {
186  maRuns.Clear();
188 
189  for (auto const& aGlyphItem : *pGlyphsImpl)
190  {
191  for (int i = aGlyphItem.charPos(); i < aGlyphItem.charPos() + aGlyphItem.charCount();
192  ++i)
193  maRuns.AddPos(i, aGlyphItem.IsRTLGlyph());
194  }
195 
196  return !maRuns.IsEmpty();
197  }
198 
199  // short circuit if no fallback is needed
200  if (maFallbackRuns.IsEmpty())
201  {
202  maRuns.Clear();
203  return false;
204  }
205 
206  // convert the fallback requests to layout requests
207  bool bRTL;
208  int nMin, nEnd;
209 
210  // get the individual fallback requests
211  std::vector<int> aPosVector;
212  aPosVector.reserve(mrStr.getLength());
214  for (; maFallbackRuns.GetRun(&nMin, &nEnd, &bRTL); maFallbackRuns.NextRun())
215  for (int i = nMin; i < nEnd; ++i)
216  aPosVector.push_back(i);
218 
219  // sort the individual fallback requests
220  std::sort(aPosVector.begin(), aPosVector.end());
221 
222  // adjust fallback runs to have the same order and limits of the original runs
223  ImplLayoutRuns aNewRuns;
224  maRuns.ResetPos();
225  for (; maRuns.GetRun(&nMin, &nEnd, &bRTL); maRuns.NextRun())
226  {
227  if (!bRTL)
228  {
229  auto it = std::lower_bound(aPosVector.begin(), aPosVector.end(), nMin);
230  for (; (it != aPosVector.end()) && (*it < nEnd); ++it)
231  aNewRuns.AddPos(*it, bRTL);
232  }
233  else
234  {
235  auto it = std::upper_bound(aPosVector.begin(), aPosVector.end(), nEnd);
236  while ((it != aPosVector.begin()) && (*--it >= nMin))
237  aNewRuns.AddPos(*it, bRTL);
238  }
239  }
240 
241  maRuns = aNewRuns; // TODO: use vector<>::swap()
242  maRuns.ResetPos();
243  return true;
244 }
245 
246 bool ImplLayoutArgs::GetNextRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL)
247 {
248  bool bValid = maRuns.GetRun(nMinRunPos, nEndRunPos, bRTL);
249  maRuns.NextRun();
250  return bValid;
251 }
252 }
253 
254 std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs)
255 {
256 #ifndef SAL_LOG_INFO
257  (void)rArgs;
258 #else
259  s << "ImplLayoutArgs{";
260 
261  s << "Flags=";
262  if (rArgs.mnFlags == SalLayoutFlags::NONE)
263  s << 0;
264  else
265  {
266  bool need_or = false;
267  s << "{";
268 #define TEST(x) \
269  if (rArgs.mnFlags & SalLayoutFlags::x) \
270  { \
271  if (need_or) \
272  s << "|"; \
273  s << #x; \
274  need_or = true; \
275  }
276  TEST(BiDiRtl);
277  TEST(BiDiStrong);
278  TEST(RightAlign);
281  TEST(Vertical);
283  TEST(ForFallback);
284 #undef TEST
285  s << "}";
286  }
287 
288  const int nLength = rArgs.mrStr.getLength();
289 
290  s << ",Length=" << nLength;
291  s << ",MinCharPos=" << rArgs.mnMinCharPos;
292  s << ",EndCharPos=" << rArgs.mnEndCharPos;
293 
294  s << ",Str=\"";
295  int lim = nLength;
296  if (lim > 10)
297  lim = 7;
298  for (int i = 0; i < lim; i++)
299  {
300  if (rArgs.mrStr[i] == '\n')
301  s << "\\n";
302  else if (rArgs.mrStr[i] < ' ' || (rArgs.mrStr[i] >= 0x7F && rArgs.mrStr[i] <= 0xFF))
303  s << "\\0x" << std::hex << std::setw(2) << std::setfill('0')
304  << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec;
305  else if (rArgs.mrStr[i] < 0x7F)
306  s << static_cast<char>(rArgs.mrStr[i]);
307  else
308  s << "\\u" << std::hex << std::setw(4) << std::setfill('0')
309  << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec;
310  }
311  if (nLength > lim)
312  s << "...";
313  s << "\"";
314 
315  s << ",DXArray=";
316  if (rArgs.mpDXArray || rArgs.mpAltNaturalDXArray)
317  {
318  s << "[";
319  int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
320  lim = count;
321  if (lim > 10)
322  lim = 7;
323  for (int i = 0; i < lim; i++)
324  {
325  if (rArgs.mpDXArray)
326  s << rArgs.mpDXArray[i];
327  else
328  s << rArgs.mpAltNaturalDXArray[i];
329  if (i < lim - 1)
330  s << ",";
331  }
332  if (count > lim)
333  {
334  if (count > lim + 1)
335  s << "...";
336  if (rArgs.mpDXArray)
337  s << rArgs.mpDXArray[count - 1];
338  else
339  s << rArgs.mpAltNaturalDXArray[count - 1];
340  }
341  s << "]";
342  }
343  else
344  s << "NULL";
345 
346  s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
347 
348  s << "}";
349 
350 #endif
351  return s;
352 }
353 
354 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ImplLayoutArgs(OUString const &rStr, int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, LanguageTag const &rLanguageTag, vcl::text::TextLayoutCache const *pLayoutCache)
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:193
std::basic_ostream< charT, traits > & operator<<(std::basic_ostream< charT, traits > &stream, const Region &rRegion)
Definition: region.hxx:136
sal_Int32 DeviceCoordinate
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
void SetLayoutWidth(DeviceCoordinate nWidth)
bool PrepareFallback(const SalLayoutGlyphsImpl *pGlyphsImpl)
void SetOrientation(Degree10 nOrientation)
const DeviceCoordinate * mpDXArray
FuncFlags mnFlags
void AddPos(int nCharPos, bool bRTL)
bool IsEmpty() const
TEST
int i
static bool IsControlChar(sal_UCS4 cChar)
bool GetNextPos(int *nCharPos, bool *bRTL)
const double * mpAltNaturalDXArray
double mnLayoutWidth
bool GetRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL) const
#define SAL_WARN_IF(condition, area, stream)
void AddRun(int nMinRunPos, int nEndRunPos, bool bRTL)
bool GetNextPos(int *nCharPos, bool *bRTL)
void SetAltNaturalDXArray(const double *pDXArray)
LanguageTag maLanguageTag
void AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL)
sal_Int32 nLength
void SetDXArray(const DeviceCoordinate *pDXArray)
DeviceCoordinate mnLayoutWidth
void AddRun(int nMinCharPos, int nEndCharPos, bool bRTL)
SalLayoutFlags
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo