Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
BiDiLayoutEngine.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_BiDiLayoutEngine_h_
5#define _Stroika_Frameworks_Led_BiDiLayoutEngine_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <compare>
10#include <memory>
11
13
14/*
15@MODULE: BiDiLayout
16@DESCRIPTION: <p>Code to break up a string to be displayed - which might contain right-to-left text mixed
17 with left-to-right text into logical and display text runs according to the UNICODE Bidirectional
18 editing algorithm.
19 </p>
20
21 */
22
23#include "Stroika/Frameworks/Led/GDI.h"
24#include "Stroika/Frameworks/Led/Support.h"
25
26namespace Stroika::Frameworks::Led {
27
28/*
29 @CONFIGVAR: qUseFriBidi
30 @DESCRIPTION: <p>Defines whether or not we use the FriBidi package to generate text layouts for bidirectional text
31 such as Hebrew or Arabic. This defaults to off, because it requires linking with an extra library.</p>
32 <p>Note that if BOTH @'qUseFriBidi' and @'qTryToUseUNISCRIBEForTextRuns' are defined, then
33 @'qUseFriBidi' will take precedence.</p>
34 */
35#ifndef qUseFriBidi
36#define qUseFriBidi 0
37#endif
38
39/*
40 @CONFIGVAR: qTryToUseUNISCRIBEForTextRuns
41 @DESCRIPTION: <p>Defines whether or not we try to use the UNISCRIBE Windows SDK to generate text layouts for bidirectional text
42 such as Hebrew or Arabic. This defaults to true iff @'qStroika_Foundation_Common_Platform_Windows' is true.
43 Note that UNISCRIBE isn't necessarily available on a particular system. This code just tries to use
44 UNISCRIBE if its available. So - you MAY want to use this option TOGETHER with @'qUseFriBidi'.</p>
45 <p>Note that if BOTH @'qUseFriBidi' and @'qTryToUseUNISCRIBEForTextRuns' are defined, then
46 @'qUseFriBidi' will take precedence.</p>
47 */
48#ifndef qTryToUseUNISCRIBEForTextRuns
49#if qStroika_Foundation_Common_Platform_Windows && qUniscribeAvailableWithSDK
50#define qTryToUseUNISCRIBEForTextRuns 1
51#else
52#define qTryToUseUNISCRIBEForTextRuns 0
53#endif
54#endif
55
56/*
57 @CONFIGVAR: qUseICUBidi
58 @DESCRIPTION: <p>Defines whether or not we use the IBM ICU package to generate text layouts for bidirectional text
59 such as Hebrew or Arabic. THIS IS NOT YET IMPLEMENTED (as of 3.1a3) and may never be - as the ICU package is huge,
60 and only setup easily to work with a DLL copy of itself.</p>
61 */
62#ifndef qUseICUBidi
63#define qUseICUBidi 0
64#endif
65
66 /*
67 @CLASS: TextLayoutBlock
68 @DESCRIPTION: <p>xxx -- XPLAIN - Add ref to URL docs for this API. Note always pass in PARAGRAPH as unit for layout (cuz in spec)
69 </p>
70 */
71 class TextLayoutBlock {
72 protected:
73 TextLayoutBlock () = default;
74 virtual ~TextLayoutBlock () = default;
75
76 public:
77 nonvirtual const Led_tChar* PeekAtRealText () const;
78 nonvirtual const Led_tChar* PeekAtVirtualText () const;
79 nonvirtual size_t GetTextLength () const;
80
81 public:
82 virtual void PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const = 0;
83 virtual void PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const = 0;
84
85 public:
86 nonvirtual TextDirection GetCharacterDirection (size_t realCharOffset) const;
87
88 public:
89 nonvirtual size_t MapRealOffsetToVirtual (size_t i) const;
90 nonvirtual size_t MapVirtualOffsetToReal (size_t i) const;
91
92 public:
93 struct ScriptRunElt {
94 // NB: Since length of a run is always the same for virtual and real - we COULD make this struct smaller!
95 TextDirection fDirection;
96 size_t fRealStart;
97 size_t fRealEnd;
98 size_t fVirtualStart;
99 size_t fVirtualEnd;
100
101 nonvirtual bool operator== (const ScriptRunElt& rhs) const = default;
102 };
103 struct LessThanVirtualStart;
104
105 public:
106 virtual vector<ScriptRunElt> GetScriptRuns () const = 0;
107
108 public:
109 nonvirtual void CopyOutRealText (const ScriptRunElt& scriptRunElt, Led_tChar* buf) const;
110 nonvirtual void CopyOutVirtualText (const ScriptRunElt& scriptRunElt, Led_tChar* buf) const;
111
112 public:
113 nonvirtual void PeekAtRealText (const ScriptRunElt& scriptRunElt, const Led_tChar** startText, const Led_tChar** endText) const;
114 nonvirtual Led_tString GetRealText () const;
115 nonvirtual Led_tString GetRealText (const ScriptRunElt& scriptRunElt) const;
116 nonvirtual void PeekAtVirtualText (const ScriptRunElt& scriptRunElt, const Led_tChar** startText, const Led_tChar** endText) const;
117 nonvirtual Led_tString GetVirtualText () const;
118 nonvirtual Led_tString GetVirtualText (const ScriptRunElt& scriptRunElt) const;
119
120 public:
121 nonvirtual bool operator== (const TextLayoutBlock& rhs) const;
122
123 // Debug support
124 public:
125 nonvirtual void Invariant () const;
126#if qStroika_Foundation_Debug_AssertionsChecked
127 protected:
128 virtual void Invariant_ () const;
129#endif
130 };
131
132 /*
133 @CLASS: TextLayoutBlock::LessThanVirtualStart
134 @BASES:
135 @DESCRIPTION:
136 <p>Use this class when you have a vector of ScriptRunElt and want to sort it by virtual start.</p>
137 <p>As in:
138 </p>
139 <code><pre>
140 vector&lt;TextLayoutBlock::ScriptRunElt&gt; runs = get_em ();
141 sort (runs.begin (), runs.end (), TextLayoutBlock::LessThanVirtualStart ());
142 </pre></code>
143 */
144 struct TextLayoutBlock::LessThanVirtualStart {
145 bool operator() (const ScriptRunElt& lhs, const ScriptRunElt& rhs)
146 {
147 int diff = int (lhs.fVirtualStart) - int (rhs.fVirtualStart);
148 return (diff < 0);
149 }
150 };
151
152 /*
153 @CLASS: TextLayoutBlock_Basic
154 @DESCRIPTION: <p>NB: the CTOR copies the argument string by value, and doesn't retain the pointer itself.
155 </p>
156 */
157 class TextLayoutBlock_Basic : public TextLayoutBlock {
158 private:
159 using inherited = TextLayoutBlock;
160
161 public:
162 TextLayoutBlock_Basic (const Led_tChar* realText, const Led_tChar* realTextEnd);
163 TextLayoutBlock_Basic (const Led_tChar* realText, const Led_tChar* realTextEnd, TextDirection initialDirection);
164
165 private:
166 nonvirtual void Construct (const Led_tChar* realText, const Led_tChar* realTextEnd, const TextDirection* initialDirection);
167
168 private:
169#if qTryToUseUNISCRIBEForTextRuns
170 nonvirtual bool Construct_UNISCRIBE (const TextDirection* initialDirection);
171#endif
172#if qUseFriBidi
173 nonvirtual void Construct_FriBidi (const TextDirection* initialDirection);
174#endif
175#if qUseICUBidi
176 nonvirtual void Construct_ICU (const TextDirection* initialDirection);
177#endif
178 nonvirtual void Construct_Default ();
179
180 public:
181 virtual void PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
182 virtual void PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
183
184 public:
185 virtual vector<ScriptRunElt> GetScriptRuns () const override;
186
187 private:
188 size_t fTextLength;
189 Foundation::Memory::InlineBuffer<Led_tChar> fRealText;
190 Foundation::Memory::InlineBuffer<Led_tChar> fVirtualText;
191 vector<ScriptRunElt> fScriptRuns;
192 };
193
194 /*
195 @CLASS: TextLayoutBlock_Copy
196 @DESCRIPTION: <p>This class is designed to allow for very cheap and quick copying of TextLayoutBlocks. This class
197 and be stack/assign copied (not just by pointer). It is an indexpensive way to cache a TextLayoutBlock, or
198 to return one from a function.
199 </p>
200 */
201 class TextLayoutBlock_Copy : public TextLayoutBlock {
202 private:
203 using inherited = TextLayoutBlock;
204
205 public:
206 TextLayoutBlock_Copy (const TextLayoutBlock& from);
207 TextLayoutBlock_Copy (const TextLayoutBlock_Copy& from);
208
209 public:
210 virtual void PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
211 virtual void PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
212
213 public:
214 virtual vector<ScriptRunElt> GetScriptRuns () const override;
215
216 private:
217 struct BlockRep {
218 nonvirtual void operator delete (void* p);
219
220 size_t fTextLength{};
221 const Led_tChar* fRealText{};
222 const Led_tChar* fVirtualText{};
223 const ScriptRunElt* fScriptRuns{};
224 const ScriptRunElt* fScriptRunsEnd{};
225 // and we allocate extra space off the end of this object for the acutal data...
226 };
227 shared_ptr<BlockRep> fRep;
228 };
229
230 /*
231 @CLASS: TextLayoutBlock_VirtualSubset
232 @DESCRIPTION: <p>NB: Saves a reference to its CTOR argument- 'subsetOf' so this must be destroyed AFTER
233 the original.</p>
234 <p>It is not obvious if this code should may a COPY of the 'real text' or refer back to the same original text with its original
235 offsets. However - the problem with doing so is that since we've picked a contigous range of the virtual space, that does
236 not coorespond to a contiguous space in the real space necessarily - except for the entire original buffer. That wouldn't be
237 the same size - however - as the virtual buffer. Our API assumes these two spaces are the same size. So - the best compromise
238 appears to be to copy the source text into a new buffer with new offsets that range from 0 to max-length and have nothing todo with
239 the original buffer offsets.
240 </p>
241 */
242 class TextLayoutBlock_VirtualSubset : public TextLayoutBlock {
243 private:
244 using inherited = TextLayoutBlock;
245
246 public:
247 TextLayoutBlock_VirtualSubset (const TextLayoutBlock& subsetOf, size_t vStart, size_t vEnd);
248
249 public:
250 virtual void PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
251 virtual void PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const override;
252
253 public:
254 virtual vector<ScriptRunElt> GetScriptRuns () const override;
255
256 private:
257 const TextLayoutBlock& fSubsetOf;
258 size_t fStart{};
259 size_t fEnd{};
260 vector<ScriptRunElt> fScriptRuns;
261 Foundation::Memory::InlineBuffer<Led_tChar> fRealText;
262 };
263
264}
265
266/*
267 ********************************************************************************
268 ***************************** Implementation Details ***************************
269 ********************************************************************************
270 */
271#include "BiDiLayoutEngine.inl"
272
273#endif /*_Stroika_Frameworks_Led_BiDiLayoutEngine_h_*/