1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3 * Copyright (C) 2004 Tomas Frydrych
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA.
19 */
20
21 #include "ut_assert.h"
22 #include "ut_debugmsg.h"
23 #include "ut_OverstrikingChars.h"
24 #include "ut_TextIterator.h"
25 #include "ut_string.h"
26 #include "gr_RenderInfo.h"
27
clear()28 void GR_Itemization::clear()
29 {
30 m_vOffsets.clear();
31
32 UT_VECTOR_PURGEALL(GR_Item *, m_vItems);
33 m_vItems.clear();
34 }
35
36
37 //////////////////////////////////////////////////////////////////////////////////////////
38 //
39 // implementation of GRXPRenderInfo
40 //
41
42 #define GRIXP_STATIC_BUFFER_SIZE 256
43
44 UT_sint32 GR_XPRenderInfo::s_iClassInstanceCount = 0;
45 UT_UCS4Char * GR_XPRenderInfo::s_pCharBuff = NULL;
46 UT_sint32 * GR_XPRenderInfo::s_pWidthBuff = NULL;
47 UT_sint32 GR_XPRenderInfo::s_iBuffSize = 0;
48 UT_sint32 * GR_XPRenderInfo::s_pAdvances = NULL;
49 GR_RenderInfo * GR_XPRenderInfo::s_pOwner = NULL;
50
GR_XPRenderInfo(GR_ScriptType type)51 GR_XPRenderInfo::GR_XPRenderInfo(GR_ScriptType type)
52 :GR_RenderInfo(type),
53 m_pChars(NULL),
54 m_pWidths(NULL),
55 m_iBufferSize(0),
56 m_pSegmentOffset(NULL),
57 m_iSegmentCount(0),
58 m_iSpaceWidthBeforeJustification(0xfffffff), // note one less 'f'
59 m_iTotalLength(0)
60 {
61 _constructorCommonCode();
62 }
63 #if 0
64 GR_XPRenderInfo::GR_XPRenderInfo(UT_UCS4Char *pChar,
65 UT_sint32 * pAdv,
66 UT_uint32 offset,
67 UT_uint32 len,
68 UT_uint32 iBufferSize,
69 GR_ScriptType type)
70 :GR_RenderInfo(type),
71 m_pChars(pChar),
72 m_pWidths(NULL),
73 m_iBufferSize(iBufferSize),
74 m_pSegmentOffset(NULL),
75 m_iSegmentCount(0),
76 m_iSpaceWidthBeforeJustification(0xfffffff) // not one less 'f'
77 {
78 m_iOffset = offset;
79 m_iLength = len;
80 xxx_UT_DEBUGMSG(("GR_XPRender %x constructed \n"));
81 _constructorCommonCode();
82 };
83 #endif
_constructorCommonCode()84 void GR_XPRenderInfo::_constructorCommonCode()
85 {
86 if(!s_iClassInstanceCount)
87 {
88 s_pCharBuff = new UT_UCS4Char [GRIXP_STATIC_BUFFER_SIZE];
89 UT_return_if_fail(s_pCharBuff);
90
91 s_pWidthBuff = new UT_sint32 [GRIXP_STATIC_BUFFER_SIZE];
92 UT_return_if_fail(s_pWidthBuff);
93
94 s_pAdvances = new UT_sint32 [GRIXP_STATIC_BUFFER_SIZE];
95 UT_return_if_fail(s_pAdvances);
96
97 s_iBuffSize = GRIXP_STATIC_BUFFER_SIZE;
98 }
99
100 s_iClassInstanceCount++;
101 }
102
103
~GR_XPRenderInfo()104 GR_XPRenderInfo::~GR_XPRenderInfo()
105 {
106 --s_iClassInstanceCount;
107 if(!s_iClassInstanceCount)
108 {
109 delete [] s_pCharBuff; s_pCharBuff = NULL;
110 delete [] s_pWidthBuff; s_pWidthBuff = NULL;
111 delete [] s_pAdvances; s_pAdvances = NULL;
112
113 s_pOwner = NULL;
114 }
115 xxx_UT_DEBUGMSG(("Deleting GR_XPRenderInfo %x \n",this));
116 delete [] m_pChars;
117 delete [] m_pWidths;
118 m_pChars = NULL;
119 m_pWidths = NULL;
120 }
121
122 /*!
123 append data represented by ri to ourselves
124
125 NB: combine the justification information
126
127 */
append(GR_RenderInfo & ri,bool bReverse)128 bool GR_XPRenderInfo::append(GR_RenderInfo &ri, bool bReverse)
129 {
130 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
131
132 if((m_iBufferSize <= m_iLength + RI.m_iLength) || (bReverse && (m_iLength > RI.m_iLength)))
133 {
134 xxx_UT_DEBUGMSG(("GR_RenderInfo::append: reallocating span buffer\n"));
135 m_iBufferSize = m_iLength + RI.m_iLength + 1;
136 UT_UCS4Char * pSB = new UT_UCS4Char[m_iBufferSize];
137 UT_sint32 * pWB = new UT_sint32[m_iBufferSize];
138
139 UT_return_val_if_fail(pSB && pWB, false);
140
141 if(bReverse)
142 {
143 UT_UCS4_strncpy(pSB, RI.m_pChars, RI.m_iLength);
144 UT_UCS4_strncpy(pSB + RI.m_iLength, m_pChars, m_iLength);
145
146 UT_UCS4_strncpy((UT_UCS4Char*)pWB, (UT_UCS4Char*)RI.m_pWidths, RI.m_iLength);
147 UT_UCS4_strncpy((UT_UCS4Char*)pWB + RI.m_iLength, (UT_UCS4Char*)m_pWidths, m_iLength);
148 }
149 else
150 {
151 UT_UCS4_strncpy(pSB,m_pChars, m_iLength);
152 UT_UCS4_strncpy(pSB + m_iLength, RI.m_pChars, RI.m_iLength);
153
154 UT_UCS4_strncpy((UT_UCS4Char*)pWB,(UT_UCS4Char*)m_pWidths, m_iLength);
155 UT_UCS4_strncpy((UT_UCS4Char*)pWB + m_iLength, (UT_UCS4Char*)RI.m_pWidths, RI.m_iLength);
156 }
157
158 *(pSB + m_iLength + RI.m_iLength) = 0;
159 delete [] m_pChars;
160 delete [] m_pWidths;
161
162 m_pChars = pSB;
163 m_pWidths = pWB;
164 }
165 else
166 {
167 UT_DEBUGMSG(("mergeWithNext: reusing existin span buffer\n"));
168 if(bReverse)
169 {
170 // can only shift the text directly in the existing buffer if
171 // getLength() <= pNext->getLength()
172 UT_return_val_if_fail(m_iLength <= RI.m_iLength, false);
173 UT_UCS4_strncpy(m_pChars + RI.m_iLength, m_pChars, m_iLength);
174 UT_UCS4_strncpy(m_pChars, RI.m_pChars, RI.m_iLength);
175
176 UT_UCS4_strncpy((UT_UCS4Char*)m_pWidths + RI.m_iLength,
177 (UT_UCS4Char*)m_pWidths, m_iLength);
178
179 UT_UCS4_strncpy((UT_UCS4Char*)m_pWidths,
180 (UT_UCS4Char*)RI.m_pWidths, RI.m_iLength);
181 }
182 else
183 {
184 UT_UCS4_strncpy(m_pChars + m_iLength, RI.m_pChars, RI.m_iLength);
185
186 UT_UCS4_strncpy((UT_UCS4Char*)m_pWidths + m_iLength,
187 (UT_UCS4Char*)RI.m_pWidths, RI.m_iLength);
188 }
189 *(m_pChars + m_iLength + RI.m_iLength) = 0;
190 }
191
192 if( RI.m_iJustificationPoints
193 || m_iJustificationPoints)
194 {
195 // the text is justified, merge the justification information
196 if(m_iSpaceWidthBeforeJustification == 0xfffffff) // note one less 'f'
197 m_iSpaceWidthBeforeJustification = RI.m_iSpaceWidthBeforeJustification;
198
199 m_iJustificationPoints += ri.m_iJustificationPoints;
200 m_iJustificationAmount += ri.m_iJustificationAmount;
201 }
202
203 // mark static buffers dirty if needed
204 if(s_pOwner == this)
205 s_pOwner = NULL;
206
207 m_bLastOnLine = RI.m_bLastOnLine;
208 m_iTotalLength = m_iTotalLength + RI.m_iTotalLength;
209 return true;
210 }
211
212 /*!
213 creates a new instance of GR_*RenderInfo and splits data between
214 ourselves and it at offset
215
216 bReverse == true indicates data in RTL order
217
218 we also calculate justification info for the two parts
219 */
split(GR_RenderInfo * & pri,bool bReverse)220 bool GR_XPRenderInfo::split (GR_RenderInfo *&pri, bool bReverse)
221 {
222 UT_ASSERT( !pri );
223 pri = new GR_XPRenderInfo(m_eScriptType);
224 UT_return_val_if_fail(pri, false);
225
226 pri->m_pItem = m_pItem->makeCopy();
227 UT_return_val_if_fail(pri->m_pItem,false);
228
229 GR_XPRenderInfo * pRI = (GR_XPRenderInfo *)pri;
230
231 UT_uint32 iPart2Len = m_iLength - m_iOffset;
232 UT_uint32 iPart1Len = m_iLength - iPart2Len;
233
234 m_iLength = iPart1Len;
235 m_iTotalLength = iPart1Len;
236
237 pRI->m_iLength = iPart2Len;
238 pRI->m_iTotalLength = iPart2Len;
239
240 // the question is whether we want to shrink the buffer here (and
241 // save memory) or leave it too big (and save time); go for memory
242 // for now
243 UT_UCS4Char * pSB = new UT_UCS4Char[m_iLength + 1];
244 UT_sint32 * pWB = new UT_sint32[m_iLength + 1];
245
246 UT_return_val_if_fail(pSB && pWB, false);
247
248 m_iBufferSize = iPart1Len;
249
250 pRI->m_pChars = new UT_UCS4Char[iPart2Len + 1];
251 pRI->m_pWidths = new UT_sint32[iPart2Len + 1];
252
253 UT_return_val_if_fail(pRI->m_pChars && pRI->m_pWidths, false);
254 pRI->m_iBufferSize = iPart2Len;
255
256
257 if(bReverse)
258 {
259 UT_UCS4_strncpy(pSB, m_pChars + pRI->m_iLength, m_iLength);
260 UT_UCS4_strncpy(pRI->m_pChars, m_pChars, pRI->m_iLength);
261
262 UT_UCS4_strncpy((UT_UCS4Char*)pWB, (UT_UCS4Char*)m_pWidths + pRI->m_iLength, m_iLength);
263 UT_UCS4_strncpy((UT_UCS4Char*)pRI->m_pWidths,
264 (UT_UCS4Char*)m_pWidths, pRI->m_iLength);
265 }
266 else
267 {
268 UT_UCS4_strncpy(pSB, m_pChars, m_iLength);
269 UT_UCS4_strncpy(pRI->m_pChars, m_pChars + m_iLength, pRI->m_iLength);
270
271 UT_UCS4_strncpy((UT_UCS4Char*)pWB,(UT_UCS4Char*)m_pWidths, m_iLength);
272 UT_UCS4_strncpy((UT_UCS4Char*)pRI->m_pWidths,
273 (UT_UCS4Char*)m_pWidths + m_iLength, pRI->m_iLength);
274 }
275
276 pSB[m_iLength] = 0;
277
278 pRI->m_pChars[pRI->m_iLength] = 0;
279
280 delete[] m_pChars;
281 m_pChars = pSB;
282
283 delete[] m_pWidths;
284 m_pWidths = pWB;
285
286 pRI->m_eShapingResult = m_eShapingResult;
287
288 // Deal with justification
289 // this has to be always done (used by isJustified())
290 pRI->m_iSpaceWidthBeforeJustification = m_iSpaceWidthBeforeJustification;
291
292 pRI->m_bLastOnLine = m_bLastOnLine;
293 m_bLastOnLine = false;
294
295 if(!isJustified())
296 {
297 // we are done
298 return true;
299 }
300
301
302 UT_return_val_if_fail(m_pGraphics, false);
303 pRI->m_pGraphics = m_pGraphics;
304
305 UT_sint32 iPoints = m_pGraphics->countJustificationPoints(*pRI);
306 pRI->m_iJustificationPoints = abs(iPoints);
307
308 if(!iPoints)
309 {
310 // the latter section has no justification points, all stays
311 // as is
312 pRI->m_iJustificationAmount = 0;
313 return true;
314 }
315
316 iPoints = m_pGraphics->countJustificationPoints(*this);
317
318 if(!iPoints)
319 {
320 // all justification is done in the latter section
321 pRI->m_iJustificationAmount = m_iJustificationAmount;
322 pRI->m_iJustificationPoints = m_iJustificationPoints;
323
324 m_iJustificationAmount = 0;
325 m_iJustificationPoints = 0;
326
327 return true;
328 }
329
330 // work out how much of the original amount falls on the new pRI
331 UT_return_val_if_fail(m_iJustificationPoints, false);
332 UT_sint32 iAmount = m_iJustificationAmount * pRI->m_iJustificationPoints / m_iJustificationPoints;
333 pRI->m_iJustificationAmount = iAmount;
334
335 m_iJustificationAmount -= iAmount;
336 m_iJustificationPoints = abs(iPoints);
337
338 return true;
339 }
340
341 /**
342 remove section of length iLen starting at offset from any chaches ...
343 return value false indicates that simple removal was not possible
344 and the caller needs to re-shape.
345 */
cut(UT_uint32 offset,UT_uint32 iLen,bool)346 bool GR_XPRenderInfo::cut(UT_uint32 offset, UT_uint32 iLen, bool /*bReverse*/)
347 {
348 UT_return_val_if_fail(m_pText, false);
349 // ascertain the state of the buffer and our shaping requirenments ...
350 bool bRefresh = (((UT_uint32)m_eState & (UT_uint32)m_eShapingResult ) != 0);
351 UT_sint32 ioffset = static_cast<UT_sint32>(offset);
352 UT_sint32 jLen = static_cast<UT_sint32>(iLen);
353
354 if(bRefresh)
355 return false;
356
357 m_iTotalLength -= jLen;
358
359 // if we got here, we just need to cut out a bit of the draw
360 // buffer
361 UT_sint32 iLenToCopy = m_iLength - ioffset - jLen;
362
363 if(m_iVisDir == UT_BIDI_RTL)
364 {
365 // if this is an rtl run, the end of the draw buffer corresponds to the start
366 // section of the run, so we are moving not what is left after the deletion,
367 // but what preceeds it
368 iLenToCopy = ioffset;
369 }
370
371 UT_return_val_if_fail(iLenToCopy >= 0, false);
372 if(iLenToCopy)
373 {
374 UT_UCS4Char * d = m_pChars+ioffset;
375 UT_UCS4Char * s = m_pChars+ioffset+jLen;
376
377 if(m_iVisDir == UT_BIDI_RTL)
378 {
379 d = m_pChars + (m_iLength - (ioffset + jLen));
380 s = m_pChars + (m_iLength - ioffset);
381 }
382
383 UT_UCS4_strncpy(d, s, iLenToCopy);
384 m_pChars[m_iLength - iLen] = 0;
385
386 d = (UT_UCS4Char *) m_pWidths+ioffset;
387 s = (UT_UCS4Char *) m_pWidths+ioffset+jLen;
388
389 if(m_iVisDir == UT_BIDI_RTL)
390 {
391 d = (UT_UCS4Char *) m_pWidths + (m_iLength - (ioffset + jLen));
392 s = (UT_UCS4Char *) m_pWidths + (m_iLength - ioffset);
393 }
394
395 UT_UCS4_strncpy(d, s, iLenToCopy);
396 m_pWidths[m_iLength - jLen] = 0;
397 }
398
399 // mark static buffers dirty if needed
400 if(s_pOwner == this)
401 s_pOwner = NULL;
402
403 return true;
404 }
405
prepareToRenderChars()406 void GR_XPRenderInfo::prepareToRenderChars()
407 {
408 if(s_pOwner == this)
409 {
410 // we currently own the static buffers, so we do not need to
411 // do anything
412 return;
413 }
414
415 // make sure that the static buffers where we temporarily store
416 // information are big enough
417 UT_return_if_fail(_checkAndFixStaticBuffers());
418
419 // strip placeholders and adjust segment offsets accordingly
420 _stripLigaturePlaceHolders();
421
422 // calculate advances from the pre-processed buffer
423 _calculateCharAdvances();
424
425 s_pOwner = this;
426 }
427
428 /*!
429 This function no longer does any ligature handling (use the Pango
430 graphics for that);
431 It still does some preprocessing on the static buffers that is needed by
432 _calculateCharacterAdvances.
433 */
_stripLigaturePlaceHolders()434 void GR_XPRenderInfo::_stripLigaturePlaceHolders()
435 {
436 UT_return_if_fail(m_iLength <= m_iBufferSize && m_pText);
437 if(!m_pSegmentOffset)
438 m_iSegmentCount = 0;
439
440 // this is sligthly complicated by having to deal with both
441 // logical and visual coordinances at the same time
442 //
443 // i and j work in visual coordiances, and correspond to the
444 // orignal pChars array and the s_pCharBuffer
445 //
446 // m and iSplitOffset are in logical coordinaces, m being index
447 // into pWidths, and iSplitOffset a value comparable to pOffset
448 // values (also in logical order)
449 UT_sint32 len = (UT_sint32) m_iLength;
450 bool bReverse = false;
451
452 if(m_iVisDir == UT_BIDI_RTL)
453 {
454 // we will be using addition on the width buffer so we need to
455 // zerow it
456 memset(s_pWidthBuff, 0, sizeof(UT_sint32)*m_iBufferSize);
457 bReverse = true;
458 }
459
460 for(UT_sint32 i = 0, j = 0; i < len; i++, j++)
461 {
462 // ordinary character, just copy it and set the width as
463 // appropriate
464 s_pCharBuff[j] = m_pChars[i];
465
466 if(bReverse)
467 s_pWidthBuff[j] += m_pWidths[i];
468 else
469 s_pWidthBuff[j] = m_pWidths[i];
470 }
471 }
472
473 /**
474 The following code calculates the advances for individual
475 characters that are to be fed to gr_Graphics::drawChars()
476 Note, that character advances are not necessarily identical to
477 character widths; in the case of combining characters the
478 required advance depends on the width of the base character and
479 the properties of the combining character, and it can be both
480 positive and negative.
481
482 At the moment, we calculate the advances here puting them into
483 a static array. Should this prove to be too much of a
484 performance bottleneck, we could cache this in a member array,
485 and refresh it inside refreshDrawBuffer()
486 */
_calculateCharAdvances()487 void GR_XPRenderInfo::_calculateCharAdvances()
488 {
489 if(m_iLength == 0)
490 return;
491
492 UT_return_if_fail(m_iLength <= m_iBufferSize);
493
494 if(m_iVisDir == UT_BIDI_RTL )
495 {
496 // we expect the width array to be the result of processing by
497 // _stripLigaturePlaceHolders(), which is in the same order as
498 // the string to which it relates
499
500 for(UT_sint32 n = 0; n < m_iLength; n++)
501 {
502 if(s_pWidthBuff[n] < 0 || s_pWidthBuff[n] >= GR_OC_LEFT_FLUSHED)
503 {
504 UT_sint32 iCumAdvance = 0;
505
506 UT_sint32 m = n+1;
507 while(m < (UT_sint32)m_iLength && s_pWidthBuff[m] < 0)
508 m++;
509
510 if(m >= m_iLength)
511 {
512 // problem: this run does not contain the
513 // character over which we are meant to be
514 // overimposing our overstriking chars
515 // we will have to set the offsets to 0
516 for(UT_sint32 k = n; k < m_iLength; k++)
517 s_pAdvances[k] = 0;
518
519 n = m_iLength;
520 }
521 else
522 {
523 UT_sint32 k;
524 for(k = n; k < m; k++)
525 {
526 UT_sint32 iAdv;
527 if(s_pWidthBuff[k] >= GR_OC_LEFT_FLUSHED)
528 {
529 UT_sint32 iThisWidth = s_pWidthBuff[k] & GR_OC_MAX_WIDTH;
530 iAdv = s_pWidthBuff[m] - iThisWidth - iCumAdvance;
531 }
532 else
533 {
534 // centered character
535 iAdv = (s_pWidthBuff[m] + s_pWidthBuff[k])/2 - iCumAdvance;
536 }
537
538 if(k == 0)
539 {
540 // k == 0, this is the leftmost character,
541 // so we have no advance to set, but we
542 // can adjust the starting point of the drawing
543 m_xoff += iAdv;
544 }
545 else if(k == n)
546 {
547 // this is a special case; we have already
548 // calculated the advance in previous
549 // round of the main loop, and this is
550 // only adjustment
551 s_pAdvances[k-1] += iAdv;
552 }
553 else
554 s_pAdvances[k-1] = iAdv;
555
556 iCumAdvance += iAdv;
557 }
558
559 s_pAdvances[k-1] = -iCumAdvance;
560 s_pAdvances[k] = s_pWidthBuff[m];
561 n = k; // should be k+1, but there will be n++ in
562 // the for loop
563 }
564
565 }
566 else
567 {
568 s_pAdvances[n] = s_pWidthBuff[n];
569 }
570 }
571 }
572 else
573 {
574 for(UT_sint32 n = 0; n < m_iLength; n++)
575 {
576 if((n < m_iLength - 1) && ((s_pWidthBuff[n+1] < 0) || (s_pWidthBuff[n+1] >= GR_OC_LEFT_FLUSHED)))
577 {
578 // remember the width of the non-zero character
579 UT_sint32 iWidth = s_pWidthBuff[n];
580 UT_sint32 iCumAdvance = 0;
581
582 // find the next non-zerow char
583 UT_sint32 m = n + 1;
584 while(m < m_iLength && s_pWidthBuff[m] < 0)
585 {
586 // plus because pCharWidths[m] < 0
587 // -1 because it is between m-1 and m
588 UT_sint32 iAdv;
589 if(s_pWidthBuff[m] >= GR_OC_LEFT_FLUSHED)
590 {
591 UT_sint32 iThisWidth = s_pWidthBuff[m] & GR_OC_MAX_WIDTH;
592 iThisWidth -= iWidth;
593
594 iAdv = -(iThisWidth - iCumAdvance);
595 }
596 else
597 {
598 //centered character
599 iAdv = iWidth - (iWidth + s_pWidthBuff[m])/2 + iCumAdvance;
600 }
601
602 s_pAdvances[m-1] = iAdv;
603 iCumAdvance += iAdv;
604 m++;
605 }
606
607 n = m-1; // this is the last 0-width char
608 s_pAdvances[n] = iWidth - iCumAdvance;
609 }
610 else
611 s_pAdvances[n] = s_pWidthBuff[n];
612 xxx_UT_DEBUGMSG(("%d ",s_pAdvances[n],s_pWidthBuff[n] ));
613 }
614 xxx_UT_DEBUGMSG(("ENDRUN \n"));
615
616 }
617 }
618
_checkAndFixStaticBuffers()619 bool GR_XPRenderInfo::_checkAndFixStaticBuffers()
620 {
621 // TODO -- FIX ME !!!
622 if(m_iLength > s_iBuffSize)
623 {
624 delete [] s_pCharBuff;
625 s_pCharBuff = new UT_UCS4Char [m_iLength];
626 UT_return_val_if_fail(s_pCharBuff, false);
627
628 delete [] s_pWidthBuff;
629 s_pWidthBuff = new UT_sint32 [m_iLength];
630 UT_return_val_if_fail(s_pWidthBuff,false);
631
632 delete [] s_pAdvances;
633 s_pAdvances = new UT_sint32 [m_iLength];
634 UT_return_val_if_fail(s_pAdvances,false);
635
636 s_iBuffSize = m_iLength;
637 }
638
639 return true;
640 }
641
642
643