1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3 * Copyright (C) 1998 AbiSource, Inc.
4 * Copyright (C) 2002 Tomas Frydrych, <tomas@frydrych.uklinux.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <ctype.h>
27 #include <math.h>
28 #include <string.h>
29
30 #include "xap_App.h"
31 #include "xap_Prefs.h"
32 #include "xap_EncodingManager.h"
33 #include "gr_Graphics.h"
34 #include "gr_CharWidths.h"
35 #include "ut_assert.h"
36 #include "ut_string.h"
37 #include "ut_std_string.h"
38 #include "ut_units.h"
39 #include "ut_sleep.h"
40 #include "ut_stack.h"
41 #include "ut_std_map.h"
42 #include "ut_growbuf.h"
43 #include "ut_debugmsg.h"
44 #include "ut_OverstrikingChars.h"
45 #include "ut_TextIterator.h"
46 #include "gr_Caret.h"
47 #include "gr_RenderInfo.h"
48
49 // static class member initializations
50 UT_uint32 GR_Font::s_iAllocCount = 0;
51 UT_VersionInfo GR_Graphics::s_Version;
52 UT_UCS4Char GR_Graphics::s_cDefaultGlyph = '?';
53
GR_Font()54 GR_Font::GR_Font():
55 m_eType(GR_FONT_UNSET),
56 m_pCharWidths(NULL)
57 {
58 s_iAllocCount++;
59 m_iAllocNo = s_iAllocCount;
60 }
61
~GR_Font()62 GR_Font::~GR_Font()
63 {
64 // need this so children can clean up
65 }
66
67 /*!
68 Return the hash key used by the cache to fetch the font
69 This method may be overridden to compute it in real time if needed
70 */
hashKey(void) const71 const std::string & GR_Font::hashKey(void) const
72 {
73 return m_hashKey;
74 }
75
76 /*!
77 Return the char width from the cache.
78 Compute the width if needed, and cache it.
79 */
getCharWidthFromCache(UT_UCSChar c) const80 UT_sint32 GR_Font::getCharWidthFromCache (UT_UCSChar c) const
81 {
82 // the way GR_CharWidthsCache is implemented will cause problems
83 // fro any graphics plugin that wants to use the cache -- we will
84 // need to instantiate the cache into a static member of
85 // GR_Graphics so that the plugin could get to it without calling
86 // the static getCharWidthCache()
87 #ifndef ABI_GRAPHICS_PLUGIN_NO_WIDTHS
88 // first of all, handle 0-width spaces ...
89 if(c == 0xFEFF || c == 0x200B || c == UCS_LIGATURE_PLACEHOLDER)
90 return 0;
91
92 UT_sint32 iWidth = GR_CW_UNKNOWN;
93
94 if (m_pCharWidths == NULL) {
95 m_pCharWidths = GR_CharWidthsCache::getCharWidthCache()->getWidthsForFont(this);
96 }
97 iWidth = m_pCharWidths->getWidth(c);
98 if (iWidth == GR_CW_UNKNOWN) {
99 iWidth = measureUnremappedCharForCache(c);
100 m_pCharWidths->setWidth(c, iWidth);
101 }
102
103 return iWidth;
104 #else
105 UT_return_val_if_fail(UT_NOT_IMPLEMENTED,0);
106 #endif
107 }
108
doesGlyphExist(UT_UCS4Char g) const109 bool GR_Font::doesGlyphExist(UT_UCS4Char g) const
110 {
111 if(getCharWidthFromCache(g) == GR_CW_ABSENT)
112 {
113 UT_DEBUGMSG(("GR_Font::doesGlyphExist: glyph 0x%04x absent from font\n",g));
114 return false;
115 }
116
117 return true;
118 }
119
120 /*!
121 Implements a GR_CharWidths.
122 Override if you which to instanciate a subclass.
123 */
newFontWidths(void) const124 GR_CharWidths* GR_Font::newFontWidths(void) const
125 {
126 #ifndef ABI_GRAPHICS_PLUGIN_NO_WIDTHS
127 return new GR_CharWidths();
128 #else
129 return NULL;
130 #endif
131 }
132
AllCarets(GR_Graphics * pG,GR_Caret ** pCaret,UT_GenericVector<GR_Caret * > * vecCarets)133 AllCarets::AllCarets(GR_Graphics * pG,
134 GR_Caret ** pCaret,
135 UT_GenericVector<GR_Caret *>* vecCarets ):
136 m_pG(pG),
137 m_pLocalCaret(pCaret),
138 m_vecCarets(vecCarets)
139 {
140 }
getBaseCaret(void)141 GR_Caret * AllCarets::getBaseCaret(void)
142 {
143 return *m_pLocalCaret;
144 }
145
enable(void)146 void AllCarets::enable(void)
147 {
148 if(*m_pLocalCaret)
149 (*m_pLocalCaret)->enable();
150 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
151 {
152 m_vecCarets->getNthItem(i)->enable();
153 }
154
155 }
156
JustErase(UT_sint32 xPoint,UT_sint32 yPoint)157 void AllCarets::JustErase(UT_sint32 xPoint,UT_sint32 yPoint)
158 {
159 if((*m_pLocalCaret))
160 (*m_pLocalCaret)->JustErase(xPoint,yPoint);
161 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
162 {
163 m_vecCarets->getNthItem(i)->JustErase(xPoint,yPoint);
164 }
165 }
166
167
disable(bool bNoMulti)168 void AllCarets::disable(bool bNoMulti)
169 {
170 if((*m_pLocalCaret))
171 (*m_pLocalCaret)->disable(bNoMulti);
172 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
173 {
174 m_vecCarets->getNthItem(i)->disable(bNoMulti);
175 }
176 }
177
setBlink(bool bBlink)178 void AllCarets::setBlink(bool bBlink)
179 {
180 if((*m_pLocalCaret))
181 (*m_pLocalCaret)->setBlink(bBlink);
182 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
183 {
184 m_vecCarets->getNthItem(i)->setBlink(bBlink);;
185 }
186 }
187
doBlinkIfNeeded(void)188 bool AllCarets::doBlinkIfNeeded(void)
189 {
190 bool bBlinked = false;
191 if((*m_pLocalCaret))
192 {
193 bBlinked = (*m_pLocalCaret)->doBlinkIfNeeded();
194 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
195 {
196 m_vecCarets->getNthItem(i)->forceDraw();
197 }
198 }
199 return bBlinked;
200 }
201
setWindowSize(UT_uint32 width,UT_uint32 height)202 void AllCarets::setWindowSize(UT_uint32 width, UT_uint32 height)
203 {
204 if((*m_pLocalCaret))
205 (*m_pLocalCaret)->setWindowSize(width, height);
206 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
207 {
208 m_vecCarets->getNthItem(i)->setWindowSize(width, height);
209 }
210 }
211
setPendingBlink(void)212 void AllCarets::setPendingBlink(void)
213 {
214 if((*m_pLocalCaret))
215 (*m_pLocalCaret)->setPendingBlink();
216 }
217
setCoords(UT_sint32 x,UT_sint32 y,UT_uint32 h,UT_sint32 x2,UT_sint32 y2,UT_uint32 h2,bool bPointDirection,const UT_RGBColor * pClr)218 void AllCarets::setCoords(UT_sint32 x, UT_sint32 y, UT_uint32 h,
219 UT_sint32 x2, UT_sint32 y2, UT_uint32 h2,
220 bool bPointDirection,
221 const UT_RGBColor * pClr)
222 {
223 if((*m_pLocalCaret))
224 (*m_pLocalCaret)->setCoords(x, y, h, x2, y2, h2, bPointDirection, pClr);
225 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
226 {
227 m_vecCarets->getNthItem(i)->setCoords(x, y, h, x2, y2, h2, bPointDirection, pClr);
228 }
229 }
230
setInsertMode(bool mode)231 void AllCarets::setInsertMode (bool mode)
232 {
233 if((*m_pLocalCaret))
234 (*m_pLocalCaret)->setInsertMode(mode);
235 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
236 {
237 m_vecCarets->getNthItem(i)->setInsertMode(mode);
238 }
239 }
240
forceDraw(void)241 void AllCarets::forceDraw(void)
242 {
243 if((*m_pLocalCaret))
244 (*m_pLocalCaret)->forceDraw();
245 for(UT_sint32 i =0; i< m_vecCarets->getItemCount();i++)
246 {
247 m_vecCarets->getNthItem(i)->forceDraw();
248 }
249 }
250
251
252
253
GR_Graphics()254 GR_Graphics::GR_Graphics()
255 : m_iZoomPercentage(100),
256 m_iFontAllocNo(0),
257 m_bHave3DColors(false),
258 m_paintCount(0),
259 m_bDoubleBufferingActive(false),
260 m_bDrawingSuspended(false),
261 m_pCaret(NULL),
262 m_bIsPortrait(true),
263 m_bSpawnedRedraw(false),
264 m_bExposePending(false),
265 m_bIsExposedAreaAccessed(false),
266 m_bDontRedraw(false),
267 m_bDoMerge(false),
268 m_iPrevYOffset(0),
269 m_iPrevXOffset(0),
270 m_AllCarets(this,&m_pCaret,&m_vecCarets),
271 m_bAntiAliasAlways(false)
272 {
273 }
274
findFont(const char * pszFontFamily,const char * pszFontStyle,const char * pszFontVariant,const char * pszFontWeight,const char * pszFontStretch,const char * pszFontSize,const char * pszLang)275 GR_Font* GR_Graphics::findFont(const char* pszFontFamily,
276 const char* pszFontStyle,
277 const char* pszFontVariant,
278 const char* pszFontWeight,
279 const char* pszFontStretch,
280 const char* pszFontSize,
281 const char* pszLang)
282 {
283 GR_Font * pFont = NULL;
284
285 // NOTE: we currently favor a readable hash key to make debugging easier
286 // TODO: speed things up with a smaller key (the three AP pointers?)
287 std::string key = UT_std_string_sprintf("%s;%s;%s;%s;%s;%s",pszFontFamily,
288 pszFontStyle, pszFontVariant,
289 pszFontWeight, pszFontStretch,
290 pszFontSize);
291
292 FontCache::const_iterator iter = m_hashFontCache.find(key);
293 if (iter == m_hashFontCache.end())
294 {
295 // TODO -- note that we currently assume font-family to be a single name,
296 // TODO -- not a list. This is broken.
297
298 pFont = _findFont(pszFontFamily, pszFontStyle,
299 pszFontVariant,pszFontWeight,
300 pszFontStretch, pszFontSize,
301 pszLang);
302 UT_ASSERT(pFont);
303 xxx_UT_DEBUGMSG(("Insert font %x in gr_Graphics cache \n",pFont));
304 // add it to the cache
305
306 if(pFont)
307 m_hashFontCache.insert(std::make_pair(key, pFont));
308 }
309 else
310 {
311 pFont = iter->second;
312 }
313 return pFont;
314 }
315
~GR_Graphics()316 GR_Graphics::~GR_Graphics()
317 {
318 xxx_UT_DEBUGMSG(("Deleting graphics class %x \n",this));
319 DELETEP(m_pCaret);
320 UT_sint32 i = 0;
321 for(i=0; i< m_vecCarets.getItemCount();i++)
322 {
323 GR_Caret * pCaret = m_vecCarets.getNthItem(i);
324 DELETEP(pCaret);
325 }
326 }
327
beginDoubleBuffering()328 bool GR_Graphics::beginDoubleBuffering()
329 {
330 if(m_bDoubleBufferingActive) return false;
331 m_DCSwitchManagementStack.push((int)SWITCHED_TO_BUFFER);
332 _DeviceContext_SwitchToBuffer();
333 m_bDoubleBufferingActive = true;
334 return true;
335 }
336
endDoubleBuffering(bool token)337 void GR_Graphics::endDoubleBuffering(bool token)
338 {
339 // check prerequisites
340 if(token == false) return;
341
342 UT_ASSERT(m_DCSwitchManagementStack.getDepth() > 0);
343
344 UT_sint32 topMostSwitch;
345 m_DCSwitchManagementStack.viewTop(topMostSwitch);
346 UT_ASSERT(topMostSwitch == (UT_sint32)SWITCHED_TO_BUFFER);
347
348 _DeviceContext_SwitchToScreen();
349 m_DCSwitchManagementStack.pop();
350 m_bDoubleBufferingActive = false;
351 }
352
suspendDrawing()353 bool GR_Graphics::suspendDrawing()
354 {
355 if(m_bDrawingSuspended) return false;
356 m_DCSwitchManagementStack.push((int)DRAWING_SUSPENDED);
357 _DeviceContext_SuspendDrawing();
358 m_bDrawingSuspended = true;
359 return true;
360 }
361
resumeDrawing(bool token)362 void GR_Graphics::resumeDrawing(bool token)
363 {
364 // check prerequisites
365 if(token == false) return;
366
367 UT_ASSERT(m_DCSwitchManagementStack.getDepth() > 0);
368
369 UT_sint32 topMostSwitch;
370 m_DCSwitchManagementStack.viewTop(topMostSwitch);
371 UT_ASSERT(topMostSwitch == (UT_sint32)DRAWING_SUSPENDED);
372
373 // take action only if the caller has the good token
374 _DeviceContext_ResumeDrawing();
375 m_DCSwitchManagementStack.pop();
376 m_bDrawingSuspended = false;
377 }
378
_destroyFonts()379 void GR_Graphics::_destroyFonts ()
380 {
381 UT_map_delete_all_second(m_hashFontCache);
382 m_hashFontCache.clear ();
383 }
384
getNthCaret(UT_sint32 i) const385 GR_Caret * GR_Graphics::getNthCaret(UT_sint32 i) const
386 {
387 if (i>= m_vecCarets.getItemCount())
388 return NULL;
389 return m_vecCarets.getNthItem(i);
390 }
391
getCaret(const std::string & sID) const392 GR_Caret * GR_Graphics::getCaret(const std::string& sID) const
393 {
394 UT_sint32 i= 0;
395 for(i=0; i<m_vecCarets.getItemCount();i++)
396 {
397 if(m_vecCarets.getNthItem(i)->getID() == sID)
398 {
399 return m_vecCarets.getNthItem(i);
400 }
401 }
402 return NULL;
403 }
404
allCarets(void)405 AllCarets * GR_Graphics::allCarets(void)
406 {
407 return &m_AllCarets;
408 }
409
disableAllCarets()410 void GR_Graphics::disableAllCarets()
411 {
412 m_AllCarets.disable();
413 }
414
enableAllCarets()415 void GR_Graphics::enableAllCarets()
416 {
417 m_AllCarets.enable();
418 }
419
createCaret(const std::string & sID)420 GR_Caret * GR_Graphics::createCaret(const std::string& sID)
421 {
422 GR_Caret * pCaret = new GR_Caret(this,sID);
423 m_vecCarets.addItem(pCaret);
424 return pCaret;
425 }
426
removeCaret(const std::string & sID)427 void GR_Graphics::removeCaret(const std::string& sID)
428 {
429 for(UT_sint32 i = 0; i < m_vecCarets.getItemCount(); i++)
430 {
431 GR_Caret* pC = m_vecCarets.getNthItem(i);
432 if (pC->getID() == sID)
433 {
434 DELETEP(pC);
435 m_vecCarets.deleteNthItem(i);
436 }
437 }
438 }
439
440 /*!
441 * WARNING! WARNING! WARNING!
442 * Only gr_UnixGraphics should call this!
443 * Because xap_UnixFontManager "owns" it's fonts, keeps it's own cache of them
444 * and will happily destroy them. If it does this, the cache of pointers here
445 * is no longer valid.
446 * It would be better to simply remove the font from this cache too but the
447 * key used in the xap_UnixFontManager class is different fromt the key used
448 * here.
449 * Until a better solution appears I'll leave this here. Other classes should
450 * use _destroyFonts to remove the cache.
451 */
invalidateCache(void)452 void GR_Graphics::invalidateCache(void)
453 {
454 m_hashFontCache.clear ();
455 }
456
beginPaint()457 void GR_Graphics::beginPaint ()
458 {
459 if (m_paintCount == 0)
460 _beginPaint ();
461
462 m_paintCount++;
463 }
464
endPaint()465 void GR_Graphics::endPaint ()
466 {
467 m_paintCount--;
468
469 if (m_paintCount == 0)
470 _endPaint ();
471 }
472
tdu(UT_sint32 layoutUnits) const473 UT_sint32 GR_Graphics::tdu(UT_sint32 layoutUnits) const
474 {
475 double d = (static_cast<double>(layoutUnits) * static_cast<double>(getDeviceResolution()) * static_cast<double>(getZoomPercentage())) * (1./100. / static_cast<double>(getResolution())) + 0.1;
476 return static_cast<UT_sint32>(d);
477 }
478
479 /*!
480 * This method converts to device units while taking account of the X-scroll
481 * offset. This will always give the exact same logical location on the screen
482 * no matter what the X-scroll offset is. This fixes off-by-1-pixel bugs in X.
483 */
_tduX(UT_sint32 layoutUnits) const484 UT_sint32 GR_Graphics::_tduX(UT_sint32 layoutUnits) const
485 {
486 return tdu(layoutUnits+getPrevXOffset()) - tdu(getPrevXOffset());
487 }
488
489 /*!
490 * This method converts to device units while taking account of the Y-scroll
491 * offset. This will always give the exact same logical location on the screen
492 * no matter what the Y-scroll offset is. This fixes off-by-1-pixel bugs in Y.
493 */
_tduY(UT_sint32 layoutUnits) const494 UT_sint32 GR_Graphics::_tduY(UT_sint32 layoutUnits) const
495 {
496 return tdu(layoutUnits+getPrevYOffset()) - tdu(getPrevYOffset());
497 }
498
499 /*!
500 * This method converts rectangle widths and heights to device units while
501 * taking account rounding down errors.
502 * This fixes off-by-1-pixel-bugs in Rectangle widths and heights.
503 */
_tduR(UT_sint32 layoutUnits) const504 UT_sint32 GR_Graphics::_tduR(UT_sint32 layoutUnits) const
505 {
506 UT_sint32 idh = tdu(layoutUnits);
507 if(tlu(idh) < layoutUnits)
508 {
509 idh += 1;
510 }
511 return idh;
512 }
513
tlu(UT_sint32 deviceUnits) const514 UT_sint32 GR_Graphics::tlu(UT_sint32 deviceUnits) const
515 {
516 return static_cast<UT_sint32>((static_cast<double>(deviceUnits) * static_cast<double>(getResolution()) * 100.) / (static_cast<double>(getDeviceResolution()) * static_cast<double>(getZoomPercentage())));
517 }
518
tduD(double layoutUnits) const519 double GR_Graphics::tduD(double layoutUnits) const
520 {
521 return (layoutUnits * static_cast<double>(getDeviceResolution()) * static_cast<double>(getZoomPercentage())) / (100.0 * static_cast<double>(getResolution()));
522 }
523
tluD(double deviceUnits) const524 double GR_Graphics::tluD(double deviceUnits) const
525 {
526 return (deviceUnits * static_cast<double>(getResolution()) / static_cast<double>(getDeviceResolution())) * 100.0 / static_cast<double>(getZoomPercentage());
527 }
528
ftlu(UT_sint32 fontUnits) const529 UT_sint32 GR_Graphics::ftlu(UT_sint32 fontUnits) const
530 {
531 UT_sint32 itmp = fontUnits * (UT_sint32)getResolution();
532 return (itmp/ (UT_sint32)getDeviceResolution());
533 }
534
ftluD(double fontUnits) const535 double GR_Graphics::ftluD(double fontUnits) const
536 {
537 return (fontUnits * static_cast<double>(getResolution()) / static_cast<double>(getDeviceResolution()));
538 }
539
setLineProperties(double,JoinStyle,CapStyle,LineStyle)540 void GR_Graphics::setLineProperties ( double /*inWidthPixels*/,
541 JoinStyle /*inJoinStyle*/,
542 CapStyle /*inCapStyle*/,
543 LineStyle /*inLineStyle*/ )
544 {
545 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
546 }
547
getMaxCharacterDimension(const UT_UCSChar * s,UT_uint32 length,UT_uint32 & width,UT_uint32 & height)548 void GR_Graphics::getMaxCharacterDimension(const UT_UCSChar*s, UT_uint32 length, UT_uint32 &width, UT_uint32 &height)
549 {
550 UT_GrowBufElement *pWidths = new UT_GrowBufElement[length];
551
552
553 UT_uint32 maxHeight = 0;
554 measureString(s, 0, length, pWidths, &maxHeight);
555
556 UT_sint32 maxWidth = 0;
557
558 for(UT_uint32 i = 0; i < length; i++)
559 {
560 if(pWidths[i] > maxWidth)
561 maxWidth = pWidths[i];
562 }
563
564 DELETEPV(pWidths);
565
566 width = maxWidth;
567 if (maxHeight > 0) {
568 height = maxHeight;
569 }
570 }
571
measureString(const UT_UCSChar * s,int iOffset,int num,UT_GrowBufElement * pWidths,UT_uint32 *)572 UT_uint32 GR_Graphics::measureString(const UT_UCSChar* s, int iOffset,
573 int num, UT_GrowBufElement* pWidths,
574 UT_uint32 * /*height*/)
575 {
576 // Generic base class version defined in terms of measureUnRemappedChar().
577 // Platform versions can roll their own if it makes a performance difference.
578 UT_ASSERT(s);
579
580 UT_sint32 stringWidth = 0, charWidth;
581 for (int i = 0; i < num; i++)
582 {
583 UT_UCSChar currentChar = s[i + iOffset];
584
585 {
586 charWidth = measureUnRemappedChar(currentChar);
587
588 if(charWidth == GR_CW_UNKNOWN || charWidth ==GR_CW_ABSENT)
589 charWidth = 0;
590 else if(UT_isOverstrikingChar(currentChar) != UT_NOT_OVERSTRIKING && charWidth > 0)
591 charWidth = -charWidth;
592
593 // if the widths is < 0 we are dealing with an
594 // overstriking character, which does not count for
595 // the overall width
596 if(charWidth > 0)
597 stringWidth += charWidth;
598 }
599
600 if (pWidths)
601 pWidths[i] = charWidth;
602 }
603 return stringWidth;
604 }
605
606
setZoomPercentage(UT_uint32 iZoom)607 void GR_Graphics::setZoomPercentage(UT_uint32 iZoom)
608 {
609 UT_ASSERT(iZoom > 0);
610
611 m_iZoomPercentage = iZoom;
612
613 // invalidate stored font allocation number (change of zoom
614 // requires font of different size to be loaded into the device
615 // context)
616 m_iFontAllocNo = 0xffffffff;
617 }
618
619
invertDimension(UT_Dimension dim,double dValue) const620 const char * GR_Graphics::invertDimension(UT_Dimension dim, double dValue) const
621 {
622 // return pointer to static buffer -- use it quickly.
623
624 double dInches = dValue / UT_LAYOUT_RESOLUTION;
625
626 return UT_convertInchesToDimensionString( dim, dInches);
627 }
628
scaleDimensions(const char * szLeftIn,const char * szWidthIn,UT_uint32 iWidthAvail,UT_sint32 * piLeft,UT_uint32 * piWidth) const629 bool GR_Graphics::scaleDimensions(const char * szLeftIn, const char * szWidthIn,
630 UT_uint32 iWidthAvail,
631 UT_sint32 * piLeft, UT_uint32 * piWidth) const
632 {
633 /* Scale the given left-offset and width using the width available.
634 ** Compute the actual left-offset and actual width used.
635 ** We allow the given left-offset to be a number.
636 ** We allow the given width to be a number or "*"; where "*" indicates
637 ** we take all remaining space available.
638 **
639 ** NOTE: This routine can also be used for vertical calculations.
640 */
641
642 UT_ASSERT(szLeftIn);
643 UT_ASSERT(szWidthIn);
644
645 UT_sint32 iLeft = UT_convertToLogicalUnits(szLeftIn);
646 UT_uint32 iWidth;
647
648 if (szWidthIn[0] == '*')
649 iWidth = iWidthAvail - iLeft;
650 else
651 iWidth = UT_convertToLogicalUnits(szWidthIn);
652
653 if (piLeft)
654 *piLeft = iLeft;
655 if (piWidth)
656 *piWidth = iWidth;
657
658 return true;
659 }
660
flush(void)661 void GR_Graphics::flush(void)
662 {
663 // default implementation does nothing
664 }
665 /*!
666 * Draw the specified image at the location specified in local units
667 * (xDest,yDest). xDest and yDest are in logical units.
668 */
drawImage(GR_Image * pImg,UT_sint32 xDest,UT_sint32 yDest)669 void GR_Graphics::drawImage(GR_Image* pImg, UT_sint32 xDest, UT_sint32 yDest)
670 {
671 if (pImg)
672 pImg->render(this, xDest, yDest);
673 }
674
675 /*!
676 This method is just like drawChars() except it treats yoff as position of the font
677 baseline. The default implementation simply subtracts the ascent of the current font
678 from yoff and calls drawChars(), which should work on all platforms except for win32.
679
680 On win32 because of the trickery we use to achieve wysiwyg layout the acent of the
681 font we work with is slightly smaller than that of the actual font the system uses to
682 draw on the screen. As a result, the characters end up positioned slightly higher than
683 they should and this has proved a problem in the math plugin (see screen shots in #9500)
684 */
drawCharsRelativeToBaseline(const UT_UCSChar * pChars,int iCharOffset,int iLength,UT_sint32 xoff,UT_sint32 yoff,int * pCharWidths)685 void GR_Graphics::drawCharsRelativeToBaseline(const UT_UCSChar* pChars,
686 int iCharOffset,
687 int iLength,
688 UT_sint32 xoff,
689 UT_sint32 yoff,
690 int* pCharWidths)
691 {
692 drawChars(pChars, iCharOffset, iLength, xoff, yoff - getFontAscent(), pCharWidths);
693 }
694
695
696 /*!
697 * Create a new image from the Raster rgba byte buffer defined by pBB.
698 * The dimensions of iWidth and iHeight are in logical units but the image
699 * doesn't scale if the resolution or zoom changes. Instead you must create
700 * a new image.
701 */
createNewImage(const char * pszName,const UT_ByteBuf * pBB,const std::string & mimetype,UT_sint32 iDisplayWidth,UT_sint32 iDisplayHeight,GR_Image::GRType iType)702 GR_Image* GR_Graphics::createNewImage(const char* pszName, const UT_ByteBuf* pBB, const std::string& mimetype,
703 UT_sint32 iDisplayWidth, UT_sint32 iDisplayHeight, GR_Image::GRType iType)
704 {
705 GR_VectorImage * vectorImage = NULL;
706
707 if (iType == GR_Image::GRT_Unknown) {
708 if (GR_Image::getBufferType(pBB) == GR_Image::GRT_Vector)
709 vectorImage = new GR_VectorImage(pszName);
710 }
711 else if (iType == GR_Image::GRT_Vector) {
712 vectorImage = new GR_VectorImage(pszName);
713 }
714
715 if (vectorImage) {
716 vectorImage->convertFromBuffer(pBB, mimetype, iDisplayWidth, iDisplayHeight);
717 }
718
719 return vectorImage;
720 }
721
_PtInPolygon(UT_Point * pts,UT_uint32 nPoints,UT_sint32 x,UT_sint32 y)722 bool GR_Graphics::_PtInPolygon(UT_Point * pts,UT_uint32 nPoints,UT_sint32 x,UT_sint32 y)
723 {
724 UT_uint32 i,j;
725 bool bResult = false;
726 for (i = 0,j = nPoints - 1;i < nPoints;j = i++){
727 if ((((pts[i].y <= y) && (y < pts[j].y)) || ((pts[j].y <= y) && (y < pts[i].y))) &&
728 (x < (pts[j].x - pts[i].x) * (y - pts[i].y) / (pts[j].y - pts[i].y) + pts[i].x))
729 {
730 bResult = !bResult;
731 }
732 }
733 return (bResult);
734 }
735
polygon(UT_RGBColor & c,UT_Point * pts,UT_uint32 nPoints)736 void GR_Graphics::polygon(UT_RGBColor& c,UT_Point *pts,UT_uint32 nPoints)
737 {
738 UT_sint32 minX,maxX,minY,maxY,x,y;
739 minX = maxX = pts[0].x;
740 minY = maxY = pts[0].y;
741 for(UT_uint32 i = 0;i < nPoints - 1;i++){
742 minX = UT_MIN(minX,pts[i].x);
743 maxX = UT_MAX(maxX,pts[i].x);
744 minY = UT_MIN(minY,pts[i].y);
745 maxY = UT_MAX(maxY,pts[i].y);
746 }
747 for(x = minX;x <= maxX;x++){
748 for(y = minY;y <= maxY;y++){
749 if(_PtInPolygon(pts,nPoints,x,y)){
750 fillRect(c,x,y,1,1);
751 }
752 }
753 }
754 }
755
756 /*!
757 * This method fills the distination rectangle with a piece of the image pImg.
758 * The size and location of the piece of the image is defined by src.
759 * src and dest are in logical units.
760 */
fillRect(GR_Image * pImg,const UT_Rect & src,const UT_Rect & dest)761 void GR_Graphics::fillRect(GR_Image * pImg, const UT_Rect & src, const UT_Rect & dest)
762 {
763 UT_return_if_fail(pImg);
764 GR_Image * pImageSection = pImg->createImageSegment(this, src);
765 UT_return_if_fail(pImageSection);
766 drawImage(pImageSection,dest.left,dest.top);
767 delete pImageSection;
768 }
769 /*!
770 * Fill the specified rectangle with color defined by "c". The dimensions
771 * of UT_Rect are in logical units.
772 */
fillRect(const UT_RGBColor & c,const UT_Rect & r)773 void GR_Graphics::fillRect(const UT_RGBColor& c, const UT_Rect &r)
774 {
775 fillRect(c, r.left, r.top, r.width, r.height);
776 }
777 #if XAP_DONTUSE_XOR
778 #else
xorRect(UT_sint32 x,UT_sint32 y,UT_sint32 w,UT_sint32 h)779 void GR_Graphics::xorRect(UT_sint32 x, UT_sint32 y, UT_sint32 w, UT_sint32 h)
780 {
781 xorLine(x, y, x + w, y);
782 xorLine(x + w, y, x + w, y + h);
783 xorLine(x + w, y + h, x, y + h);
784 xorLine(x, y + h, x, y);
785 }
786
xorRect(const UT_Rect & r)787 void GR_Graphics::xorRect(const UT_Rect& r)
788 {
789 xorRect(r.left, r.top, r.width, r.height);
790 }
791
792 #endif
793
794 /////////////////////////////////////////////////////////////////////////////////
795 //
796 // COMPLEX SCRIPT PROCESSING FUNCTIONS
797 //
798 //
799
800 /*!
801 itemize() analyses text represented by text, notionally dividing
802 it into segments that from the point of the shaper are uniform;
803 this notional division is stored in GR_Itemization I.
804
805 The default implementation that only deals with bidi reordering
806
807 derrived classes can either provide entirely different
808 implementation, or call the default implementation first and then
809 further divide the results into items with same shaping needs
810 */
811 #ifndef ABI_GRAPHICS_PLUGIN
itemize(UT_TextIterator & text,GR_Itemization & I)812 bool GR_Graphics::itemize(UT_TextIterator & text, GR_Itemization & I)
813 {
814 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
815
816 I.clear();
817 UT_uint32 iCurOffset = 0, iLastOffset = 0;
818 UT_uint32 iPosStart = text.getPosition();
819
820 // the main loop that will span the whole text of the iterator
821 while(text.getStatus() == UTIter_OK)
822 {
823 UT_BidiCharType iPrevType, iLastStrongType = UT_BIDI_UNSET, iType;
824
825 UT_UCS4Char c = text.getChar();
826
827 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
828
829 iType = UT_bidiGetCharType(c);
830 #if 0
831 // this branch of code breaks at all direction bounaries
832 // it is disabled because doing that causes bug 8099
833 iCurOffset = iLastOffset = text.getPosition();
834 ++text;
835
836 // this loop will cover a single homogenous item
837 while(text.getStatus() == UTIter_OK)
838 {
839 iPrevType = iType;
840
841 c = text.getChar();
842 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
843
844 // remember the offset
845 iLastOffset = text.getPosition();
846
847 iType = UT_bidiGetCharType(c);
848 if(iType != iPrevType)
849 {
850 break;
851 }
852
853 ++text;
854 }
855 #else
856 //we have to break the text into chunks that each will go into a
857 //separate run in a manner that will ensure that the text will
858 //be correctly processed later. The most obvious way is to
859 //break every time we encounter a change of directional
860 //properties. Unfortunately that means breaking at each white
861 //space, which adds a huge amount of processing due to
862 //allocating and deleting runs when loading a
863 //document. The following code tries to catch out the obvious
864 //cases when the span can remain intact. Tomas, Jan 28, 2003
865
866 // remember where we are ...
867 iCurOffset = iLastOffset = text.getPosition();
868 ++text;
869
870 UT_BidiCharType iNextType;
871
872 // this loop will cover a single homogenous item
873 while(text.getStatus() == UTIter_OK)
874 {
875 iPrevType = iType;
876 if(UT_BIDI_IS_STRONG(iType))
877 iLastStrongType = iType;
878
879 c = text.getChar();
880 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
881
882 // remember the offset
883 iLastOffset = text.getPosition();
884 ++text;
885
886 iType = UT_bidiGetCharType(c);
887 if(iType != iPrevType)
888 {
889 // potential direction boundary see if we can ignore
890 // it
891 bool bIgnore = false;
892 #if 0
893 // this assumption is not true; for instance in the
894 // sequence ") " the parenthesis and the space can
895 // resolve to different directions
896 //
897 // I am leaving it here so that I do not add it one
898 // day again (Tomas, Apr 10, 2003)
899
900 if(UT_BIDI_IS_NEUTRAL(iPrevType) && UT_BIDI_IS_NEUTRAL(iType))
901 {
902 // two neutral characters in a row will have the same
903 // direction
904 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: ntrl->ntrl (c=0x%04x)\n",c));
905 bIgnore = true;
906 }
907 else
908 #endif
909 if(UT_BIDI_IS_STRONG(iPrevType) && UT_BIDI_IS_NEUTRAL(iType))
910 {
911 // we can ignore a neutral character following a
912 // strong one if it is followed by a strong
913 // character of identical type to the previous one
914 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: strong->ntrl (c=0x%04x)\n",c));
915
916 // take a peek at what follows
917 UT_uint32 iOldPos = text.getPosition();
918
919 while(text.getStatus() == UTIter_OK)
920 {
921 UT_UCS4Char c2 = text.getChar();
922 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
923
924 ++text;
925
926 iNextType = UT_bidiGetCharType(c2);
927 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: iNextType 0x%04x\n", iNextType));
928
929 if(iNextType == iPrevType)
930 {
931 bIgnore = true;
932 break;
933 }
934
935 // if the next character is strong, we cannot
936 // ignore the boundary
937 if(UT_BIDI_IS_STRONG(iNextType))
938 break;
939 }
940
941 // restore position
942 text.setPosition(iOldPos);
943 }
944 else if(UT_BIDI_IS_NEUTRAL(iPrevType) && UT_BIDI_IS_STRONG(iType))
945 {
946 // a neutral character followed by a strong one -- we
947 // can ignore it, if the neutral character was
948 // preceeded by a strong character of the same
949 // type
950 if(iType == iLastStrongType)
951 {
952 bIgnore = true;
953 }
954 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: ntrl->strong (c=0x%04x)\n",c));
955 }
956 else
957 {
958 // in all other cases we will split
959 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: other (c=0x%04x)\n",pSpan[i]));
960 }
961
962 xxx_UT_DEBUGMSG(("GR_Graphics::itemize: bIgnore %d\n",static_cast<UT_uint32>(bIgnore)));
963 if(!bIgnore)
964 break;
965 }
966
967 }
968 #endif
969
970 I.addItem(iCurOffset - iPosStart, new GR_XPItem(GRScriptType_Undefined));
971 }
972
973 // add an extra record of type Void to allow for calculation of
974 // length of the last item
975 // iLastOffset is the offset of the last valid character; the Void
976 // offset is one beyond that
977 I.addItem(iLastOffset - iPosStart + 1, new GR_XPItem(GRScriptType_Void));
978 return true;
979 }
980
981 /*!
982 this function remaps a number of specialised glyphs to reasonable
983 alternatives
984 */
s_remapGlyph(UT_UCS4Char g)985 static UT_UCS4Char s_remapGlyph(UT_UCS4Char g)
986 {
987 // various hyphens
988 if(g >= 0x2010 && g <= 0x2015) return '-';
989
990 // various quotes
991 if(g >= 0x2018 && g <= 0x201b) return '\'';
992 if(g == 0x2039) return '<';
993 if(g == 0x203a) return '>';
994 if(g >= 0x201c && g <= 0x201f) return '\"';
995
996 // bullets
997 if(g == 0x2022 || g == 0x2023) return '*';
998
999 // miscell. punctuation
1000 if(g == 0x2044) return '/';
1001 if(g == 0x2045) return '[';
1002 if(g == 0x2046) return ']';
1003 if(g == 0x2052) return '%';
1004 if(g == 0x2053) return '~';
1005
1006 // currency symbols
1007 if(g == 0x20a3) return 'F';
1008 if(g == 0x20a4) return 0x00a3;
1009 if(g == 0x20ac) return 'E';
1010
1011 // letter like symbols
1012 if(g == 0x2103) return 'C';
1013 if(g == 0x2109) return 'F';
1014 if(g == 0x2117) return 0x00a9;
1015 if(g == 0x2122) return 'T'; // TM symbol, this is not satisfactory, but ...
1016 if(g == 0x2126) return 0x03a9;
1017 if(g == 0x212a) return 'K';
1018
1019 // dingbats
1020 if(g >= 0x2715 && g <= 0x2718) return 0x00d7;
1021 if(g >= 0x2719 && g <= 0x2720) return '+';
1022 if(g == 0x271) return '*';
1023 if(g >= 0x2722 && g <= 0x2727) return '+';
1024 if(g >= 0x2728 && g <= 0x274b) return '*';
1025 if(g >= 0x2758 && g <= 0x275a) return '|';
1026 if(g == 0x275b || g == 0x275c) return '\'';
1027 if(g == 0x275d || g == 0x275e) return '\"';
1028 if(g == 0x2768 || g == 0x276a) return '(';
1029 if(g == 0x2769 || g == 0x276b) return ')';
1030 if(g == 0x276c || g == 0x276e || g == 0x2770) return '<';
1031 if(g == 0x276d || g == 0x276f || g == 0x2771) return '>';
1032 if(g == 0x2772) return '[';
1033 if(g == 0x2773) return ']';
1034 if(g == 0x2774) return '{';
1035 if(g == 0x2775) return '}';
1036 if(g >= 0x2776 && g <= 0x2793) return ((g-0x2775)%10 + '0');
1037
1038 return g;
1039 }
1040
1041 // if the character has a mirror form, returns the mirror form,
1042 // otherwise returns the character itself
s_getMirrorChar(UT_UCSChar c)1043 static UT_UCSChar s_getMirrorChar(UT_UCSChar c)
1044 {
1045 //got to do this, otherwise bsearch screws up
1046 UT_UCS4Char mc;
1047
1048 if (UT_bidiGetMirrorChar(c,mc))
1049 return mc;
1050 else
1051 return c;
1052 }
1053
1054 /*!
1055 shape() processes the information encapsulated by GR_ShapingInfo
1056 si and stores results in GR_*RenderInfo* pri.
1057
1058 If the contents of pri are NULL the function must create a new
1059 instance of GR_*RenderInfo of the appropriate type and store the
1060 pointer in pri; it also must store pointer to this graphics
1061 instance in pri->m_pGraphics.
1062
1063 If ri indicates that the text is justified, appropriate processing
1064 needs to be done
1065
1066 This function is tied closely together to a class derrived from
1067 GR_RenderInfo which may contain caches of various data that will
1068 speed subsequent calls to prepareToRenderChars() and renderChars()
1069 */
shape(GR_ShapingInfo & si,GR_RenderInfo * & pri)1070 bool GR_Graphics::shape(GR_ShapingInfo & si, GR_RenderInfo *& pri)
1071 {
1072 if(!si.m_pItem || si.m_pItem->getType() == GRScriptType_Void || !si.m_pFont)
1073 return false;
1074
1075 if(!pri)
1076 {
1077 pri = new GR_XPRenderInfo(si.m_pItem->getType());
1078 UT_return_val_if_fail(pri, false);
1079 pri->m_pGraphics = this;
1080 }
1081
1082 GR_XPRenderInfo * pRI = (GR_XPRenderInfo *)pri;
1083
1084 const GR_Font *pFont = si.m_pFont;
1085
1086 // make sure that the buffers are of sufficient size ...
1087 if(si.m_iLength > pRI->m_iBufferSize) //buffer too small, reallocate
1088 {
1089 delete[] pRI->m_pChars;
1090 delete[] pRI->m_pWidths;
1091
1092 pRI->m_pChars = new UT_UCS4Char[si.m_iLength + 1];
1093 UT_return_val_if_fail(pRI->m_pChars, false);
1094
1095 pRI->m_pWidths = new UT_sint32[si.m_iLength + 1];
1096 UT_return_val_if_fail(pRI->m_pWidths, false);
1097
1098 pRI->m_iBufferSize = si.m_iLength + 1;
1099 }
1100
1101 pRI->m_iLength = si.m_iLength;
1102 pRI->m_iTotalLength = si.m_iLength;
1103 pRI->m_eScriptType = si.m_pItem->getType();
1104 pRI->m_pItem = si.m_pItem;
1105
1106 UT_UCS4Char glyph, current;
1107 UT_UCS4Char * dst_ptr = pRI->m_pChars;
1108 bool previousWasSpace = si.m_previousWasSpace;
1109
1110 for(UT_sint32 i = 0; i < si.m_iLength; ++i, ++si.m_Text)
1111 {
1112 UT_return_val_if_fail(si.m_Text.getStatus() == UTIter_OK, false);
1113 current = si.m_Text.getChar();
1114
1115 if (si.m_TextTransform == GR_ShapingInfo::LOWERCASE)
1116 current = g_unichar_tolower(current);
1117 else if (si.m_TextTransform == GR_ShapingInfo::UPPERCASE)
1118 current = g_unichar_toupper(current);
1119 else if (si.m_TextTransform == GR_ShapingInfo::CAPITALIZE) {
1120 if (previousWasSpace) {
1121 current = g_unichar_toupper(current);
1122 }
1123 } // else si.m_TextTransform == GR_ShapingInfo::NONE
1124
1125 previousWasSpace = g_unichar_isspace(current);
1126
1127 if(si.m_iVisDir == UT_BIDI_RTL)
1128 glyph = s_getMirrorChar(current);
1129 else
1130 glyph = current;
1131
1132 if(pFont->doesGlyphExist(glyph))
1133 *dst_ptr++ = glyph;
1134 else
1135 {
1136 UT_UCS4Char t = s_remapGlyph(glyph);
1137 if(pFont->doesGlyphExist(t))
1138 {
1139 *dst_ptr++ = t;
1140 }
1141 else
1142 {
1143 *dst_ptr++ = s_cDefaultGlyph;
1144 }
1145 }
1146 }
1147
1148 pRI->m_eState = GRSR_BufferClean;
1149
1150 if(pRI->isJustified())
1151 justify(*pRI);
1152
1153 // make sure that we invalidate the static buffers if we own them
1154 if(pRI->s_pOwner == pRI)
1155 pRI->s_pOwner = NULL;
1156
1157 return true;
1158 }
1159
appendRenderedCharsToBuff(GR_RenderInfo & ri,UT_GrowBuf & buf) const1160 void GR_Graphics::appendRenderedCharsToBuff(GR_RenderInfo & ri, UT_GrowBuf & buf) const
1161 {
1162 UT_return_if_fail(ri.getType() == GRRI_XP);
1163
1164 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
1165 buf.append(reinterpret_cast<UT_GrowBufElement *>(RI.m_pChars),RI.m_iLength);
1166 }
1167
getTextWidth(GR_RenderInfo & ri)1168 UT_sint32 GR_Graphics::getTextWidth(GR_RenderInfo & ri)
1169 {
1170 UT_return_val_if_fail(ri.getType() == GRRI_XP, 0);
1171 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
1172
1173 // NB: the width array is in VISUAL order, but offset is a logical offset
1174 bool bReverse = (ri.m_iVisDir == UT_BIDI_RTL);
1175
1176 UT_sint32 iWidth = 0;
1177 for (UT_sint32 i = ri.m_iOffset; i < ri.m_iLength + ri.m_iOffset; ++i)
1178 {
1179 UT_uint32 k = i;
1180
1181 if(bReverse)
1182 {
1183 if((static_cast<UT_sint32>(RI.m_iTotalLength) - i - 1) < 0)
1184 {
1185 UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1186 continue;
1187 }
1188
1189 k = RI.m_iTotalLength - i - 1;
1190 }
1191
1192 UT_uint32 iCW = RI.m_pWidths[k] > 0 ? RI.m_pWidths[k] : 0;
1193 iWidth += iCW;
1194 }
1195
1196 return iWidth;
1197 }
1198
1199
measureRenderedCharWidths(GR_RenderInfo & ri)1200 void GR_Graphics::measureRenderedCharWidths(GR_RenderInfo & ri)
1201 {
1202 UT_return_if_fail(ri.getType() == GRRI_XP);
1203 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
1204 UT_return_if_fail(RI.m_pWidths);
1205
1206 //bool bReverse = (RI.m_iVisDir == UT_BIDI_RTL);
1207
1208 UT_sint32 i;
1209
1210 for (i = 0; i < RI.m_iLength; i++)
1211 {
1212 if(i > 0 && *(RI.m_pChars + i) == UCS_LIGATURE_PLACEHOLDER)
1213 {
1214 RI.m_pWidths[i] = RI.m_pWidths[i - 1]/2;
1215 UT_uint32 mod = RI.m_pWidths[i-1]%2;
1216 RI.m_pWidths[i-1] = RI.m_pWidths[i] + mod;
1217 }
1218 else
1219 {
1220
1221 measureString(RI.m_pChars + i, 0, 1,
1222 static_cast<UT_GrowBufElement*>(RI.m_pWidths) + i);
1223 }
1224 }
1225
1226 if(RI.isJustified())
1227 {
1228 justify(RI);
1229 }
1230
1231 // make sure that we invalidate the static buffers if we own them
1232 if(RI.s_pOwner == &RI)
1233 RI.s_pOwner = NULL;
1234
1235 }
1236
1237 /*!
1238 prepareToRenderChars() does any preprocessing necessary immediately
1239 prior to the actual output on screen (which is done by
1240 renderChars()), and must be always called before renderChars().
1241
1242 What this function does is entirely dependend of the specific
1243 shaping engine (and in some cases might not do anthing at all). For
1244 example, this function might refresh any temporary buffers,
1245 etc.
1246
1247 The reason for dividing the actual drawing into two steps (prepare
1248 and render) is to limit the amount of processing in cases where the
1249 caller needs to make several calls to renderChars() with the same
1250 GR_RenderInfo. For example, fp_TextRun::_draw() draws the text in
1251 one, two or three segments depending whether there is a selection
1252 and where in the run it is. It will make one call to
1253 prepareToRenderChars() and then 1-3 calls to renderChars() chaning
1254 the background and text colour in between those calls.
1255 */
prepareToRenderChars(GR_RenderInfo & ri)1256 void GR_Graphics::prepareToRenderChars(GR_RenderInfo & ri)
1257 {
1258 UT_return_if_fail(ri.getType() == GRRI_XP);
1259 GR_XPRenderInfo & RI = (GR_XPRenderInfo &)ri;
1260 RI.prepareToRenderChars();
1261 }
1262
1263 /*!
1264 renderChars() outputs textual data represented by ri onto the device (screen,
1265 priter, etc.).
1266
1267 The output starts at ri.m_iOffset, is ri.m_iLength
1268 long and the drawing starts at ri.m_xoff, ri.m_yoff
1269 */
renderChars(GR_RenderInfo & ri)1270 void GR_Graphics::renderChars(GR_RenderInfo & ri)
1271 {
1272 UT_return_if_fail(ri.getType() == GRRI_XP);
1273 GR_XPRenderInfo & RI = (GR_XPRenderInfo &)ri;
1274
1275 drawChars(RI.s_pCharBuff,RI.m_iOffset,RI.m_iLength,RI.m_xoff,RI.m_yoff,RI.s_pAdvances);
1276
1277
1278 }
1279
1280 /*!
1281 return true if linebreak at character c is permissible
1282 */
canBreak(GR_RenderInfo & ri,UT_sint32 & iNext,bool bAfter)1283 bool GR_Graphics::canBreak(GR_RenderInfo & ri, UT_sint32 &iNext, bool bAfter)
1284 {
1285 UT_UCS4Char c[2];
1286
1287 // Default to -1.
1288 iNext = -1;
1289
1290 // Check the iterator is OK.
1291 UT_return_val_if_fail(ri.m_pText && ri.m_pText->getStatus() == UTIter_OK, false);
1292
1293 // Advance the iterator by the given offset.
1294 *(ri.m_pText) += ri.m_iOffset;
1295 // Check that we haven't run off the end of the iterator.
1296 UT_return_val_if_fail(ri.m_pText->getStatus() == UTIter_OK, false);
1297
1298 // Fetch a pointer to the Encoding manager.
1299 UT_return_val_if_fail(XAP_App::getApp(), false);
1300 const XAP_EncodingManager *encMan = XAP_App::getApp()->getEncodingManager();
1301 UT_return_val_if_fail(encMan, false);
1302
1303 // Set up c[] appropriately depending on whether we're looking
1304 // for break before or after.
1305 if (bAfter)
1306 {
1307 c[1] = ri.m_pText->getChar();
1308 }
1309 else
1310 {
1311 --(*ri.m_pText);
1312 c[1] = ri.m_pText->getChar();
1313 }
1314
1315 // Make sure we managed to get the character we wanted.
1316 if (c[1] == UT_IT_ERROR)
1317 return false;
1318
1319 UT_uint32 iCount = ri.m_iOffset;
1320 do
1321 {
1322 ++(*ri.m_pText);
1323 c[0] = c[1];
1324 c[1] = ri.m_pText->getChar();
1325
1326 // If we reach the end of the document then return false
1327 // (and leave iNext set to -1).
1328 if (c[1] == UT_IT_ERROR)
1329 return false;
1330 iCount++;
1331 }
1332 while (!encMan->canBreakBetween(c));
1333
1334 // Set iNext.
1335 iNext = iCount - 1;
1336
1337 // If a break was possible between the first character pair
1338 // then return true.
1339 if (iNext == ri.m_iOffset)
1340 return true;
1341 // ...otherwise, return false.
1342 return false;
1343 }
1344
1345 /*!
1346 resetJustification() makes the data represented by ri unjustified
1347 and returns value by which the total width changed as a result such
1348 that OriginalWidth + ReturnValue = NewWidth (i.e., the return
1349 value should normally be negative).
1350
1351 The parameter bPermanent indicates that the resetting is permanent
1352 and any buffers used to hold justification information can be
1353 remove, e.g., the paragraph alignment has changed from justified to
1354 left (in some circumstance the reset can be only temporary and we
1355 will be asked to subsequently recalculate the justification
1356 information; in such case it makes sense to keep the buffers in place)
1357
1358 */
resetJustification(GR_RenderInfo & ri,bool)1359 UT_sint32 GR_Graphics::resetJustification(GR_RenderInfo & ri, bool /* bPermanent*/)
1360 {
1361 UT_return_val_if_fail(ri.getType() == GRRI_XP, 0);
1362 GR_XPRenderInfo & RI = (GR_XPRenderInfo &)ri;
1363
1364 UT_return_val_if_fail(RI.m_pChars && RI.m_pWidths, 0);
1365
1366 UT_sint32 iAccumDiff = 0;
1367
1368 if(RI.isJustified())
1369 {
1370 UT_sint32 iSpaceWidthBefore = RI.m_iSpaceWidthBeforeJustification;
1371
1372 if(RI.m_pWidths == NULL)
1373 {
1374 return 0;
1375 }
1376
1377 for(UT_sint32 i = 0; i < RI.m_iLength; ++i)
1378 {
1379 if(RI.m_pChars[i] != UCS_SPACE)
1380 continue;
1381
1382 if(RI.m_pWidths[i] != iSpaceWidthBefore)
1383 {
1384 iAccumDiff += iSpaceWidthBefore - RI.m_pWidths[i];
1385 RI.m_pWidths[i] = iSpaceWidthBefore;
1386 }
1387 }
1388
1389 RI.m_iSpaceWidthBeforeJustification = 0xfffffff; // note one less 'f'
1390 RI.m_iJustificationPoints = 0;
1391 RI.m_iJustificationAmount = 0;
1392
1393 if(RI.s_pOwner == &RI)
1394 RI.s_pOwner = NULL;
1395
1396 }
1397
1398 return iAccumDiff;
1399 }
1400
1401 /*!
1402 countJustificationPoints() counts the number of points between
1403 which any extra justification width could be distributed (in Latin
1404 text these are typically spaces).spaces in the text;
1405
1406 The function returns the count as negative value if
1407 the run contains only blank data (i.e., only spaces in Latin text).
1408 */
countJustificationPoints(const GR_RenderInfo & ri) const1409 UT_sint32 GR_Graphics::countJustificationPoints(const GR_RenderInfo & ri) const
1410 {
1411 UT_return_val_if_fail(ri.getType() == GRRI_XP, 0);
1412 GR_XPRenderInfo & RI = (GR_XPRenderInfo &)ri;
1413
1414 UT_return_val_if_fail(RI.m_pChars, 0);
1415
1416 UT_sint32 iCount = 0;
1417 bool bNonBlank = false;
1418
1419 for(UT_sint32 i = (UT_sint32)RI.m_iLength-1; i >= 0; --i)
1420 {
1421 if(RI.m_pChars[i] != UCS_SPACE)
1422 {
1423 bNonBlank = true;
1424 continue;
1425 }
1426
1427 // only count this space if this is not last run, or if we
1428 // have found something other than spaces
1429 if(!ri.m_bLastOnLine || bNonBlank)
1430 iCount++;
1431 }
1432
1433 if(!bNonBlank)
1434 {
1435 return -iCount;
1436 }
1437 else
1438 {
1439 return iCount;
1440 }
1441 }
1442
1443 /*!
1444 justify() distributes justification information into the text.
1445
1446 The justification information consists of ri.m_iJustificationPoints and
1447 m_iJustificationAmount
1448
1449 NB: This function must not modify the original values in
1450 ri.m_iJustificationAmount and ri.m_iJustificationPoints
1451 */
justify(GR_RenderInfo & ri)1452 void GR_Graphics::justify(GR_RenderInfo & ri)
1453 {
1454 UT_return_if_fail(ri.getType() == GRRI_XP);
1455 GR_XPRenderInfo & RI = (GR_XPRenderInfo &)ri;
1456
1457 UT_return_if_fail(RI.m_pChars && RI.m_pWidths);
1458
1459 // need to leave the original values alone ...
1460 UT_uint32 iPoints = ri.m_iJustificationPoints;
1461 UT_sint32 iAmount = ri.m_iJustificationAmount;
1462
1463
1464 if(!iAmount)
1465 {
1466 // this can happend near the start of the line (the line is
1467 // processed from back to front) due to rounding errors in
1468 // the algorithm; we simply mark the run as one that does not
1469 // use justification
1470
1471 // not needed, since we are always called after ::resetJustification()
1472 // resetJustification();
1473 return;
1474 }
1475
1476 if(iPoints)
1477 {
1478 for(UT_sint32 i = 0; i < RI.m_iLength; ++i)
1479 {
1480 if(RI.m_pChars[i] != UCS_SPACE)
1481 continue;
1482
1483 RI.m_iSpaceWidthBeforeJustification = RI.m_pWidths[i];
1484
1485 UT_sint32 iThisAmount = iAmount / iPoints;
1486
1487 RI.m_pWidths[i] += iThisAmount;
1488
1489 xxx_UT_DEBUGMSG(("Space at loc %d new width %d given extra width %d \n",
1490 i,pCharWidths[i],iThisAmount));
1491
1492 iAmount -= iThisAmount;
1493
1494 iPoints--;
1495
1496 if(!iPoints)
1497 break;
1498 }
1499
1500 if(RI.s_pOwner == &RI)
1501 RI.s_pOwner = NULL;
1502 }
1503 }
1504
XYToPosition(const GR_RenderInfo & ri,UT_sint32,UT_sint32) const1505 UT_uint32 GR_Graphics::XYToPosition(const GR_RenderInfo & ri, UT_sint32 /*x*/,
1506 UT_sint32 /*y*/) const
1507 {
1508 UT_return_val_if_fail(ri.getType() == GRRI_XP, 0);
1509 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
1510 UT_return_val_if_fail(RI.m_pWidths, 0);
1511
1512 UT_return_val_if_fail(UT_NOT_IMPLEMENTED,0 );
1513 return 0;
1514 }
1515
positionToXY(const GR_RenderInfo & ri,UT_sint32 &,UT_sint32 &,UT_sint32 &,UT_sint32 &,UT_sint32 &,bool &) const1516 void GR_Graphics::positionToXY(const GR_RenderInfo & ri,
1517 UT_sint32& /*x*/, UT_sint32& /*y*/,
1518 UT_sint32& /*x2*/, UT_sint32& /*y2*/,
1519 UT_sint32& /*height*/, bool& /*bDirection*/) const
1520 {
1521 UT_return_if_fail(ri.getType() == GRRI_XP);
1522 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) ri;
1523 UT_return_if_fail(RI.m_pWidths);
1524
1525 UT_return_if_fail(UT_NOT_IMPLEMENTED);
1526 }
1527
adjustCaretPosition(GR_RenderInfo & ri,bool)1528 UT_uint32 GR_Graphics::adjustCaretPosition(GR_RenderInfo & ri, bool /*bForward*/)
1529 {
1530 return ri.m_iOffset;
1531 }
1532
adjustDeletePosition(GR_RenderInfo &)1533 void GR_Graphics::adjustDeletePosition(GR_RenderInfo & )
1534 {
1535 return;
1536 }
1537
1538 #endif // #ifndef ABI_GRAPHICS_PLUGIN
1539
1540 ///////////////////////////////////////////////////////////////////////////////
1541 //
1542 // IMPLEMNATION OF GR_GraphicsFactory
1543 //
1544 // GR_GraphicsFactory allows parallel existence of differnt
1545 // implementations of GR_Graphics class, so that the graphics
1546 // class used by the application can be changed at runtime.
1547 //
1548 // The factory is accessed via XAP_App functions.
1549 //
1550 // Each derrived class needs to be registered with the factory using
1551 // the registerClass() function; new instances of the graphics class
1552 // are obtained by newGraphics()
1553 //
1554 //
1555 //
1556
1557
1558 /*!
1559 Registers the class allocator and descriptor functions with the
1560 factory.
1561
1562 allocator is a static intermediary to the graphics constructor; it
1563 takes a parameter of type GR_AllocInfo*, which should point to any
1564 data the allocator needs to pass onto the constructor.
1565
1566 descriptor is a static function that returns a string that
1567 describes the graphics class in human-readable manner (e.g., to be
1568 shown in dialogues).
1569
1570 Returns true on success, false if requested id is already
1571 registered (except for IDs < GRID_LAST_DEFAULT, which will
1572 automatically be reassigned).
1573
1574 This function is to be used by built-in classes; plugins
1575 should use registerPluginClass() instead.
1576
1577 The default platform implementation of the graphics class should
1578 register itself twice, once with its predefined id and once as the
1579 default class.
1580 */
registerClass(GR_Allocator allocator,GR_Descriptor descriptor,UT_uint32 iClassId)1581 bool GR_GraphicsFactory::registerClass(GR_Allocator allocator, GR_Descriptor descriptor,
1582 UT_uint32 iClassId)
1583 {
1584 UT_return_val_if_fail(allocator && descriptor && iClassId > GRID_LAST_DEFAULT, false);
1585
1586 UT_sint32 indx = m_vClassIds.findItem(iClassId);
1587
1588 if(indx >= 0)
1589 {
1590 return false;
1591 }
1592
1593 m_vAllocators.addItem(allocator);
1594 m_vDescriptors.addItem(descriptor);
1595 m_vClassIds.addItem((UT_sint32)iClassId);
1596
1597 return true;
1598 }
1599
1600
1601 /*!
1602 As registerClass() but to be used by plugins; it automatically
1603 allocates an id for the class by which it will be identified.
1604
1605 \return id > 0 on success, 0 on failure
1606 */
registerPluginClass(GR_Allocator allocator,GR_Descriptor descriptor)1607 UT_uint32 GR_GraphicsFactory::registerPluginClass(GR_Allocator allocator, GR_Descriptor descriptor)
1608 {
1609 UT_return_val_if_fail(allocator && descriptor, 0);
1610
1611 static UT_uint32 iLastId = GRID_LAST_EXTENSION;
1612 iLastId++;
1613
1614 while(iLastId < GRID_UNKNOWN && !registerClass(allocator,descriptor, iLastId))
1615 iLastId++;
1616
1617 if(iLastId != GRID_UNKNOWN)
1618 return iLastId;
1619
1620 return 0;
1621 }
1622
1623
1624 /*!
1625 Unregisteres class with the given id; this function is only to be
1626 used by plugins.
1627
1628 Returns true on success.
1629
1630 The caller must never try to unregister any other class that
1631 itself; the sole exception to this is GRID_DEFAULT
1632
1633 The built-in graphics classes cannot be unregistered.
1634 */
unregisterClass(UT_uint32 iClassId)1635 bool GR_GraphicsFactory::unregisterClass(UT_uint32 iClassId)
1636 {
1637 // cannot unregister built-in classes
1638 UT_return_val_if_fail(iClassId > GRID_LAST_BUILT_IN, false);
1639
1640 // cannot unregister the default graphics class, hopefully the
1641 // rogue plugin will pay attention to the return value
1642 UT_return_val_if_fail(iClassId == m_iDefaultScreen || iClassId == m_iDefaultPrinter, false);
1643
1644 UT_sint32 indx = m_vClassIds.findItem(iClassId);
1645
1646 if(indx < 0)
1647 return false;
1648
1649 m_vClassIds.deleteNthItem(indx);
1650 m_vAllocators.deleteNthItem(indx);
1651 m_vDescriptors.deleteNthItem(indx);
1652
1653 return true;
1654 }
1655
1656 /*!
1657 Creates an instance of the graphics class represented by iClassId,
1658 passing param to the class allocator.
1659 */
newGraphics(UT_uint32 iClassId,GR_AllocInfo & param) const1660 GR_Graphics * GR_GraphicsFactory::newGraphics(UT_uint32 iClassId, GR_AllocInfo ¶m) const
1661 {
1662 if(iClassId == GRID_DEFAULT)
1663 iClassId = m_iDefaultScreen;
1664
1665 if(iClassId == GRID_DEFAULT_PRINT)
1666 iClassId = m_iDefaultPrinter;
1667
1668 UT_sint32 indx = m_vClassIds.findItem(iClassId);
1669
1670 if(indx < 0)
1671 return NULL;
1672
1673 GR_Allocator alloc = m_vAllocators.getNthItem(indx);
1674
1675 if(!alloc)
1676 return NULL;
1677
1678 return alloc(param);
1679 }
1680
getClassDescription(UT_uint32 iClassId) const1681 const char * GR_GraphicsFactory::getClassDescription(UT_uint32 iClassId) const
1682 {
1683 if(iClassId == GRID_DEFAULT)
1684 iClassId = m_iDefaultScreen;
1685
1686 if(iClassId == GRID_DEFAULT_PRINT)
1687 iClassId = m_iDefaultPrinter;
1688
1689 UT_sint32 indx = m_vClassIds.findItem(iClassId);
1690
1691 if(indx < 0)
1692 return NULL;
1693
1694 GR_Descriptor descr = m_vDescriptors.getNthItem(indx);
1695
1696 if(!descr)
1697 return NULL;
1698
1699 return descr();
1700
1701 }
1702
isRegistered(UT_uint32 iClassId) const1703 bool GR_GraphicsFactory::isRegistered(UT_uint32 iClassId) const
1704 {
1705 UT_sint32 indx = m_vClassIds.findItem(iClassId);
1706
1707 if(indx < 0)
1708 return false;
1709
1710 return true;
1711 }
1712
1713 #if defined(WITH_CAIRO)
1714 #include "gr_CairoNullGraphics.h"
1715 #elif defined(TOOLKIT_WIN)
1716 #include "gr_Win32Graphics.h"
1717 #else
1718 #warning un-handled case
1719 #endif
1720
1721 /**
1722 * Creates an offscreen graphics context. Only used for measuring font metrics and whatnot, not actually for drawing
1723 */
1724 /* static */
newNullGraphics()1725 GR_Graphics* GR_Graphics::newNullGraphics()
1726 {
1727 // todo: support other platforms when possible
1728
1729 #if defined(WITH_CAIRO)
1730 GR_CairoNullGraphicsAllocInfo ai;
1731 return XAP_App::getApp()->newGraphics(GRID_CAIRO_NULL, (GR_AllocInfo&)ai);
1732 #elif defined(TOOLKIT_WIN)
1733 GR_Win32AllocInfo ai (GR_Win32Graphics::createbestmetafilehdc(), GR_Win32Graphics::getDocInfo(), NULL);
1734 return XAP_App::getApp()->newGraphics(GRID_WIN32, (GR_AllocInfo&)ai);
1735 #else
1736 #endif
1737
1738 return NULL;
1739 }
1740