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 &param) 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