1 #include "RichText.h"
2
3 namespace Upp {
4
Smh(Lines & lines,HeightInfo * th,int cx) const5 void RichPara::Smh(Lines& lines, HeightInfo *th, int cx) const
6 {
7 Line& l = lines.line.Top();
8 l.ascent = l.descent = l.external = 0;
9 const HeightInfo *he = th + l.pos + l.len;
10 for(const HeightInfo *h = th + l.pos; h < he; h++) {
11 if(h->ascent > l.ascent) l.ascent = h->ascent;
12 if(h->descent > l.descent) l.descent = h->descent;
13 if(h->external > l.external) l.external = h->external;
14 }
15 if(format.linespacing == LSP15) {
16 l.ascent = (3 * l.ascent) >> 1;
17 l.descent = (3 * l.descent) >> 1;
18 }
19 if(format.linespacing == LSP20) {
20 l.ascent = 2 * l.ascent;
21 l.descent = 2 * l.descent;
22 }
23 l.xpos = format.lm;
24 cx -= format.lm + format.rm;
25 l.xpos += lines.GetCount() == 1 ? lines.first_indent : lines.next_indent;
26 if(!l.withtabs)
27 switch(format.align) {
28 case ALIGN_RIGHT:
29 l.xpos += cx - l.cx;
30 break;
31 case ALIGN_CENTER:
32 l.xpos += (cx - l.cx) / 2;
33 break;
34 }
35 l.cx -= lines.GetCount() == 1 ? lines.first_indent : lines.next_indent;
36 }
37
GetNextTab(int pos,int cx) const38 RichPara::Tab RichPara::GetNextTab(int pos, int cx) const
39 {
40 int tabi = -1;
41 int dist = INT_MAX;
42 for(int i = 0; i < format.tab.GetCount(); i++) {
43 const Tab& tab = format.tab[i];
44 int tabpos = tab.pos;
45 if(tabpos & TAB_RIGHTPOS)
46 tabpos = cx - (tabpos & ~TAB_RIGHTPOS);
47 if(tabpos > pos && tabpos - pos < dist) {
48 tabi = i;
49 dist = tabpos - pos;
50 }
51 }
52 if(format.bullet == BULLET_TEXT) {
53 int q = format.indent + format.lm;
54 if(q > pos && q - pos < dist) {
55 Tab tab;
56 tab.align = ALIGN_LEFT;
57 tab.pos = q;
58 return tab;
59 }
60 }
61 if(tabi < 0) {
62 Tab tab;
63 tab.pos = format.tabsize ? (pos + format.tabsize) / format.tabsize * format.tabsize : 0;
64 tab.align = ALIGN_LEFT;
65 return tab;
66 }
67 Tab tab = format.tab[tabi];
68 if(tab.pos & TAB_RIGHTPOS)
69 tab.pos = cx - (tab.pos & ~TAB_RIGHTPOS);
70 return tab;
71 }
72
73 struct RichPara::StorePart {
74 wchar *t;
75 int *w;
76 int *p;
77 const CharFormat **f;
78 HeightInfo *h;
79 int pos;
80 FontInfo pfi;
81
82 void Store(Lines& lines, const Part& p, int pinc);
83 };
84
Store(Lines & lines,const Part & part,int pinc)85 void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc)
86 {
87 CharFormat& pfmt = lines.hformat.Add();
88 pfmt = part.format;
89 if(part.field && pinc) {
90 for(int i = 0; i < part.fieldpart.GetCount(); i++)
91 Store(lines, part.fieldpart[i], 0);
92 pos++;
93 }
94 else
95 if(part.object) {
96 *f++ = &pfmt;
97 Size sz = part.object.GetSize();
98 *w++ = sz.cx;
99 h->ydelta = part.object.GetYDelta();
100 h->ascent = sz.cy - h->ydelta;
101 h->descent = max(h->ydelta, 0);
102 h->external = 0;
103 lines.object.Add(part.object);
104 h->object = &lines.object.Top();
105 h++;
106 *t++ = 'x';
107 *p++ = pos;
108 pos += pinc;
109 }
110 else {
111 const wchar *s = part.text;
112 const wchar *lim = part.text.End();
113 Font fnt = part.format;
114 FontInfo fi = fnt.Info();
115 FontInfo wfi = fi;
116 if(part.format.sscript) {
117 fnt.Height(fnt.GetHeight() * 3 / 5);
118 wfi = fnt.Info();
119 }
120 if(part.format.capitals) {
121 CharFormat& cfmt = lines.hformat.Add();
122 cfmt = part.format;
123 cfmt.Height(cfmt.GetHeight() * 4 / 5);
124 FontInfo cfi = cfmt.Info();
125 FontInfo cwfi = cfi;
126 if(part.format.sscript) {
127 Font fnt = cfmt;
128 fnt.Height(fnt.GetHeight() * 3 / 5);
129 cwfi = fnt.Info();
130 }
131
132 while(s < lim) {
133 wchar c = *s++;
134 if(c == 9) {
135 *f++ = &pfmt;
136 h->ascent = pfi.GetAscent();
137 h->descent = pfi.GetDescent();
138 h->external = pfi.GetExternal();
139 *w++ = 0;
140 }
141 else
142 if(IsLower(c)) {
143 *f++ = &cfmt;
144 c = (wchar)ToUpper(c);
145 h->ascent = cfi.GetAscent();
146 h->descent = cfi.GetDescent();
147 h->external = cfi.GetExternal();
148 *w++ = c >= 32 ? cwfi[c] : 0;
149 }
150 else {
151 *f++ = &pfmt;
152 h->ascent = fi.GetAscent();
153 h->descent = fi.GetDescent();
154 h->external = fi.GetExternal();
155 *w++ = c >= 32 ? wfi[c] : 0;
156 }
157 h->object = NULL;
158 *t++ = c;
159 *p++ = pos;
160 pos += pinc;
161 h++;
162 }
163 }
164 else {
165 while(s < lim) {
166 wchar c = *s++;
167 *f++ = &pfmt;
168 if(c == 9) {
169 h->ascent = pfi.GetAscent();
170 h->descent = pfi.GetDescent();
171 h->external = pfi.GetExternal();
172 }
173 else {
174 h->ascent = fi.GetAscent();
175 h->descent = fi.GetDescent();
176 h->external = fi.GetExternal();
177 }
178 h->object = NULL;
179 *p++ = pos;
180 pos += pinc;
181 h++;
182 *w++ = c >= 32 ? wfi[c] : 0;
183 *t++ = c;
184 }
185 }
186 }
187 }
188
CountChars(const Array<RichPara::Part> & part)189 static int CountChars(const Array<RichPara::Part>& part)
190 {
191 int n = 0;
192 for(int i = 0; i < part.GetCount(); i++) {
193 const RichPara::Part& p = part[i];
194 if(p.field)
195 n += CountChars(p.fieldpart);
196 else
197 n += p.GetLength();
198 }
199 return n;
200 }
201
Lines()202 RichPara::Lines::Lines()
203 {
204 justified = false;
205 incache = false;
206 cacheid = 0;
207 }
208
Cache()209 Array<RichPara::Lines>& RichPara::Lines::Cache()
210 {
211 static Array<Lines> x;
212 return x;
213 }
214
~Lines()215 RichPara::Lines::~Lines()
216 {
217 if(cacheid && line.GetCount() && !incache) {
218 Mutex::Lock __(cache_lock);
219 Array<Lines>& cache = Cache();
220 incache = true;
221 cache.Insert(0) = pick(*this);
222 // cache.SetCount(1);
223 int total = 0;
224 for(int i = 1; i < cache.GetCount(); i++) {
225 total += cache[i].clen;
226 if(total > 10000 || i > 64) {
227 cache.SetCount(i);
228 break;
229 }
230 }
231 }
232 }
233
FormatLines(int acx) const234 RichPara::Lines RichPara::FormatLines(int acx) const
235 {
236 Lines lines;
237 if(cacheid) {
238 Mutex::Lock __(cache_lock);
239 Array<Lines>& cache = Lines::Cache();
240 for(int i = 0; i < cache.GetCount(); i++)
241 if(cache[i].cacheid == cacheid && cache[i].cx == acx) {
242 lines = pick(cache[i]);
243 lines.incache = false;
244 cache.Remove(i);
245 return lines;
246 }
247 }
248
249 int i;
250 lines.cacheid = cacheid;
251 lines.cx = acx;
252 lines.len = GetLength();
253 lines.clen = CountChars(part);
254 lines.first_indent = lines.next_indent = format.indent;
255 if(format.bullet == BULLET_TEXT)
256 lines.first_indent = 0;
257 else
258 if(!format.bullet && !format.IsNumbered())
259 lines.next_indent = 0;
260
261 FontInfo pfi = format.Info();
262 if(lines.len == 0) {
263 Line& l = lines.line.Add();
264 l.pos = 0;
265 l.ppos = 0;
266 l.plen = 0;
267 l.len = 0;
268 l.cx = lines.first_indent;
269 l.withtabs = false;
270 HeightInfo dummy;
271 Smh(lines, &dummy, lines.cx);
272 l.ascent = pfi.GetAscent();
273 l.descent = pfi.GetDescent();
274 l.external = pfi.GetExternal();
275 return lines;
276 }
277
278 lines.text.Alloc(lines.clen);
279 lines.width.Alloc(lines.clen);
280 lines.pos.Alloc(lines.clen);
281 lines.format.Alloc(lines.clen);
282 lines.height.Alloc(lines.clen);
283
284 StorePart sp;
285 sp.t = lines.text;
286 sp.w = lines.width;
287 sp.p = lines.pos;
288 sp.f = lines.format;
289 sp.h = lines.height;
290 sp.pfi = pfi;
291 sp.pos = 0;
292
293 for(i = 0; i < part.GetCount(); i++)
294 sp.Store(lines, part[i], 1);
295
296 wchar *s = lines.text;
297 wchar *text = s;
298 wchar *end = lines.text + lines.clen;
299 wchar *space = NULL;
300 int *w = lines.width;
301 int cx = lines.first_indent;
302 int rcx = lines.cx - format.lm - format.rm;
303 bool withtabs = false;
304 int scx = cx;
305 while(s < end) {
306 Tab t;
307 if(*s == ' ') {
308 space = s;
309 scx = cx;
310 }
311 else {
312 if(*s == '\t') {
313 t = GetNextTab(cx + format.lm, rcx);
314 space = NULL;
315 }
316 if(cx + *w > rcx && s > text ||
317 *s == '\t' && (t.align == ALIGN_RIGHT ? t.pos - format.lm > rcx
318 : t.pos - format.lm >= rcx)) {
319 Line& l = lines.line.Add();
320 l.withtabs = withtabs;
321 l.pos = (int)(text - lines.text);
322 if(space) {
323 l.len = (int)(space - text) + 1;
324 l.cx = scx;
325 text = s = space + 1;
326 }
327 else {
328 l.len = (int)(s - text);
329 l.cx = cx;
330 text = s;
331 }
332 Smh(lines, lines.height, lines.cx);
333 cx = lines.next_indent;
334 w = text - ~lines.text + lines.width;
335 space = NULL;
336 rcx = lines.cx - format.lm - format.rm;
337 withtabs = false;
338 t = GetNextTab(cx + format.lm, acx);
339 }
340 }
341 if(*s == '\t') {
342 *s += t.fillchar;
343 if(t.align == ALIGN_LEFT) {
344 *w++ = t.pos - format.lm - cx;
345 cx = t.pos - format.lm;
346 }
347 else {
348 int tcx = 0;
349 int *tw = w + 1;
350 for(wchar *ts = s + 1; ts < end && *ts != '\t'; ts++)
351 tcx += *tw++;
352 int ww = t.pos - format.lm - cx - (t.align == ALIGN_RIGHT ? tcx : tcx / 2);
353 if(ww > 0) {
354 *w++ = ww;
355 cx += ww;
356 }
357 else
358 *w++ = 0;
359 }
360 withtabs = true;
361 }
362 else
363 cx += *w++;
364 s++;
365 }
366 Line& l = lines.line.Add();
367 l.withtabs = withtabs;
368 l.pos = (int)(text - lines.text);
369 l.len = (int)(s - text);
370 l.cx = cx;
371 Smh(lines, lines.height, lines.cx);
372 for(i = 0; i < lines.line.GetCount(); i++) {
373 Line& l = lines.line[i];
374 l.ppos = lines.pos[l.pos];
375 l.plen = (l.pos + l.len < lines.clen ? lines.pos[l.pos + l.len] : lines.len) - l.ppos;
376 }
377
378 return lines;
379 }
380
Justify(const RichPara::Format & format)381 void RichPara::Lines::Justify(const RichPara::Format& format)
382 {
383 if(justified)
384 return;
385 justified = true;
386 if(format.align != ALIGN_JUSTIFY) return;
387 for(int i = 0; i < line.GetCount() - 1; i++) {
388 const Line& li = line[i];
389 if(!li.withtabs && li.len) {
390 const wchar *s = ~text + li.pos;
391 const wchar *lim = s + li.len;
392 while(lim - 1 > s) {
393 if(*(lim - 1) != ' ') break;
394 lim--;
395 }
396 while(s < lim) {
397 if(*s != ' ' && *s != 160) break;
398 s++;
399 }
400
401 const wchar *beg = s;
402 int nspc = 0;
403 while(s < lim) {
404 if(*s == ' ' || *s == 160) nspc++;
405 s++;
406 }
407 s = beg;
408 if(nspc) {
409 int q = ((cx - format.lm - format.rm -
410 (i == 0 ? first_indent : next_indent) - li.cx) << 16)
411 / nspc;
412 int *w = beg - ~text + width;
413 int prec = 0;
414 while(s < lim) {
415 if(*s == ' ' || *s == 160) {
416 *w += (prec + q) >> 16;
417 prec = (prec + q) & 0xffff;
418 }
419 w++;
420 s++;
421 }
422 }
423 }
424 }
425 }
426
BodyHeight() const427 int RichPara::Lines::BodyHeight() const
428 {
429 int sum = 0;
430 for(int i = 0; i < line.GetCount(); i++)
431 sum += line[i].Sum();
432 return sum;
433 }
434
435 }
436