1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3  * Copyright (C) 2004-2007 Tomas Frydrych
4  * Copyright (C) 2009 Hubert Figuiere
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 <string>
27 
28 #include "gr_CairoGraphics.h"
29 #include "gr_Painter.h"
30 
31 #include "xap_App.h"
32 #include "xap_Prefs.h"
33 #include "xap_Strings.h"
34 #include "xap_Frame.h"
35 
36 #include "ut_debugmsg.h"
37 #include "ut_misc.h"
38 #include "ut_locale.h"
39 #include "ut_std_string.h"
40 #include "ut_std_vector.h"
41 
42 // need this to include what Pango considers 'low-level' api
43 #define PANGO_ENABLE_ENGINE
44 
45 #include <pango/pango-item.h>
46 #include <pango/pango-engine.h>
47 #include <pango/pangocairo.h>
48 
49 #include <math.h>
50 
51 // only became "public" in pango 1.20. see http://bugzilla.gnome.org/show_bug.cgi?id=472303
52 #ifndef PANGO_GLYPH_EMPTY
53 #define PANGO_GLYPH_EMPTY ((PangoGlyph)0x0FFFFFFF)
54 #endif
55 
56 #if !PANGO_VERSION_CHECK(1,22,0)
57 // stuff deprecated in 1.22....
pango_font_map_create_context(PangoFontMap * fontmap)58 PangoContext* pango_font_map_create_context(PangoFontMap* fontmap)
59 {
60 	return pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(fontmap));
61 }
62 #endif
63 
64 UT_uint32 adobeDingbatsToUnicode(UT_uint32 iAdobe);
65 UT_uint32 adobeToUnicode(UT_uint32 iAdobe);
66 
67 UT_uint32      GR_CairoGraphics::s_iInstanceCount = 0;
68 UT_VersionInfo GR_CairoGraphics::s_Version;
69 int            GR_CairoGraphics::s_iMaxScript = 0;
70 
71 
72 
73 /*!
74  * The idea is to create a
75  * new image from the rectangular segment in device units defined by
76  * UT_Rect rec. The Image should be deleted by the calling routine.
77  */
createImageSegment(GR_Graphics * pG,const UT_Rect & rec)78 GR_Image * GR_CairoRasterImage::createImageSegment(GR_Graphics * pG,const UT_Rect & rec)
79 {
80 	UT_sint32 x = pG->tdu(rec.left);
81 	UT_sint32 y = pG->tdu(rec.top);
82 	if(x < 0)
83 	{
84 		x = 0;
85 	}
86 	if(y < 0)
87 	{
88 		y = 0;
89 	}
90 	UT_sint32 width = pG->tdu(rec.width);
91 	UT_sint32 height = pG->tdu(rec.height);
92 	UT_sint32 dH = getDisplayHeight();
93 	UT_sint32 dW = getDisplayWidth();
94 	if(height > dH)
95 	{
96 		height = dH;
97 	}
98 	if(width > dW)
99 	{
100 		width = dW;
101 	}
102 	if(x + width > dW)
103 	{
104 		width = dW - x;
105 	}
106 	if(y + height > dH)
107 	{
108 		height = dH - y;
109 	}
110 	if(width <= 0)
111 	{
112 	        x = dW -1;
113 		width = 1;
114 	}
115 	if(height <= 0)
116 	{
117 		y = dH -1;
118 		height = 1;
119 	}
120 	std::string sName("");
121 	getName(sName);
122 	sName += UT_std_string_sprintf("_segemnt_%d_%d_%d_%d",x,y,width,height);
123 
124 	GR_CairoRasterImage * pImage = makeSubimage(sName, x, y, width, height);
125 	if(pImage)
126 	{
127 		pImage->setDisplaySize(width,height);
128 	}
129 	return pImage;
130 }
131 
132 
133 
GR_CairoPatternImpl(const char * fileName)134 GR_CairoPatternImpl::GR_CairoPatternImpl(const char * fileName)
135 	: m_pattern(NULL)
136 {
137 	cairo_surface_t * surface = cairo_image_surface_create_from_png(fileName);
138 	m_pattern = cairo_pattern_create_for_surface(surface);
139 	cairo_pattern_set_extend(m_pattern, CAIRO_EXTEND_REPEAT);
140 	cairo_surface_destroy(surface);
141 }
142 
GR_CairoPatternImpl(cairo_surface_t * surf)143 GR_CairoPatternImpl::GR_CairoPatternImpl(cairo_surface_t * surf)
144 	: m_pattern(cairo_pattern_create_for_surface(surf))
145 {
146 	cairo_pattern_set_extend(m_pattern, CAIRO_EXTEND_REPEAT);
147 }
148 
GR_CairoPatternImpl(const GR_CairoPatternImpl & p)149 GR_CairoPatternImpl::GR_CairoPatternImpl(const GR_CairoPatternImpl & p)
150 	: UT_ColorPatImpl(p)
151 	, m_pattern(cairo_pattern_reference(p.m_pattern))
152 {
153 }
154 
~GR_CairoPatternImpl()155 GR_CairoPatternImpl::~GR_CairoPatternImpl()
156 {
157 	cairo_pattern_destroy(m_pattern);
158 }
159 
160 
clone() const161 UT_ColorPatImpl * GR_CairoPatternImpl::clone() const
162 {
163 	return new GR_CairoPatternImpl(*this);
164 }
165 
166 
_pango_item_list_free(GList * items)167 static void _pango_item_list_free(GList * items)
168 {
169 	GList * l;
170 	for( l = items; l ; l = l->next) {
171 		if(l->data) {
172 			pango_item_free(static_cast<PangoItem*>(l->data));
173 			l->data = NULL;
174 		}
175 	}
176 	g_list_free(items);
177 }
178 
179 
180 
181 class GR_CairoPangoItem: public GR_Item
182 {
183 	friend class GR_CairoGraphics;
184 	friend class GR_UnixPangoPrintGraphics;
185 	friend class GR_PangoRenderInfo;
186 
187   public:
~GR_CairoPangoItem()188 	virtual ~GR_CairoPangoItem(){ if (m_pi) {pango_item_free(m_pi);}};
189 
getType() const190 	virtual GR_ScriptType getType() const {return (GR_ScriptType)m_iType;}
191 
makeCopy() const192 	virtual GR_Item *     makeCopy() const
193 	    {
194 			return new GR_CairoPangoItem(pango_item_copy(m_pi));
195 		}
196 
getClassId() const197 	virtual GRRI_Type     getClassId() const {return GRRI_CAIRO_PANGO;}
198 
199   protected:
200 	GR_CairoPangoItem(PangoItem *pi);
GR_CairoPangoItem()201 	GR_CairoPangoItem() : m_pi(NULL) { }; // just a dummy used to terminate
202 	                                     // GR_Itemization list
203 
204 	PangoItem *m_pi;
205 	UT_uint32 m_iType;
206 };
207 
GR_CairoPangoItem(PangoItem * pi)208 GR_CairoPangoItem::GR_CairoPangoItem(PangoItem *pi):
209 	m_pi(pi)
210 {
211 	// there does not seem to be anything that we could use to identify the
212 	// items, so we will hash the pointers to the two text engines
213 	if(!pi)
214 	{
215 		m_iType = (UT_uint32)GRScriptType_Void;
216 	}
217 	else
218 	{
219 		// there does not seem to be anything that we could use to easily
220 		// identify the script, so we will hash the pointers to the two text
221 		// engines
222 
223 		void * b[2];
224 		b[0] = (void*)pi->analysis.shape_engine;
225 		b[1] = (void*)pi->analysis.lang_engine;
226 
227 		m_iType = UT_hash32((const char *) &b, 2 * sizeof(void*));
228 	}
229 }
230 
231 class GR_PangoRenderInfo : public GR_RenderInfo
232 {
233   public:
GR_PangoRenderInfo(GR_ScriptType t)234 	GR_PangoRenderInfo(GR_ScriptType t):
235 		GR_RenderInfo(t),
236 		m_pGlyphs(NULL),
237 		m_pScaledGlyphs(NULL),
238 		m_pLogOffsets(NULL),
239 		m_pJustify(NULL),
240 		m_iZoom(0),
241 		m_iCharCount(0),
242 		m_iShapingAllocNo(0)
243 	{
244 		++s_iInstanceCount;
245 		if(sUTF8 == NULL)
246 			sUTF8 = new UT_UTF8String("");
247 	};
248 
~GR_PangoRenderInfo()249 	virtual ~GR_PangoRenderInfo()
250 	{
251 		delete [] m_pJustify; delete [] m_pLogOffsets;
252 		if(m_pGlyphs)
253 			pango_glyph_string_free(m_pGlyphs);
254 		if(m_pScaledGlyphs)
255 			pango_glyph_string_free(m_pScaledGlyphs);
256 		s_iInstanceCount--;
257 
258 		if(!s_iInstanceCount)
259 		{
260 			delete [] s_pLogAttrs;
261 			s_pLogAttrs = NULL;
262 			DELETEP(sUTF8);
263 		}
264 	};
265 
getType() const266 	virtual GRRI_Type getType() const {return GRRI_CAIRO_PANGO;}
267 	virtual bool append(GR_RenderInfo &ri, bool bReverse = false);
268 	virtual bool split (GR_RenderInfo *&pri, bool bReverse = false);
269 	virtual bool cut(UT_uint32 offset, UT_uint32 iLen, bool bReverse = false);
270 	virtual bool isJustified() const;
271 	virtual bool canAppend(GR_RenderInfo &ri) const;
272 
273 	bool getUTF8Text();
274 
allocStaticBuffers(UT_uint32 iSize)275 	inline bool       allocStaticBuffers(UT_uint32 iSize)
276 	    {
277 			if(s_pLogAttrs)
278 				delete [] s_pLogAttrs;
279 
280 			s_pLogAttrs = new PangoLogAttr[iSize];
281 
282 			if(!s_pLogAttrs)
283 				return false;
284 
285 			s_iStaticSize = iSize;
286 			return true;
287 	    }
288 
289 	PangoGlyphString* m_pGlyphs;
290 	PangoGlyphString* m_pScaledGlyphs;
291 	int *             m_pLogOffsets;
292 	int *             m_pJustify;
293 	UT_uint32         m_iZoom;
294 	UT_uint32         m_iCharCount;
295 	UT_uint32         m_iShapingAllocNo;
296 
297 	static UT_UTF8String * sUTF8;
298 	static GR_PangoRenderInfo * s_pOwnerUTF8;
299 	static UT_uint32  s_iInstanceCount;
300 	static UT_uint32  s_iStaticSize;  // size of the static buffers
301 
302 	static PangoLogAttr *           s_pLogAttrs;
303 	static GR_PangoRenderInfo * s_pOwnerLogAttrs;
304 };
305 
306 
307 GR_PangoRenderInfo * GR_PangoRenderInfo::s_pOwnerUTF8 = NULL;
308 UT_UTF8String *          GR_PangoRenderInfo::sUTF8 = NULL;
309 UT_uint32                GR_PangoRenderInfo::s_iInstanceCount = 0;
310 UT_uint32                GR_PangoRenderInfo::s_iStaticSize = 0;
311 GR_PangoRenderInfo * GR_PangoRenderInfo::s_pOwnerLogAttrs = NULL;
312 PangoLogAttr *           GR_PangoRenderInfo::s_pLogAttrs = NULL;
313 
314 
getUTF8Text()315 bool GR_PangoRenderInfo::getUTF8Text()
316 {
317 	if(s_pOwnerUTF8 == this)
318 		return true;
319 
320 	UT_return_val_if_fail( m_pText && m_pText->getStatus() == UTIter_OK, false );
321 
322 	UT_TextIterator & text = *m_pText;
323 	sUTF8->clear();
324 	sUTF8->reserve( text.getUpperLimit());
325 	// we intentionally run this as far as the iterator lets us, even if that is
326 	// past the end of this item
327 	for(; text.getStatus() == UTIter_OK; ++text)
328 	{
329 		*sUTF8 += text.getChar();
330 	}
331 
332 	s_pOwnerUTF8 = this;
333 
334 	return true;
335 }
336 
getDefaultDeviceResolution()337 UT_uint32 GR_CairoGraphics::getDefaultDeviceResolution()
338 {
339 	PangoFontMap * pFontMap = pango_cairo_font_map_get_default();
340 	return (UT_uint32) pango_cairo_font_map_get_resolution(PANGO_CAIRO_FONT_MAP(pFontMap));
341 	// The default font map must not be freed.
342 }
343 
344 // TODO maybe consolidate a common constructor again?
GR_CairoGraphics(cairo_t * cr,UT_uint32 iDeviceResolution)345 GR_CairoGraphics::GR_CairoGraphics(cairo_t *cr, UT_uint32 iDeviceResolution)
346   :	m_pFontMap(NULL),
347 	m_pContext(NULL),
348 	m_pLayoutFontMap(NULL),
349 	m_pLayoutContext(NULL),
350 	m_pPFont(NULL),
351 	m_pPFontGUI(NULL),
352 	m_pAdjustedPangoFont(NULL),
353 	m_pAdjustedPangoFontDescription(NULL),
354 	m_iAdjustedPangoFontSize(0),
355 	m_pAdjustedLayoutPangoFont(NULL),
356 	m_pAdjustedLayoutPangoFontDescription(NULL),
357 	m_iAdjustedLayoutPangoFontSize(0),
358 	m_iDeviceResolution(iDeviceResolution),
359 	m_cr(cr),
360 	m_cursor(GR_CURSOR_INVALID),
361 	m_cs(GR_Graphics::GR_COLORSPACE_COLOR),
362 	m_curColorDirty(false),
363 	m_clipRectDirty(false),
364 	m_lineWidth(1.0),
365 	m_joinStyle(JOIN_MITER),
366 	m_capStyle(CAP_BUTT),
367 	m_lineStyle(LINE_SOLID),
368 	m_linePropsDirty(false),
369 	m_bIsSymbol(false),
370 	m_bIsDingbat(false),
371 	m_iPrevX1(0),
372 	m_iPrevX2(0),
373 	m_iPrevY1(0),
374 	m_iPrevY2(0),
375 	m_iPrevRect(1000), // arbitary number that leaves room for plenty of carets
376 	m_iXORCount(0)
377 {
378 	_initPango();
379 }
380 
GR_CairoGraphics()381 GR_CairoGraphics::GR_CairoGraphics()
382   :	m_pFontMap(NULL),
383 	m_pContext(NULL),
384 	m_pLayoutFontMap(NULL),
385 	m_pLayoutContext(NULL),
386 	m_pPFont(NULL),
387 	m_pPFontGUI(NULL),
388 	m_pAdjustedPangoFont(NULL),
389 	m_pAdjustedPangoFontDescription(NULL),
390 	m_iAdjustedPangoFontSize(0),
391 	m_pAdjustedLayoutPangoFont(NULL),
392 	m_pAdjustedLayoutPangoFontDescription(NULL),
393 	m_iAdjustedLayoutPangoFontSize(0),
394 	m_iDeviceResolution(getDefaultDeviceResolution()),
395 	m_cr(NULL),
396 	m_cursor(GR_CURSOR_INVALID),
397 	m_cs(GR_Graphics::GR_COLORSPACE_COLOR),
398 	m_curColorDirty(false),
399 	m_clipRectDirty(false),
400 	m_lineWidth(1.0),
401 	m_joinStyle(JOIN_MITER),
402 	m_capStyle(CAP_BUTT),
403 	m_lineStyle(LINE_SOLID),
404 	m_linePropsDirty(false),
405 	m_bIsSymbol(false),
406 	m_bIsDingbat(false),
407 	m_iPrevX1(0),
408 	m_iPrevX2(0),
409 	m_iPrevY1(0),
410 	m_iPrevY2(0),
411 	m_iPrevRect(1000),
412 	m_iXORCount(0)
413 
414 {
415 	_initPango();
416 }
417 
418 
_initCairo()419 void GR_CairoGraphics::_initCairo()
420 {
421 	UT_ASSERT(m_cr);
422 	cairo_translate(m_cr, 0.5, 0.5);
423 	cairo_set_line_width (m_cr, 1);
424 }
425 
_initPango()426 void GR_CairoGraphics::_initPango()
427 {
428 	m_pFontMap =  pango_cairo_font_map_new();
429 	pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(m_pFontMap), m_iDeviceResolution);
430 	m_pContext = pango_font_map_create_context(PANGO_FONT_MAP(m_pFontMap));
431 
432 	m_pLayoutFontMap = pango_cairo_font_map_new();
433 	pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(m_pLayoutFontMap), getResolution());
434 	m_pLayoutContext = pango_font_map_create_context(PANGO_FONT_MAP(m_pLayoutFontMap));
435 
436 	UT_DEBUGMSG(("Created LayoutFontMap %p Layout Context %p resolution %d device resolution %d \n",
437 				 m_pLayoutFontMap,	m_pLayoutContext, getResolution(),
438 				 m_iDeviceResolution));
439 }
440 
~GR_CairoGraphics()441 GR_CairoGraphics::~GR_CairoGraphics()
442 {
443 	xxx_UT_DEBUGMSG(("Deleting UnixPangoGraphics %x \n",this));
444 
445 	// free m_vSaveRect & m_vSaveRectBuf elements
446 	UT_std_vector_sparsepurgeall(m_vSaveRect);
447 	UT_std_vector_freeall(m_vSaveRectBuf, cairo_surface_destroy);
448 
449 	cairo_destroy(m_cr);
450 	m_cr = NULL;
451 
452 	if(m_pAdjustedPangoFont!= NULL)
453 	{
454 		g_object_unref(m_pAdjustedPangoFont);
455 	}
456 	if(m_pAdjustedPangoFontDescription)
457 	{
458 		pango_font_description_free(m_pAdjustedPangoFontDescription);
459 	}
460 	if(m_pAdjustedLayoutPangoFont!= NULL)
461 	{
462 		g_object_unref(m_pAdjustedLayoutPangoFont);
463 	}
464 	if(m_pAdjustedLayoutPangoFontDescription)
465 	{
466 		pango_font_description_free(m_pAdjustedLayoutPangoFontDescription);
467 	}
468 	if (m_pContext != NULL)
469 	{
470 		g_object_unref(m_pContext);
471 	}
472 
473 	_destroyFonts();
474 	delete m_pPFontGUI;
475 	if(m_pLayoutContext) {
476 		g_object_unref(m_pLayoutContext);
477 	}
478 	if(m_pFontMap) {
479 		g_object_unref(m_pFontMap);
480 	}
481 
482 	// MES After much reading and playing I discovered that the
483 	// FontMap gets unreferenced after every font that uses it is
484 	// removed provied Context is also unrefed. Leaving the unref of
485 	// the FontMap causes an intermitent crash on exit, particularly on
486 	// documents with lots of Math. This fixes those crashes and checks with
487 	// valgrind show no measureable increase in leacked memory.
488 	//
489 	// Hub: but we still leak and the doc say to unref.
490 	if (m_pLayoutFontMap) {
491 		g_object_unref(m_pLayoutFontMap);
492 		m_pLayoutFontMap = NULL;
493 	}
494 }
495 
resetFontMapResolution(void)496 void GR_CairoGraphics::resetFontMapResolution(void)
497 {
498 	pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(m_pFontMap), m_iDeviceResolution);
499 }
500 
queryProperties(GR_Graphics::Properties gp) const501 bool GR_CairoGraphics::queryProperties(GR_Graphics::Properties gp) const
502 {
503 	switch (gp)
504 	{
505 		case DGP_SCREEN:
506 		case DGP_OPAQUEOVERLAY:
507 			return true;
508 		case DGP_PAPER:
509 			return false;
510 		default:
511 			UT_ASSERT(0);
512 			return false;
513 	}
514 }
515 
516 
startPrint(void)517 bool GR_CairoGraphics::startPrint(void)
518 {
519 	UT_ASSERT(0);
520 	return false;
521 }
522 
startPage(const char *,UT_uint32,bool,UT_uint32,UT_uint32)523 bool GR_CairoGraphics::startPage(const char * /*szPageLabel*/, UT_uint32 /*pageNumber*/,
524 								bool /*bPortrait*/, UT_uint32 /*iWidth*/, UT_uint32 /*iHeight*/)
525 {
526 	UT_ASSERT(0);
527 	return false;
528 }
529 
endPrint(void)530 bool GR_CairoGraphics::endPrint(void)
531 {
532 	UT_ASSERT(0);
533 	return false;
534 }
535 
drawGlyph(UT_uint32 Char,UT_sint32 xoff,UT_sint32 yoff)536 void GR_CairoGraphics::drawGlyph(UT_uint32 Char, UT_sint32 xoff, UT_sint32 yoff)
537 {
538 	drawChars(&Char, 0, 1, xoff, yoff, NULL);
539 }
540 
setColorSpace(GR_Graphics::ColorSpace)541 void GR_CairoGraphics::setColorSpace(GR_Graphics::ColorSpace /* c */)
542 {
543 	// we only use ONE color space here now (GdkRGB's space)
544 	// and we don't let people change that on us.
545 	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
546 }
547 
getColorSpace(void) const548 GR_Graphics::ColorSpace GR_CairoGraphics::getColorSpace(void) const
549 {
550 	return m_cs;
551 }
552 
553 
getCursor(void) const554 GR_Graphics::Cursor GR_CairoGraphics::getCursor(void) const
555 {
556 	return m_cursor;
557 }
558 
setColor3D(GR_Color3D c)559 void GR_CairoGraphics::setColor3D(GR_Color3D c)
560 {
561 	UT_ASSERT(c < COUNT_3D_COLORS);
562 
563 	setColor(m_3dColors[c]);
564 }
565 
getColor3D(GR_Color3D name,UT_RGBColor & color)566 bool GR_CairoGraphics::getColor3D(GR_Color3D name, UT_RGBColor &color)
567 {
568 	if (m_bHave3DColors) {
569 		color = m_3dColors[name];
570 		return true;
571 	}
572 	return false;
573 }
574 
575 
getDeviceResolution(void) const576 UT_uint32 GR_CairoGraphics::getDeviceResolution(void) const
577 {
578 	return m_iDeviceResolution;
579 }
580 
581 
measureUnRemappedChar(const UT_UCSChar c,UT_uint32 * height)582 UT_sint32 GR_CairoGraphics::measureUnRemappedChar(const UT_UCSChar c, UT_uint32 * height)
583 {
584         if (height) {
585 		*height = 0;
586 	}
587 	UT_sint32 w = measureString(&c, 0, 1, NULL, height);
588 	return w;
589 }
590 
itemize(UT_TextIterator & text,GR_Itemization & I)591 bool GR_CairoGraphics::itemize(UT_TextIterator & text, GR_Itemization & I)
592 {
593 	// Performance is not of the highest priorty, as this function gets only
594 	// called once on each text fragment on load or keyboard entry
595 	xxx_UT_DEBUGMSG(("GR_CairoGraphics::itemize\n"));
596 	UT_return_val_if_fail( m_pContext, false );
597 
598 	// we need to convert our ucs4 data to utf8 for pango
599 	UT_UTF8String utf8;
600 
601 	UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
602 	UT_uint32 iPosStart = text.getPosition();
603 	UT_uint32 iPosEnd   = text.getUpperLimit();
604 	UT_return_val_if_fail(iPosEnd < 0xffffffff && iPosEnd >= iPosStart, false);
605 
606 	UT_uint32 iLen = iPosEnd - iPosStart + 1; // including iPosEnd
607 
608 	UT_uint32 i;
609 	for(i = 0; i < iLen; ++i, ++text)
610 	{
611 		UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
612 		utf8 += text.getChar();
613 	}
614 
615 	UT_uint32 iByteLength = utf8.byteLength();
616 
617 	PangoAttrList *pAttrList = pango_attr_list_new();
618 	PangoAttrIterator *pIter = pango_attr_list_get_iterator (pAttrList);
619 	const GR_PangoFont * pFont = (const GR_PangoFont *) I.getFont();
620 
621 	if (pFont)
622 	{
623 		const PangoFontDescription * pfd = pFont->getPangoDescription();
624 		PangoAttribute * pAttr = pango_attr_font_desc_new (pfd);
625 		pAttr->start_index = 0;
626 		pAttr->end_index = iByteLength;
627 		pango_attr_list_insert(pAttrList, pAttr);
628 	}
629 
630 	const char * pLang = I.getLang();
631 
632 	if (pLang)
633 	{
634 		PangoLanguage  * pl = pango_language_from_string(pLang);
635 		PangoAttribute * pAttr = pango_attr_language_new (pl);
636 		pAttr->start_index = 0;
637 		pAttr->end_index = iByteLength;
638 		pango_attr_list_insert(pAttrList, pAttr);
639 	}
640 
641 	UT_uint32 iItemCount;
642 
643 	// this will result in itemization assuming base direction of 0
644 	// we set the appropriate embedding level later in shape()
645 	GList *gItems = pango_itemize(m_pContext,
646 								  utf8.utf8_str(),
647 								  0, iByteLength,
648 								  pAttrList, pIter);
649 
650 	pango_attr_iterator_destroy (pIter);
651 	pango_attr_list_unref (pAttrList);
652 
653 	iItemCount = g_list_length(gItems);
654 
655 	// now we process the ouptut
656 	UT_uint32 iOffset = 0;
657 	xxx_UT_DEBUGMSG(("itemize: number of items %d\n", iItemCount));
658 	for(i = 0; i < iItemCount; ++i)
659 	{
660 		xxx_UT_DEBUGMSG(("itemize: creating item %d\n", i));
661 		PangoItem *pItem = (PangoItem *)g_list_nth(gItems, i)->data;
662 		GR_CairoPangoItem * pI = new GR_CairoPangoItem(pItem);
663 
664 #if 0 //def DEBUG
665 		PangoFont * pf = pI->m_pi->analysis.font;
666 		PangoFontDescription * pfd = pango_font_describe (pf);
667 		char * pfds = pango_font_description_to_string (pfd);
668 
669 		PangoLanguage * lang = pI->m_pi->analysis.language;
670 
671 		UT_DEBUGMSG(("@@@@ ===== Item [%s] [%s] =====\n",
672 					 pfds, pango_language_to_string(lang)));
673 
674 		pango_font_description_free (pfd);
675 		g_free (pfds);
676 #endif
677 
678 		I.addItem(iOffset, pI);
679 		iOffset += pItem->num_chars;
680 	}
681 
682 	I.addItem(iPosEnd - iPosStart + 1, new GR_CairoPangoItem());
683 
684 	g_list_free(gItems);
685 
686 	xxx_UT_DEBUGMSG(("itemize succeeded\n"));
687 	return true;
688 }
689 
690 int *
_calculateLogicalOffsets(PangoGlyphString * pGlyphs,UT_BidiCharType iVisDir,const char * pUtf8)691 GR_CairoGraphics::_calculateLogicalOffsets (PangoGlyphString * pGlyphs,
692 												UT_BidiCharType iVisDir,
693 												const char * pUtf8)
694 {
695 	UT_return_val_if_fail( pGlyphs && pUtf8, NULL );
696 
697 	// pGlyphs contains logical cluster info, which is
698 	// unfortunately indexed to bytes in the utf-8 string, not characters --
699 	// this is real pain and we have to convert it.
700 
701 	int * pLogOffsets = new int [pGlyphs->num_glyphs];
702 
703 	// In LTR text, the values in log_clusters are guaranteed to be increasing,
704 	// in RTL text, the values in log_clusters are decreasing
705 
706 	glong offset = 0;
707 	const gchar *s = pUtf8;
708 
709 	if (iVisDir == UT_BIDI_LTR ||
710 		(pGlyphs->num_glyphs > 1 &&
711 		 pGlyphs->log_clusters[0] < pGlyphs->log_clusters[1]))
712 	{
713 		for(int i = 0; i < pGlyphs->num_glyphs; ++i)
714 		{
715 
716 			int iOff = pGlyphs->log_clusters[i];
717 
718 		//      below is equivalent to
719 		//	pLogOffsets[i] =  g_utf8_pointer_to_offset (pUtf8, pUtf8 + iOff);
720 		// 	but avoids quadratic behavior because we use s and offset from
721 		//      previous iteration
722 
723     		        while (s <  pUtf8 + iOff)
724 		        {
725 				s = g_utf8_next_char (s);
726 				offset++;
727 		        }
728 			pLogOffsets[i] =  offset;
729 		}
730 	}
731 	else // GR_ShapingInfo.m_iVisDir == UT_BIDI_RTL)
732 	{
733 		for(int i = pGlyphs->num_glyphs - 1; i >= 0; --i)
734 		{
735 			int iOff = pGlyphs->log_clusters[i];
736   		        while (s <  pUtf8 + iOff)
737 		        {
738 				s = g_utf8_next_char (s);
739 				offset++;
740 		        }
741 			pLogOffsets[i] =  offset;
742 		}
743 	}
744 
745 	return pLogOffsets;
746 }
747 
shape(GR_ShapingInfo & si,GR_RenderInfo * & ri)748 bool GR_CairoGraphics::shape(GR_ShapingInfo & si, GR_RenderInfo *& ri)
749 {
750 	xxx_UT_DEBUGMSG(("GR_CairoGraphics::shape, len %d\n", si.m_iLength));
751 	UT_return_val_if_fail(si.m_pItem &&
752 						  si.m_pItem->getClassId() == GRRI_CAIRO_PANGO &&
753 						  si.m_pFont, false);
754 
755 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem *)si.m_pItem;
756 
757 	PangoFontset * pfs = NULL;
758 	PangoFont    * pFontSubst = NULL;
759 
760 	if(!ri)
761 	{
762 		// this simply allocates new instance of the RI which this function
763 		// will fill with meaningful data
764 		ri = new GR_PangoRenderInfo(pItem->getType());
765 		UT_return_val_if_fail(ri, false);
766 	}
767 	else
768 	{
769 		UT_return_val_if_fail(ri->getType() == GRRI_CAIRO_PANGO, false);
770 	}
771 
772 	GR_PangoRenderInfo * RI = (GR_PangoRenderInfo *)ri;
773 
774 	// need this so that isSymbol() and isDingbat() are correct
775 	setFont(si.m_pFont);
776 
777 	/*
778 	 * Pango does a royally bad job of the font substitution in
779 	 * pango_itemize(): it will happily return 'Times New Roman' as
780 	 * font when we have requested 'Arial', even though the latter is
781 	 * present and has the necessary coverage. Consequently we have to
782 	 * do the font substitution manually even on the first shapping.
783 	 *
784 	 * If the font has changed from the one for which we previously shapped
785 	 * (or have not shaped, in which case alloc no is 0), we load a fontset
786 	 * for the requested font description. Later on, we pick the best font
787 	 * for each character in this run.
788 	 */
789 	if(RI->m_iShapingAllocNo != si.m_pFont->getAllocNumber())
790 	{
791 		//UT_DEBUGMSG(("@@@@ ===== Font change %d -> %d\n",
792 		//			 RI->m_iShapingAllocNo,
793 		//			 si.m_pFont->getAllocNumber()));
794 
795 		GR_PangoFont * pFont = (GR_PangoFont*)si.m_pFont;
796 
797 		pfs = pango_font_map_load_fontset (getFontMap(),
798 										   getContext(),
799 										   pFont->getPangoDescription(),
800 										   pItem->m_pi->analysis.language);
801 	}
802 
803 	UT_UTF8String utf8;
804 	utf8.reserve(si.m_iLength);
805 	bool previousWasSpace = si.m_previousWasSpace;
806 
807 	UT_sint32 i;
808 	for(i = 0; i < si.m_iLength; ++i, ++si.m_Text)
809 	{
810 		UT_return_val_if_fail(si.m_Text.getStatus() == UTIter_OK, false);
811 		UT_UCS4Char c = si.m_Text.getChar();
812 		if(isSymbol())
813 			utf8 += adobeToUnicode(c);
814 		else if(isDingbat())
815 			utf8 += adobeDingbatsToUnicode(c);
816 		else {
817 
818 			if (si.m_TextTransform == GR_ShapingInfo::LOWERCASE)
819 				c = g_unichar_tolower(c);
820 			else if (si.m_TextTransform == GR_ShapingInfo::UPPERCASE)
821 				c = g_unichar_toupper(c);
822 			else if (si.m_TextTransform == GR_ShapingInfo::CAPITALIZE) {
823 				if (previousWasSpace) {
824 					c = g_unichar_toupper(c);
825 				}
826 			} // else si.m_TextTransform == GR_ShapingInfo::NONE
827 
828 			utf8 += c;
829 			previousWasSpace = g_unichar_isspace(c);
830 		}
831 
832 		if (pfs)
833 		{
834 			/*
835 			 * A font change; get the best font for this character
836 			 */
837 			PangoFont * font = pango_fontset_get_font (pfs, c);
838 
839 			if (!font)
840 			{
841 				/*
842 				 * We did not find a suitable font -- nothing we can do.
843 				 */
844 				UT_DEBUGMSG(("@@@@ ===== Failed to find font for u%04x\n", c));
845 			}
846 			else if (pFontSubst && (pFontSubst != font))
847 			{
848 				/*
849 			     * Ok, the font we got for this character does not match
850 			     * the one we got the for the preceding characters.
851 			     *
852 			     * What we could do is to split the run before this character
853 			     * so we might use two different fonts, but we currently
854 			     * do not have the infrastructure to do this. Also, doing this
855 			     * breaks when the missing glyph is a combining character.
856 			     *
857 			     * Alternatively, we would need to maintain an internal list
858 			     * of fonts for each section, but that would mean also to
859 			     * maintain separate glyph strings, which would be a nightmare.
860 			     *
861 			     * We can limit this from happening by preventing items that
862 			     * will use different font from merging, which I have now done,
863 			     * but again, this does not work when combining characters are
864 			     * involved, because we cannot draw the combining character on
865 			     * it's own.
866 			     *
867 			     * TODO -- devise a sensible way of handling this.
868 			     */
869 #if DEBUG
870 				PangoFontDescription * pfd = pango_font_describe (pFontSubst);
871 				char * sFontSubst = pango_font_description_to_string (pfd);
872 				pango_font_description_free (pfd);
873 				pfd = pango_font_describe (font);
874 				char * sFont = pango_font_description_to_string (pfd);
875 				pango_font_description_free (pfd);
876 				UT_DEBUGMSG(("@@@@ ===== Font for u%04x (%s) does not match "
877 							 "earlier font %s\n", c, sFont, sFontSubst));
878 				g_free (sFontSubst);
879 				g_free (sFont);
880 #endif
881 				g_object_unref (G_OBJECT (pFontSubst));
882 				pFontSubst = font;
883 			}
884 			else if (pFontSubst == font)
885 			{
886 				/* We now have two references to this font, rectify */
887 				g_object_unref (G_OBJECT (font));
888 			}
889 			else
890 			{
891 				pFontSubst = font;
892 			}
893 
894 		}
895 	}
896 
897 	if(pfs)
898 	{
899 		g_object_unref((GObject*)pfs);
900 		pfs = NULL;
901 	}
902 	if (pFontSubst)
903 	{
904 		/*
905 		 * We are doing font substitution -- release the font previously
906 		 * stored in the PangoAnalysis and replace it with this one.
907 		 */
908 		if (pItem->m_pi->analysis.font)
909 			g_object_unref (G_OBJECT (pItem->m_pi->analysis.font));
910 
911 		pItem->m_pi->analysis.font = (PangoFont*)pFontSubst;
912 	}
913 
914 	RI->m_iCharCount = si.m_iLength;
915 
916 	if(RI->m_pGlyphs)
917 	{
918 		pango_glyph_string_free(RI->m_pGlyphs);
919 		RI->m_pGlyphs = NULL;
920 	}
921 
922 	if(RI->m_pScaledGlyphs)
923 	{
924 		pango_glyph_string_free(RI->m_pScaledGlyphs);
925 		RI->m_pScaledGlyphs = NULL;
926 	}
927 
928 	RI->m_pGlyphs = pango_glyph_string_new();
929 	RI->m_pScaledGlyphs = pango_glyph_string_new();
930 
931 	/*
932 	 * We want to do the shaping on a font at it's actual point size, so we
933 	 * cannot use the font in our PangoAnalysis structure, which we will
934 	 * later use for drawing, and which will be adjusted for the current
935 	 * zoom.
936 	 */
937 	UT_LocaleTransactor t(LC_NUMERIC, "C");
938 	UT_String              s;
939 	PangoFont            * pPangoFontOrig = pItem->m_pi->analysis.font;
940 	GR_PangoFont     * pFont = (GR_PangoFont *) si.m_pFont;;
941 	PangoFontDescription * pfd;
942 
943 	if (pPangoFontOrig)
944 	{
945 		pfd = pango_font_describe (pPangoFontOrig);
946 		double dSize = (double)PANGO_SCALE * pFont->getPointSize();
947 		pango_font_description_set_size (pfd, (gint)dSize);
948 
949 #if 0 //def DEBUG
950 		char * s = pango_font_description_to_string (pfd);
951 		UT_DEBUGMSG(("@@@@ ===== Shaping with font [%s]\n", s));
952 		g_free (s);
953 #endif
954 	}
955 	else
956 	{
957 		UT_ASSERT_HARMLESS( !pFont->isGuiFont() );
958 		UT_String_sprintf(s, "%s %f",
959 						  pFont->getDescription().c_str(),
960 						  pFont->getPointSize());
961 
962 		pfd = pango_font_description_from_string(s.c_str());
963 	}
964 
965 	UT_return_val_if_fail(pfd, false);
966 	PangoFont * pf = pango_context_load_font(getLayoutContext(), pfd);
967 	pango_font_description_free(pfd);
968 
969 	// no need to ref pf because it will replaced right after
970 	pItem->m_pi->analysis.font = pf;
971 
972 	// need to set the embedding level here based on the level of our run
973 	pItem->m_pi->analysis.level = si.m_iVisDir == UT_BIDI_RTL ? 1 : 0;
974 
975 	pango_shape(utf8.utf8_str(), utf8.byteLength(),
976 				&(pItem->m_pi->analysis), RI->m_pGlyphs);
977 	pango_shape(utf8.utf8_str(), utf8.byteLength(),
978 				&(pItem->m_pi->analysis), RI->m_pScaledGlyphs);
979 
980 	pItem->m_pi->analysis.font = pPangoFontOrig;
981 
982 	g_object_unref(pf);
983 
984 	if(RI->m_pLogOffsets)
985 	{
986 		delete [] RI->m_pLogOffsets;
987 	}
988 
989 	RI->m_pLogOffsets = _calculateLogicalOffsets(RI->m_pGlyphs,
990 												 si.m_iVisDir,
991 												 utf8.utf8_str());
992 
993 	// need to transfer data that we will need later from si to RI
994 	RI->m_iLength = si.m_iLength;
995 	RI->m_pItem   = si.m_pItem;
996 	RI->m_pFont   = si.m_pFont;
997 	RI->m_iShapingAllocNo = si.m_pFont->getAllocNumber();
998 
999 	RI->m_eShapingResult = GRSR_ContextSensitiveAndLigatures;
1000 
1001 	// remove any justification information -- it will have to be recalculated
1002 	delete[] RI->m_pJustify; RI->m_pJustify = NULL;
1003 
1004 	// we did our calculations at notional 100%
1005 	RI->m_iZoom = 100;
1006 
1007 	// Make sure that s_pOwnerUTF8 and s_pOwnerLogAttrs are not referencing RI
1008 	if (RI->s_pOwnerLogAttrs == RI)
1009 		RI->s_pOwnerLogAttrs = NULL;
1010 	if (RI->s_pOwnerUTF8 == RI)
1011 		RI->s_pOwnerUTF8 = NULL;
1012 
1013 	return true;
1014 }
1015 
1016 
getTextWidth(GR_RenderInfo & ri)1017 UT_sint32 GR_CairoGraphics::getTextWidth(GR_RenderInfo & ri)
1018 {
1019 	xxx_UT_DEBUGMSG(("GR_CairoGraphics::getTextWidth\n"));
1020 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO, 0);
1021 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1022 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem *)RI.m_pItem;
1023 
1024 	UT_return_val_if_fail( RI.m_pGlyphs && RI.m_pLogOffsets && pItem, 0 );
1025 
1026 	GR_PangoFont * pFont = (GR_PangoFont *) RI.m_pFont;
1027 	UT_return_val_if_fail( pFont, 0 );
1028 
1029 	//
1030 	// Actually want the layout font here
1031 	//
1032 	PangoFont * pf = _adjustedLayoutPangoFont(pFont, pItem->m_pi->analysis.font);
1033 
1034 	xxx_UT_DEBUGMSG(("Adjusted Layout font %x Adjusted font %x \n",pf));
1035 	UT_return_val_if_fail( pf, 0 );
1036 
1037 	UT_sint32 iStart = RI.m_iOffset;
1038 	UT_sint32 iEnd   = RI.m_iOffset + RI.m_iLength;
1039 
1040 	UT_sint32 iWidth =  _measureExtent (RI.m_pGlyphs, pf, RI.m_iVisDir, NULL,
1041 						   RI.m_pLogOffsets, iStart, iEnd);
1042 	xxx_UT_DEBUGMSG(("TextWidths Pango Font %x height %d text width %d \n",
1043 					 pFont, pFont->getAscent(), iWidth));
1044 	return iWidth;
1045 }
1046 
1047 /*!
1048  * Calculates the extents of string corresponding to glyphstring from
1049  * *character* offset iStart to iEnd (excluding iEnd);
1050  *
1051  * iDir is the visual direction of the text
1052  *
1053  * pUtf8 pointer to the corresponding utf8 string; can be NULL if pLogOffsets
1054  *    is provided
1055  *
1056  * pLogOffsets is array of logical offsets (see
1057  *    gr_UnixPangoRenderInfo::m_pLogOffsets); if NULL, it will be calculated
1058  *    using the corresponding utf8 string and pointer returned back; the
1059  *    caller needs to delete[] it when no longer needed.
1060  *
1061  * On return iStart and iEnd contain the offset values that correspond to the
1062  * returned extent (e.g., if the original iStart and/or iEnd are not legal
1063  * character postions due to clustering rules, these can be different from
1064  * the requested values).
1065  */
_measureExtent(PangoGlyphString * pg,PangoFont * pf,UT_BidiCharType iDir,const char * pUtf8,int * & pLogOffsets,UT_sint32 & iStart,UT_sint32 & iEnd)1066 UT_uint32 GR_CairoGraphics::_measureExtent (PangoGlyphString * pg,
1067 												PangoFont * pf,
1068 												UT_BidiCharType iDir,
1069 												const char * pUtf8,
1070 												int * & pLogOffsets,
1071 												UT_sint32 & iStart,
1072 												UT_sint32 & iEnd)
1073 {
1074 	UT_return_val_if_fail( pg && pf, 0 );
1075 	PangoRectangle LR;
1076 
1077 	// need to convert the char offset and length to glyph offsets
1078 	UT_uint32 iGlyphCount = pg->num_glyphs;
1079 	UT_sint32 iOffsetStart = -1, iOffsetEnd = -1;
1080 
1081 	if (!pLogOffsets)
1082 	{
1083 		UT_return_val_if_fail( pUtf8, 0 );
1084 		pLogOffsets = _calculateLogicalOffsets (pg, iDir, pUtf8);
1085 	}
1086 
1087 	UT_return_val_if_fail( pLogOffsets, 0 );
1088 
1089 	// loop running in visual plane
1090 	for(UT_uint32 i = 0; i < iGlyphCount; ++i)
1091 	{
1092 		// have to index glyphs in logical plane to hit our logical start
1093 		// offset before the end offset
1094 		UT_uint32 k = (iDir == UT_BIDI_RTL) ? iGlyphCount - i - 1 : i;
1095 
1096 		// test for >= -- in case of combining characters, the requested offset
1097 		// might inside the cluster, which is not legal, we take the first
1098 		// offset given to us
1099 		if(iOffsetStart < 0 && pLogOffsets[k] >= iStart)
1100 		{
1101 			iOffsetStart = k;
1102 			iStart = pLogOffsets[k];
1103 			xxx_UT_DEBUGMSG(("::getTextWidth: iOffsetStart == %d\n",
1104 						 iOffsetStart));
1105 			continue;
1106 		}
1107 
1108 
1109 		if(pLogOffsets[k] >= iEnd)
1110 		{
1111 			iOffsetEnd = k;
1112 			iEnd = pLogOffsets[k];
1113 			xxx_UT_DEBUGMSG(("::getTextWidth: iOffsetEnd == %d\n",
1114 							 iOffsetEnd));
1115 			break;
1116 		}
1117 	}
1118 
1119 	UT_ASSERT_HARMLESS( iOffsetStart >= 0 );
1120 
1121 	xxx_UT_DEBUGMSG(("Font size in _measureExtents %d \n",
1122 						pango_font_description_get_size(pango_font_describe (pf))));
1123 
1124 	if(iOffsetEnd < 0 && iDir == UT_BIDI_LTR)
1125 	{
1126 		// to the end
1127 		iOffsetEnd = iGlyphCount;
1128 	}
1129 
1130 	if (iDir == UT_BIDI_RTL)
1131 	{
1132 		// in RTL text, the start offset will be higher than the end offset
1133 		// and we will want to measure (iOffsetEnd, iOffsetStart>
1134 		UT_sint32 t  = iOffsetStart;
1135 		iOffsetStart = iOffsetEnd + 1; // + 1 excludes iOffsetEnd
1136 		iOffsetEnd   = t + 1;          // + 1 includes iOffsetStart
1137 	}
1138 
1139 	UT_return_val_if_fail( iOffsetStart >= 0, 0 );
1140 
1141 	pango_glyph_string_extents_range(pg,
1142 									 iOffsetStart,
1143 									 iOffsetEnd, pf, NULL, &LR);
1144 
1145 	xxx_UT_DEBUGMSG(("::getTextWidth start %d, end %d, w %d, x %d\n",
1146 				 iOffsetStart, iOffsetEnd, LR.width, LR.x));
1147 
1148 	return ptlunz(LR.width + LR.x);
1149 }
1150 
1151 
1152 /*!
1153     Do any pre-processing that might be needed for rendering our text (This
1154     function is guaranteed to be called just before renderChars(), but where
1155     drawing of a single RI item is done inside of a loop, e.g., drawing the
1156     different segments of partially selected run, this function can be take out
1157     of the loop.)
1158 */
prepareToRenderChars(GR_RenderInfo & ri)1159 void GR_CairoGraphics::prepareToRenderChars(GR_RenderInfo & ri)
1160 {
1161 	// the only thing we need to do here is to make sure that the glyph metrics
1162 	// are calculated to a correct zoom level.
1163 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
1164 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1165 
1166 	if(RI.m_iZoom != getZoomPercentage())
1167 	{
1168 		_scaleCharacterMetrics(RI);
1169 	}
1170 }
1171 
1172 /*
1173  * This is used to get device zoomed PangoFont that is correct for present zoom level.
1174  * pFont is the font that we are supposed to be using (the user-selected font)
1175  * pf is the PangoFont that we are actually using (possibly a different,
1176  * substituted font).
1177  */
_adjustedPangoFont(GR_PangoFont * pFont,PangoFont * pf)1178 PangoFont *  GR_CairoGraphics::_adjustedPangoFont (GR_PangoFont * pFont, PangoFont * pf)
1179 {
1180 	UT_return_val_if_fail(pFont, NULL);
1181 
1182 	if (!pf)
1183 		return pFont->getPangoFont();
1184 
1185 	/*
1186 	 * When Pango is doing font substitution for us, the substitute font
1187 	 * we are getting always has size 12pt, so we have to use the size of
1188 	 * our own font to fix this.
1189 	 */
1190 	PangoFontDescription * pfd = pango_font_describe (pf);
1191 	UT_sint32 dSize = (gint)(pFont->getPointSize() * (double)PANGO_SCALE * (double)getZoomPercentage() / 100.0);
1192 	pango_font_description_set_size (pfd, dSize);
1193 
1194 	// Check if we have already cached a font with this description and size
1195 	if (m_pAdjustedPangoFontDescription && pango_font_description_equal(m_pAdjustedPangoFontDescription, pfd) && m_iAdjustedPangoFontSize == dSize)
1196 	{
1197 		pango_font_description_free(pfd);
1198 		return m_pAdjustedPangoFont;
1199 	}
1200 
1201 	/* Create and cache this font to avoid all this huha if we can */
1202 	if (m_pAdjustedPangoFont)
1203 		g_object_unref(m_pAdjustedPangoFont);
1204 	if (m_pAdjustedPangoFontDescription)
1205 		pango_font_description_free(m_pAdjustedPangoFontDescription);
1206 
1207 	m_pAdjustedPangoFont = pango_context_load_font(getContext(), pfd);
1208 	m_pAdjustedPangoFontDescription = pfd;
1209 	m_iAdjustedPangoFontSize = dSize;
1210 
1211 	return m_pAdjustedPangoFont;
1212 }
1213 
1214 
1215 /*
1216  * This is used to get Layout PangoFont (that is independent from the zoom level).
1217  * pFont is the font that we are supposed to be using (the user-selected font)
1218  * pf is the PangoFont that we are actually using (possibly a different,
1219  * substituted font).
1220  */
_adjustedLayoutPangoFont(GR_PangoFont * pFont,PangoFont * pf)1221 PangoFont *  GR_CairoGraphics::_adjustedLayoutPangoFont (GR_PangoFont * pFont, PangoFont * pf)
1222 {
1223 	UT_return_val_if_fail(pFont, NULL);
1224 
1225 	if (!pf)
1226 		return pFont->getPangoLayoutFont();
1227 
1228 	/*
1229 	 * When Pango is doing font substitution for us, the substitute font
1230 	 * we are getting always has size 12pt, so we have to use the size of
1231 	 * our own font to fix this.
1232 	 */
1233 	PangoFontDescription * pfd = pango_font_describe (pf);
1234 	UT_sint32 dSize = (gint)(pFont->getPointSize()*(double)PANGO_SCALE);
1235 	pango_font_description_set_size (pfd, dSize);
1236 
1237 	// Check if we have already cached a font with this description and size
1238 	if (m_pAdjustedLayoutPangoFontDescription && pango_font_description_equal(m_pAdjustedLayoutPangoFontDescription, pfd) && m_iAdjustedLayoutPangoFontSize == dSize)
1239 	{
1240 		pango_font_description_free(pfd);
1241 		return m_pAdjustedLayoutPangoFont;
1242 	}
1243 
1244 	/* Create and cache this font to avoid all this huha if we can */
1245 	if (m_pAdjustedLayoutPangoFont)
1246 		g_object_unref(m_pAdjustedLayoutPangoFont);
1247 	if (m_pAdjustedLayoutPangoFontDescription)
1248 		pango_font_description_free(m_pAdjustedLayoutPangoFontDescription);
1249 
1250 	m_pAdjustedLayoutPangoFont = pango_context_load_font(getLayoutContext(), pfd);
1251 	m_pAdjustedLayoutPangoFontDescription = pfd;
1252 	m_iAdjustedLayoutPangoFontSize = dSize;
1253 
1254 	return m_pAdjustedLayoutPangoFont;
1255 }
1256 
1257 
1258 /*!
1259     The offset passed to us as part of ri is a visual offset
1260 */
renderChars(GR_RenderInfo & ri)1261 void GR_CairoGraphics::renderChars(GR_RenderInfo & ri)
1262 {
1263 	if (m_cr == NULL)
1264 		return;
1265 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
1266 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1267 	GR_PangoFont * pFont = (GR_PangoFont *)RI.m_pFont;
1268 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem *)RI.m_pItem;
1269 	UT_return_if_fail(pItem && pFont && pFont->getPangoFont());
1270 	xxx_UT_DEBUGMSG(("GR_CairoGraphics::renderChars length %d \n",
1271 					 RI.m_iLength));
1272 
1273 	if(RI.m_iLength == 0)
1274 		return;
1275 
1276 	_setProps();
1277 	//
1278 	// Actually want the zoomed device font here
1279 	//
1280 	PangoFont * pf = _adjustedPangoFont(pFont, pItem->m_pi->analysis.font);
1281 
1282 	xxx_UT_DEBUGMSG(("Pango renderChars: xoff %d yoff %d\n",
1283 					 RI.m_xoff, RI.m_yoff));
1284 
1285 	double xoff = _tdudX(RI.m_xoff);
1286 	double yoff = _tdudY(RI.m_yoff + getFontAscent(pFont));
1287 
1288 	UT_return_if_fail(RI.m_pScaledGlyphs);
1289 
1290 	// TODO -- test here for the endpoint as well
1291 	if(RI.m_iOffset == 0 &&
1292 	   (RI.m_iLength == (UT_sint32)RI.m_iCharCount || !RI.m_iCharCount))
1293 	{
1294 		xxx_UT_DEBUGMSG(("Doing Cairo Render now.\n"));
1295 		cairo_save(m_cr);
1296 		cairo_translate(m_cr, xoff, yoff);
1297 		pango_cairo_show_glyph_string(m_cr, pf, RI.m_pScaledGlyphs);
1298 		cairo_restore(m_cr);
1299 	}
1300 	else
1301 	{
1302 		// This is really stupid -- Pango provides no way of drawing substrings,
1303 		// so we need to create a new glyph string, that only contains the
1304 		// subset This is complicated by the fact that all offsets in the Pango
1305 		// api are stupid byte offsets in to utf8 strings, not character offsets
1306 		UT_return_if_fail( RI.m_pText );
1307 		UT_TextIterator & text = *RI.m_pText;
1308 		PangoGlyphString gs;
1309 
1310 		UT_UTF8String utf8;
1311 		UT_uint32 i;
1312 
1313 		for(i = 0; i < RI.m_iCharCount && text.getStatus() == UTIter_OK;
1314 			++i, ++text)
1315 		{
1316 			utf8 += text.getChar();
1317 		}
1318 
1319 		if(RI.m_iCharCount > i)
1320 		{
1321 			// it seems the iterator run out on us
1322 			// this should probably not happen
1323 			xxx_UT_DEBUGMSG(("gr_UnixPangoGraphics::renderChars: iterator too short\n"));
1324 			return;
1325 		}
1326 
1327 		UT_sint32 iOffsetStart
1328 			= RI.m_iVisDir == UT_BIDI_RTL ?
1329 			                 RI.m_iCharCount - RI.m_iOffset - RI.m_iLength : RI.m_iOffset;
1330 		xxx_UT_DEBUGMSG(("\n\niOffsetStart (in chars): %d\n", iOffsetStart));
1331 
1332 		const char * pUtf8   = utf8.utf8_str();
1333 		const char * pOffset = g_utf8_offset_to_pointer (pUtf8, iOffsetStart);
1334 
1335 		if (pOffset)
1336 			iOffsetStart = pOffset - pUtf8;
1337 		xxx_UT_DEBUGMSG(("iOffsetStart (in bytes): %d\n", iOffsetStart));
1338 
1339 		UT_sint32 iOffsetEnd
1340 			= RI.m_iVisDir == UT_BIDI_RTL ?
1341 			                 RI.m_iCharCount - RI.m_iOffset:
1342 			                 RI.m_iOffset + RI.m_iLength;
1343 		xxx_UT_DEBUGMSG(("iOffsetEnd (in chars): %d\n", iOffsetEnd));
1344 
1345 		pOffset = g_utf8_offset_to_pointer (pUtf8, iOffsetEnd);
1346 
1347 		xxx_UT_DEBUGMSG(("RI.m_iCharCount: %d, RI.m_iOffset: %d, RI.m_iLength: %d, rtl: %s\n", RI.m_iCharCount, RI.m_iOffset, RI.m_iLength, RI.m_iVisDir == UT_BIDI_RTL ? "yes" : "no"));
1348 
1349 		if (pOffset)
1350 			iOffsetEnd = pOffset - pUtf8;
1351 		xxx_UT_DEBUGMSG(("iOffsetEnd (in bytes): %d\n", iOffsetEnd));
1352 
1353 		//
1354 		// now we need to work out the glyph offsets given the start and end offsets in bytes
1355 		//
1356 
1357 		// the glyph end offset should point to the offset of the first glyph that is to be rendered
1358 		UT_sint32 iGlyphsStart = -1;
1359 		// the glyph end offset should point to the offset of the first glyph that is NOT to be rendered anymore
1360 		// (which in the special case of "render everything from iGlyphsStart to the end" for rtl text means
1361 		// offset -1, as index 0 points the the "last" glyph of the string. For ltr text this simply means
1362 		// offset "num_glyphs".
1363 		UT_sint32 iGlyphsEnd
1364 			= RI.m_iVisDir == UT_BIDI_RTL ?
1365 							-1 : RI.m_pScaledGlyphs->num_glyphs;
1366 
1367 		// count downwards for RTL text, so we include the full character clusters
1368 		i = RI.m_iVisDir == UT_BIDI_RTL ? RI.m_pScaledGlyphs->num_glyphs - 1 : 0;
1369 		while(i < (UT_uint32)RI.m_pScaledGlyphs->num_glyphs)
1370 		{
1371 			xxx_UT_DEBUGMSG(("RI.m_pScaledGlyphs->log_clusters[%d] == %d\n", i, RI.m_pScaledGlyphs->log_clusters[i]));
1372 			if(iGlyphsStart < 0 && RI.m_pScaledGlyphs->log_clusters[i] == iOffsetStart)
1373 				iGlyphsStart = i;
1374 
1375 			if(RI.m_pScaledGlyphs->log_clusters[i] == iOffsetEnd)
1376 			{
1377 				iGlyphsEnd = i;
1378 				break;
1379 			}
1380 
1381 			RI.m_iVisDir == UT_BIDI_RTL ? --i : ++i;
1382 		}
1383 		if (RI.m_iVisDir == UT_BIDI_RTL)
1384 		{
1385 			// Swap the start and end offset for rtl text, so start <= end again.
1386 			// Note that this means that iGlyphsStart now points to the offset of
1387 			// the "last  glyph that should not rendered yet". The glyphs starting from
1388 			// this offset + 1 should be rendered. iGlyphsEnd on the other hand now
1389 			// points to the offset of the last glyph that should be rendered.
1390 			std::swap(iGlyphsStart, iGlyphsEnd);
1391 		}
1392 
1393 		xxx_UT_DEBUGMSG(("iGlyphsStart: %d, iGlyphsEnd: %d\n", iGlyphsStart, iGlyphsEnd));
1394 		UT_return_if_fail(iGlyphsStart <= iGlyphsEnd);
1395 
1396 		// both of these can be 0 (iGlyphsEnd == 0 => only 1 glyph)
1397 		xxx_UT_DEBUGMSG(("Drawing glyph subset from %d to %d (byte offsets %d, %d)\n",
1398 					 iGlyphsStart, iGlyphsEnd,
1399 					 iOffsetStart, iOffsetEnd));
1400 
1401 		gs.num_glyphs = iGlyphsEnd - iGlyphsStart;
1402 		gs.glyphs
1403 			= RI.m_iVisDir == UT_BIDI_RTL ?
1404 				RI.m_pScaledGlyphs->glyphs + iGlyphsStart + 1 :
1405 				RI.m_pScaledGlyphs->glyphs + iGlyphsStart;
1406 		gs.log_clusters
1407 			= RI.m_iVisDir == UT_BIDI_RTL ?
1408 				RI.m_pGlyphs->log_clusters + iGlyphsStart + 1 :
1409 				RI.m_pGlyphs->log_clusters + iGlyphsStart;
1410 
1411 		// finally we can render the substring
1412 		cairo_save(m_cr);
1413 		cairo_translate(m_cr, xoff, yoff);
1414 		pango_cairo_show_glyph_string(m_cr, pf, &gs);
1415 		cairo_restore(m_cr);
1416 	}
1417 }
1418 
1419 
_getCairoSurfaceFromContext(cairo_t * cr,const cairo_rectangle_t & rect)1420 cairo_surface_t * GR_CairoGraphics::_getCairoSurfaceFromContext(cairo_t *cr,
1421                                                                      const cairo_rectangle_t & rect)
1422 {
1423 	cairo_surface_t * surface = cairo_surface_create_similar(cairo_get_target(cr),
1424 	                                       CAIRO_CONTENT_COLOR_ALPHA,
1425 	                                       rect.width, rect.height);
1426 
1427 	cairo_surface_t * source = cairo_get_target(cr);
1428 	cairo_surface_flush(source);
1429 
1430 	cairo_t * dest = cairo_create(surface);
1431 	cairo_set_source_surface(dest, source, rect.x, rect.y);
1432 	cairo_paint(dest);
1433 	cairo_destroy(dest);
1434 	return surface;
1435 }
1436 
1437 
_setSource(cairo_t * cr,const UT_RGBColor & clr)1438 void GR_CairoGraphics::_setSource(cairo_t *cr, const UT_RGBColor &clr)
1439 {
1440 	const GR_CairoPatternImpl * pat
1441 		= NULL;//dynamic_cast<const GR_CairoPatternImpl *>(clr.pattern());
1442 	if(pat) {
1443 		cairo_set_source(cr, pat->getPattern());
1444 	}
1445 	else {
1446 		cairo_set_source_rgb(cr, clr.m_red/255., clr.m_grn/255., clr.m_blu/255.);
1447 	}
1448 }
1449 
1450 
mapCapStyle(GR_Graphics::CapStyle in)1451 static cairo_line_cap_t mapCapStyle(GR_Graphics::CapStyle in)
1452 {
1453 	switch (in) {
1454 	case GR_Graphics::CAP_ROUND:
1455 		return CAIRO_LINE_CAP_ROUND;
1456 	case GR_Graphics::CAP_PROJECTING:
1457 		return CAIRO_LINE_CAP_SQUARE;
1458 	case GR_Graphics::CAP_BUTT:
1459 	default:
1460 		return CAIRO_LINE_CAP_BUTT;
1461     }
1462 }
1463 
1464 /*
1465  * \param width line width
1466  * \param dashes Array for dash pattern
1467  * \param n_dashes IN: length of dashes, OUT: number of needed dash pattern fields
1468  */
mapDashStyle(GR_Graphics::LineStyle in,double width,double * dashes,int * n_dashes)1469 static void mapDashStyle(GR_Graphics::LineStyle in, double width, double *dashes, int *n_dashes)
1470 {
1471 	switch (in) {
1472 	case GR_Graphics::LINE_ON_OFF_DASH:
1473 	case GR_Graphics::LINE_DOUBLE_DASH:				// see GDK_LINE_DOUBLE_DASH, but it was never used
1474 		UT_ASSERT(*n_dashes > 0);
1475 		dashes[0] = 4 * width;
1476 		*n_dashes = 1;
1477 		break;
1478 	case GR_Graphics::LINE_DOTTED:
1479 		UT_ASSERT(*n_dashes > 0);
1480 		dashes[0] = 2 * width;
1481 		*n_dashes = 1;
1482 		break;
1483 	case GR_Graphics::LINE_SOLID:
1484 	default:
1485 		*n_dashes = 0;
1486 	}
1487 }
1488 
mapJoinStyle(GR_Graphics::JoinStyle in)1489 static cairo_line_join_t mapJoinStyle(GR_Graphics::JoinStyle in)
1490 {
1491 	switch ( in ) {
1492 	case GR_Graphics::JOIN_ROUND:
1493 		return CAIRO_LINE_JOIN_ROUND;
1494 	case GR_Graphics::JOIN_BEVEL:
1495 		return CAIRO_LINE_JOIN_BEVEL;
1496 	case GR_Graphics::JOIN_MITER:
1497 	default:
1498 		return CAIRO_LINE_JOIN_MITER;
1499     }
1500 }
1501 
_resetClip(void)1502 void GR_CairoGraphics::_resetClip(void)
1503 {
1504 	if (m_cr == NULL)
1505 		return;
1506 	xxx_UT_DEBUGMSG(("Reset clip in cairo xp!!! \n"));
1507 	cairo_reset_clip(m_cr);
1508 }
1509 
_setProps()1510 void GR_CairoGraphics::_setProps()
1511 {
1512 	if (m_cr == NULL)
1513 		return;
1514 
1515 	if(m_curColorDirty)
1516 	{
1517 		_setSource(m_cr, m_curColor);
1518 
1519 		m_curColorDirty = false;
1520 	}
1521 	if(m_clipRectDirty)
1522 	{
1523 		_resetClip();
1524 		if (m_pRect)
1525 		{
1526 			double x, y, width, height;
1527 			x = _tdudX(m_pRect->left);
1528 			y = _tdudY(m_pRect->top);
1529 			width = _tduR(m_pRect->width);
1530 			height = _tduR(m_pRect->height);
1531 			cairo_rectangle(m_cr, x, y, width, height);
1532 			cairo_clip(m_cr);
1533 		}
1534 		m_clipRectDirty = false;
1535 	}
1536 	if(m_linePropsDirty)
1537 	{
1538 		double dashes[2];
1539 		double width;
1540 		int n_dashes;
1541 		width = tduD(m_lineWidth);
1542 		if(width < 1.0)
1543 			width = 1.0;
1544 		cairo_set_line_width (m_cr, width);
1545 		cairo_set_line_join (m_cr, mapJoinStyle(m_joinStyle));
1546 		cairo_set_line_cap (m_cr, mapCapStyle(m_capStyle));
1547 
1548 		width = cairo_get_line_width(m_cr);
1549 		n_dashes = G_N_ELEMENTS(dashes);
1550 		mapDashStyle(m_lineStyle, width, dashes, &n_dashes);
1551 		cairo_set_dash(m_cr, dashes, n_dashes, 0);
1552 
1553 		m_linePropsDirty = false;
1554 	}
1555 }
1556 
_scaleCharacterMetrics(GR_PangoRenderInfo & RI)1557 void GR_CairoGraphics::_scaleCharacterMetrics(GR_PangoRenderInfo & RI)
1558 {
1559 	UT_uint32 iZoom = getZoomPercentage();
1560 
1561 	xxx_UT_DEBUGMSG(("_scaleCharacterMetrics... \n"));
1562 	for(int i = 0; i < RI.m_pGlyphs->num_glyphs; ++i)
1563 	{
1564 		RI.m_pScaledGlyphs->glyphs[i].geometry.x_offset =
1565 			_tduX(RI.m_pGlyphs->glyphs[i].geometry.x_offset);
1566 
1567 
1568 		RI.m_pScaledGlyphs->glyphs[i].geometry.y_offset = _tduY(RI.m_pGlyphs->glyphs[i].geometry.y_offset);
1569 
1570 		RI.m_pScaledGlyphs->glyphs[i].geometry.width =_tduX(RI.m_pGlyphs->glyphs[i].geometry.width );
1571 	}
1572 	RI.m_iZoom = iZoom;
1573 }
1574 
1575 
_scaleJustification(GR_PangoRenderInfo & RI)1576 void GR_CairoGraphics::_scaleJustification(GR_PangoRenderInfo & RI)
1577 {
1578 	RI.m_iZoom = getZoomPercentage();
1579 	return;
1580 }
1581 
1582 
1583 /*!
1584    This function is called after shaping and before any operations are done on
1585    the glyphs. Although Pango does not have a separate character placement
1586    stage, we need to scale the glyph metrics to appropriate zoom level (since
1587    we shape @100% zoom).
1588 
1589    NB: this is probably not ideal with int arithmetic as moving from one zoom
1590        to another, and back we are bound to end up with incorrect metrics due
1591        to rounding errors.
1592 */
measureRenderedCharWidths(GR_RenderInfo & ri)1593 void GR_CairoGraphics::measureRenderedCharWidths(GR_RenderInfo & ri)
1594 {
1595 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
1596 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1597 
1598 	_scaleCharacterMetrics(RI);
1599 
1600 	if(RI.m_pJustify)
1601 	{
1602 		_scaleJustification(RI);
1603 	}
1604 }
1605 
appendRenderedCharsToBuff(GR_RenderInfo &,UT_GrowBuf &) const1606 void GR_CairoGraphics::appendRenderedCharsToBuff(GR_RenderInfo & /*ri*/,
1607 													 UT_GrowBuf & /*buf*/) const
1608 {
1609 	UT_return_if_fail( UT_NOT_IMPLEMENTED );
1610 }
1611 
1612 /*!
1613     returns true on success
1614  */
_scriptBreak(GR_PangoRenderInfo & ri)1615 bool GR_CairoGraphics::_scriptBreak(GR_PangoRenderInfo &ri)
1616 {
1617 	UT_return_val_if_fail(ri.m_pText && ri.m_pGlyphs && ri.m_pItem, false);
1618 
1619 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem*)ri.m_pItem;
1620 
1621 	// fill the static buffer with UTF8 text
1622 	UT_return_val_if_fail(ri.getUTF8Text(), false);
1623 
1624 	// the buffer has to have at least one more slot than the number of glyphs
1625 	if(!ri.s_pLogAttrs || ri.s_iStaticSize < ri.sUTF8->length() + 1)
1626 	{
1627 		UT_return_val_if_fail(ri.allocStaticBuffers(ri.sUTF8->length()+1),
1628 							  false);
1629 	}
1630 
1631 	pango_break(ri.sUTF8->utf8_str(),
1632 				ri.sUTF8->byteLength(),
1633 				&(pItem->m_pi->analysis),
1634 				ri.s_pLogAttrs, ri.s_iStaticSize);
1635 
1636 	ri.s_pOwnerLogAttrs = &ri;
1637 	return true;
1638 }
1639 
canBreak(GR_RenderInfo & ri,UT_sint32 & iNext,bool bAfter)1640 bool GR_CairoGraphics::canBreak(GR_RenderInfo & ri, UT_sint32 &iNext,
1641 									bool bAfter)
1642 {
1643 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO &&
1644 						  ri.m_iOffset < ri.m_iLength, false);
1645 
1646 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1647 	iNext = -1;
1648 
1649 	if(!RI.s_pLogAttrs || RI.s_pOwnerLogAttrs != &ri)
1650 	{
1651 		if(!_scriptBreak(RI))
1652 			return false;
1653 	}
1654 
1655 	UT_uint32 iDelta  = 0;
1656 	if(bAfter)
1657 	{
1658 		// the caller wants to know if break can occur on the (logically) right
1659 		// edge of the given character
1660 
1661 		if(ri.m_iOffset + 1 >= (UT_sint32)RI.s_iStaticSize)
1662 		{
1663 			// we are quering past what have data for
1664 			return false;
1665 		}
1666 
1667 		// we will examine the next character, since Pango tells us about
1668 		// breaking on the left edge
1669 		iDelta = 1;
1670 	}
1671 
1672 	if(RI.s_pLogAttrs[ri.m_iOffset + iDelta].is_line_break)
1673 		return true;
1674 
1675 	// find the next break
1676 	for(UT_sint32 i = ri.m_iOffset + iDelta + 1; i < RI.m_iLength; ++i)
1677 	{
1678 		if(RI.s_pLogAttrs[i].is_line_break)
1679 		{
1680 			iNext = i - iDelta;
1681 			break;
1682 		}
1683 	}
1684 
1685 	if(iNext == -1)
1686 	{
1687 		// we have not found any breaks in this run -- signal this to the
1688 		// caller
1689 		iNext = -2;
1690 	}
1691 
1692 	return false;
1693 }
1694 
1695 
needsSpecialCaretPositioning(GR_RenderInfo & ri)1696 bool GR_CairoGraphics::needsSpecialCaretPositioning(GR_RenderInfo &ri)
1697 {
1698 	// something smarter is needed here, so we do not go through this for
1699 	// langugages that do not need it.
1700 	// HACK HACK HACK
1701 	// This simple code will return false for European Languages,
1702 	// otherwise it will return true
1703 	// We should really some fancy pango function to determine if
1704 	// we have a complex script with combining chars
1705 	//
1706 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1707 	if(RI.m_pText == NULL)
1708 		return false;
1709 
1710 	UT_TextIterator & text = static_cast<UT_TextIterator &>(*RI.m_pText);
1711 	UT_uint32 origPos = text.getPosition();
1712 
1713 	for(UT_sint32 i = 0; i < RI.m_iLength && text.getStatus() == UTIter_OK;
1714 		++i, ++text)
1715 	{
1716 		UT_UCS4Char c = text.getChar();
1717 		if(c != ' ' && c<256)
1718 		{
1719 			// restore the iterator back to its original position
1720 			text.setPosition(origPos);
1721 			return false;
1722 		}
1723 	}
1724 
1725 	// restore the iterator back to its original position
1726 	text.setPosition(origPos);
1727 	return true;
1728 }
1729 
adjustCaretPosition(GR_RenderInfo & ri,bool bForward)1730 UT_uint32 GR_CairoGraphics::adjustCaretPosition(GR_RenderInfo & ri,
1731 													bool bForward)
1732 {
1733 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO, 0);
1734 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1735 
1736 	if(!RI.s_pLogAttrs || RI.s_pOwnerLogAttrs != &ri)
1737 		_scriptBreak(RI);
1738 
1739 	UT_return_val_if_fail( RI.s_pLogAttrs, RI.m_iOffset );
1740 
1741 	UT_sint32 iOffset = ri.m_iOffset;
1742 
1743 	if(bForward)
1744 		while(!RI.s_pLogAttrs[iOffset].is_cursor_position &&
1745 			  iOffset < RI.m_iLength)
1746 			iOffset++;
1747 	else
1748 		while(!RI.s_pLogAttrs[iOffset].is_cursor_position && iOffset > 0)
1749 			iOffset--;
1750 
1751 	return iOffset;
1752 }
1753 
adjustDeletePosition(GR_RenderInfo & ri)1754 void GR_CairoGraphics::adjustDeletePosition(GR_RenderInfo & ri)
1755 {
1756 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
1757 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1758 
1759 	if(ri.m_iOffset + ri.m_iLength >= (UT_sint32)RI.m_iCharCount)
1760 		return;
1761 
1762 	if(!RI.s_pLogAttrs || RI.s_pOwnerLogAttrs != &ri)
1763 		_scriptBreak(RI);
1764 
1765 	UT_return_if_fail( RI.s_pLogAttrs);
1766 
1767 	// deletion can start anywhere, but can only end on cluster boundary if the
1768 	// base character is included in the deletion
1769 
1770 	// get the offset of the character that follows the deleted segment
1771 	UT_sint32 iNextOffset = (UT_sint32)ri.m_iOffset + ri.m_iLength;
1772 
1773 	if(RI.s_pLogAttrs[iNextOffset].is_cursor_position)
1774 	{
1775 		// the next char is a valid caret position, so we are OK
1776 		return;
1777 	}
1778 
1779 	// If we got this far, we were asked to end the deletion before a character
1780 	// that is not a valid caret position. We need to determine if the segment
1781 	// we are asked to delete contains the character's base character; if it
1782 	// does, we have to expand the seletion to delete the entire cluster.
1783 
1784 	UT_sint32 iOffset = iNextOffset - 1;
1785 	while(iOffset > 0 && iOffset > ri.m_iOffset &&
1786 		  !RI.s_pLogAttrs[iOffset].is_cursor_position)
1787 		iOffset--;
1788 
1789 	if(RI.s_pLogAttrs[iOffset].is_cursor_position)
1790 	{
1791 		// our delete segment includes the base character, so we have to delete
1792 		// the entire cluster
1793 		iNextOffset = iOffset + 1;
1794 
1795 		while(iNextOffset < (UT_sint32)RI.s_iStaticSize - 1 // -1 because iLogBuffSize is char count +1
1796 			  && !RI.s_pLogAttrs[iNextOffset].is_cursor_position)
1797 			iNextOffset++;
1798 
1799 
1800 		ri.m_iLength = iNextOffset - ri.m_iOffset;
1801 		return;
1802 	}
1803 
1804 	// two posibilities: we are deleting only a cluster appendage or the run
1805 	// does not contain base character. The latter should probably not happen,
1806 	// but in both cases we will let the delete proceed as is
1807 }
1808 
1809 /*!
1810  * I believe this code clears all the justification points. MES June 2008
1811  * It returns the total space assigned to justify the text in layout units.
1812  */
resetJustification(GR_RenderInfo & ri,bool bPermanent)1813 UT_sint32 GR_CairoGraphics::resetJustification(GR_RenderInfo & ri,
1814 												   bool bPermanent)
1815 {
1816 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO, 0);
1817 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1818 
1819 	if(!RI.m_pJustify)
1820 		return 0;
1821 
1822 
1823 	UT_sint32 iWidth2 = 0;
1824 	for(UT_sint32 i = 0; i < RI.m_pGlyphs->num_glyphs; ++i)
1825 	{
1826 		iWidth2 += RI.m_pJustify[i];
1827 
1828 		// TODO here we need to substract the amount from pango metrics
1829 		RI.m_pGlyphs->glyphs[i].geometry.width -= RI.m_pJustify[i];
1830 	}
1831 	//
1832 	// This sets the glyphs that will be displayed on screen.
1833 	//
1834 	_scaleCharacterMetrics(RI);
1835 
1836 	if(bPermanent)
1837 	{
1838 		delete [] RI.m_pJustify;
1839 		RI.m_pJustify = NULL;
1840 	}
1841 	else
1842 	{
1843 		memset(RI.m_pJustify, 0, RI.m_pGlyphs->num_glyphs * sizeof(int));
1844 	}
1845 
1846 	// Justification in pango units. Convert to layout units.
1847 
1848 	return -ptlunz(iWidth2);
1849 }
1850 
1851 
countJustificationPoints(const GR_RenderInfo & ri) const1852 UT_sint32 GR_CairoGraphics::countJustificationPoints(const GR_RenderInfo & ri) const
1853 {
1854 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO, 0);
1855 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
1856 
1857 	UT_return_val_if_fail(RI.m_pText, 0);
1858 	UT_TextIterator & text = *RI.m_pText;
1859 	UT_uint32 iPosEnd   = text.getUpperLimit();
1860 
1861 	text.setPosition(iPosEnd);
1862 	UT_return_val_if_fail( text.getStatus()== UTIter_OK, 0 );
1863 
1864 
1865 	UT_sint32 iCount = 0;
1866 	bool bNonBlank = false;
1867 	UT_sint32 iLen = RI.m_iLength;
1868 
1869 	for(; iLen > 0 && text.getStatus() == UTIter_OK; --text, --iLen)
1870 	{
1871 		UT_UCS4Char c = text.getChar();
1872 
1873 		if(c != UCS_SPACE)
1874 		{
1875 			bNonBlank = true;
1876 			continue;
1877 		}
1878 
1879 		// only count this space if this is not last run, or if we
1880 		// have found something other than spaces
1881 		if(!ri.m_bLastOnLine || bNonBlank)
1882 			iCount++;
1883 	}
1884 
1885 	if(!bNonBlank)
1886 	{
1887 		return -iCount;
1888 	}
1889 	else
1890 	{
1891 		return iCount;
1892 	}
1893 }
1894 
1895 /*!
1896     We take the same approach as with Uniscribe; we store the justification
1897     amount in a separate array of the ri and add it to the offsets before we
1898     draw. We will probably need some static buffers to speed things up
1899 
1900 It requires as input RI.m_iJustificationAmount and RI.m_iJustificationPoints.
1901 These are determined in fp_TextRun using calculations in layout units
1902 
1903  */
justify(GR_RenderInfo & ri)1904 void GR_CairoGraphics::justify(GR_RenderInfo & ri)
1905 {
1906 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
1907 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &) ri;
1908 	if(!RI.m_iJustificationPoints || !RI.m_iJustificationAmount ||
1909 	   !RI.m_pGlyphs)
1910 		return;
1911 
1912 	// make sure that we are not adding apples to oranges
1913 	// We don't need this now.
1914 	//	if(RI.m_iZoom != getZoomPercentage())
1915 	//	_scaleCharacterMetrics(RI);
1916 
1917 	if(!RI.m_pJustify)
1918 		RI.m_pJustify = new int[RI.m_pGlyphs->num_glyphs];
1919 
1920 	UT_return_if_fail(RI.m_pJustify);
1921 	memset(RI.m_pJustify, 0, RI.m_pGlyphs->num_glyphs * sizeof(int));
1922 
1923 	UT_uint32 iExtraSpace = RI.m_iJustificationAmount;
1924 	UT_uint32 iPoints     = RI.m_iJustificationPoints;
1925 
1926 	xxx_UT_DEBUGMSG(("::Justify Extra justification space %d \n",iExtraSpace));
1927 	xxx_UT_DEBUGMSG(("::Justify Number of justification points %d \n",iPoints));
1928 
1929 	UT_return_if_fail(RI.m_pText);
1930 
1931 	UT_TextIterator & text = *RI.m_pText;
1932 	UT_sint32 iGlyphCount  = RI.m_pGlyphs->num_glyphs;
1933 	UT_BidiCharType iDir   =
1934 		RI.m_iVisDir % 2 ? UT_BIDI_RTL : UT_BIDI_LTR;
1935 
1936 	// Split into two big loops to avoid all the LTR/RTL logic inside -- if
1937 	// you make changes to one branch, please make sure to do the same to the
1938 	// other.
1939 
1940 	UT_sint32 i; // glyph index
1941 	UT_sint32 j; // text index
1942 
1943 	UT_uint32 iSpace = iExtraSpace/iPoints;
1944 
1945 	if (iDir == UT_BIDI_LTR)
1946 	{
1947 		i = 0;
1948 		j = 0;
1949 
1950 		while (text.getStatus() == UTIter_OK &&
1951 			   i < iGlyphCount &&
1952 			   j < RI.m_iLength)
1953 		{
1954 			UT_UCS4Char c = text.getChar();
1955 
1956 			if(c == UCS_SPACE)
1957 			{
1958 
1959 
1960 				// iSpace is in layout units. Convert to pango units
1961 
1962 				RI.m_pJustify[i] = ltpunz(iSpace);
1963 
1964 				iPoints--;
1965 
1966 				// add this amount the pango units
1967 				xxx_UT_DEBUGMSG(("Justify-1 Prev geom width %d additional %d \n",RI.m_pGlyphs->glyphs[i].geometry.width,RI.m_pJustify[i]));
1968 
1969 				RI.m_pGlyphs->glyphs[i].geometry.width += RI.m_pJustify[i];
1970 
1971 				if(!iPoints)
1972 					break;
1973 			}
1974 
1975 
1976 			// skip over any glyphs that belong to the current character
1977 			// LTR run, the glyphs and the text are in the same order,
1978 			// and logical offsets are increasing
1979 			UT_sint32 iOffset = RI.m_pLogOffsets[i++];
1980 
1981 			while (RI.m_pLogOffsets[i] == iOffset && i < iGlyphCount)
1982 				++i;
1983 
1984 			if (i >= iGlyphCount)
1985 				break;
1986 
1987 			// if the glyph cluster represents more characters than its
1988 			// length, we have to advance the iterator accordingly
1989 			UT_sint32 iDiff = RI.m_pLogOffsets[i] - iOffset;
1990 
1991 			text += iDiff;
1992 			j += iDiff;
1993 		}
1994 	}
1995 	else
1996 	{
1997 		i = iGlyphCount - 1;
1998 		j = 0;
1999 
2000 		while (text.getStatus() == UTIter_OK &&
2001 			   i >= 0 &&
2002 			   j < RI.m_iLength)
2003 		{
2004 			UT_UCS4Char c = text.getChar();
2005 
2006 			if(c == UCS_SPACE)
2007 			{
2008 
2009 				iPoints--;
2010 
2011 				// iSpace is in layout units. Convert to pango units
2012 
2013 				RI.m_pJustify[i] = ltpunz(iSpace);
2014 
2015 				// add this amount the pango metrics
2016 
2017 				xxx_UT_DEBUGMSG(("Justify-2 Prev geom width %d additional %d \n",RI.m_pGlyphs->glyphs[i].geometry.width,RI.m_pJustify[i]));
2018 				RI.m_pGlyphs->glyphs[i].geometry.width += RI.m_pJustify[i];
2019 
2020 				if(!iPoints)
2021 					break;
2022 			}
2023 
2024 
2025 			// skip over any glyphs that belong to the current character
2026 			// RTL run, so the glyphs are in reversed order of the text,
2027 			// and logical offsets are decreesing
2028 			UT_sint32 iOffset = RI.m_pLogOffsets[i--];
2029 
2030 			while (RI.m_pLogOffsets[i] == iOffset && i >= 0)
2031 				--i;
2032 
2033 			if (i < 0)
2034 				break;
2035 
2036 			// if the glyph cluster represents more characters than its
2037 			// length, we have to advance the iterator accordingly
2038 			UT_sint32 iDiff = iOffset - RI.m_pLogOffsets[i];
2039 
2040 			text += iDiff;
2041 			j += iDiff;
2042 		}
2043 	}
2044 
2045 	//
2046  	// Now scale the metrics for the drawing glyphs
2047  	//
2048  	_scaleCharacterMetrics(RI);
2049 }
2050 
2051 /*!
2052  * This function takes (x,y) in layout units and determines the location in the
2053  * pango string.
2054  */
XYToPosition(const GR_RenderInfo & ri,UT_sint32 x,UT_sint32) const2055 UT_uint32 GR_CairoGraphics::XYToPosition(const GR_RenderInfo & ri, UT_sint32 x,
2056 											 UT_sint32 /*y*/) const
2057 {
2058 	UT_return_val_if_fail(ri.getType() == GRRI_CAIRO_PANGO, 0);
2059 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &) ri;
2060 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem *)RI.m_pItem;
2061 	UT_return_val_if_fail(pItem, 0);
2062 
2063 	// TODO: this is very inefficient: to cache or not to cache ?
2064 	UT_UTF8String utf8;
2065 
2066 	UT_sint32 i;
2067 	for(i = 0; i < RI.m_iLength; ++i, ++(*(RI.m_pText)))
2068 	{
2069 		UT_return_val_if_fail(RI.m_pText->getStatus() == UTIter_OK, 0);
2070 		if(isSymbol())
2071 		{
2072 			utf8 += adobeToUnicode(RI.m_pText->getChar());
2073 		}
2074 		else if(isDingbat())
2075 		{
2076 			utf8 += adobeDingbatsToUnicode(RI.m_pText->getChar());
2077 		}
2078 		utf8 += RI.m_pText->getChar();
2079 	}
2080 
2081 	// Since the glyphs are measured in pango units
2082 	// we need to convert from layout units
2083 
2084 	int x_pos = ltpunz(x);
2085 	int len = utf8.byteLength();
2086 	int iPos = len;
2087 	int iTrailing;
2088 	const char * pUtf8 = utf8.utf8_str();
2089 
2090 	/* Another jolly pango function:
2091 	 * if x is greater than the width of the string, it will happily read
2092 	 * pass the end of it.
2093 	 */
2094 	pango_glyph_string_x_to_index(RI.m_pGlyphs,
2095 								  (char*)pUtf8, // do not like this ...
2096 								  len,
2097 								  &(pItem->m_pi->analysis),
2098 								  x_pos,
2099 								  &iPos,
2100 								  &iTrailing);
2101 
2102 	/* if at the end (or pass) the end of the string, just return the length*/
2103 	if (iPos >= len)
2104 	{
2105 		return RI.m_iLength;
2106 	}
2107 
2108 	i = g_utf8_pointer_to_offset(pUtf8, pUtf8 + iPos);
2109 
2110 	if(iTrailing)
2111 		i++;
2112 
2113 	return i;
2114 }
2115 
2116 /*!
2117  * Return a location in layout units (x,y) of a pango glyph.
2118  */
positionToXY(const GR_RenderInfo & ri,UT_sint32 & x,UT_sint32 &,UT_sint32 & x2,UT_sint32 &,UT_sint32 &,bool &) const2119 void GR_CairoGraphics::positionToXY(const GR_RenderInfo & ri,
2120 										UT_sint32& x, UT_sint32& /*y*/,
2121 										UT_sint32& x2, UT_sint32& /*y2*/,
2122 										UT_sint32& /*height*/, bool& /*bDirection*/) const
2123 {
2124 	UT_return_if_fail(ri.getType() == GRRI_CAIRO_PANGO);
2125 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &) ri;
2126 	GR_CairoPangoItem * pItem = (GR_CairoPangoItem *)RI.m_pItem;
2127 
2128 	if(!pItem)
2129 		return;
2130 
2131 	// TODO: this is very inefficient: to cache or not to cache ?
2132 	UT_UTF8String utf8;
2133 
2134 	UT_sint32 i;
2135 	for(i = 0; i < RI.m_iLength; ++i, ++(*(RI.m_pText)))
2136 	{
2137 		UT_return_if_fail(RI.m_pText->getStatus() == UTIter_OK);
2138 		if(isSymbol())
2139 		{
2140 			utf8 += adobeToUnicode(RI.m_pText->getChar());
2141 		}
2142 		else if(isDingbat())
2143 		{
2144 			utf8 += adobeDingbatsToUnicode(RI.m_pText->getChar());
2145 		}
2146 		utf8 += RI.m_pText->getChar();
2147 	}
2148 
2149 	UT_sint32 iByteOffset = 0;
2150 	gboolean  bTrailing = TRUE;
2151 	const char * pUtf8 = utf8.utf8_str();
2152 	const char * pOffset = NULL;
2153 
2154 	if(RI.m_iOffset < 0)
2155 	{
2156 		// we translate negative offsets into leading edge of the first char
2157 		iByteOffset = 0;
2158 		bTrailing = FALSE;
2159 	}
2160 	else if(RI.m_iOffset == 0)
2161 	{
2162 		// trailing edge of the first char
2163 		iByteOffset = 0;
2164 	}
2165 	else if( i > RI.m_iOffset)
2166 	{
2167 		// withing range of our string
2168 		pOffset = g_utf8_offset_to_pointer (pUtf8, RI.m_iOffset);
2169 	}
2170 	else if(i >= 1)
2171 	{
2172 		// this is the case where the requested offset is past the end
2173 		// of our string; we will use the last char; as we have more than one
2174 		// character we can use g_utf8_prev_char ();
2175 		pOffset = g_utf8_prev_char (pUtf8 + utf8.byteLength());
2176 	}
2177 	else
2178 	{
2179 		// something utterly wrong ...
2180 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2181 		iByteOffset = 0;
2182 	}
2183 
2184 	if (pOffset)
2185 		iByteOffset = pOffset - pUtf8;
2186 
2187 	pango_glyph_string_index_to_x (RI.m_pGlyphs,
2188 								   (char*)pUtf8, // do not like this ...
2189 								   utf8.byteLength(),
2190 								   &(pItem->m_pi->analysis),
2191 								   iByteOffset,
2192 								   bTrailing,
2193 								   &x);
2194 
2195 	//
2196 	// Since the glyphs are measured in pango units we need to convert to layout
2197 	//
2198 	x = ptlunz(x);
2199 	x2 = x;
2200 }
2201 
drawChars(const UT_UCSChar * pChars,int iCharOffset,int iLength,UT_sint32 xoff,UT_sint32 yoff,int * pCharWidth)2202 void GR_CairoGraphics::drawChars(const UT_UCSChar* pChars,
2203 									int iCharOffset, int iLength,
2204 									UT_sint32 xoff, UT_sint32 yoff,
2205 									 int * pCharWidth)
2206 {
2207 	if (m_cr == NULL)
2208 		return;
2209 	_setProps();
2210 	UT_UTF8String utf8;
2211 	xxx_UT_DEBUGMSG(("isDingBat %d \n",isDingbat()));
2212 	if(isSymbol())
2213 	{
2214 		for(int i = iCharOffset; i < iCharOffset + iLength; ++i)
2215 		{
2216 			utf8 += adobeToUnicode(pChars[i]);
2217 		}
2218 	}
2219 	else if(isDingbat())
2220 	{
2221 		for(int i = iCharOffset; i < iCharOffset + iLength; ++i)
2222 		{
2223 			utf8 += adobeDingbatsToUnicode(pChars[i]);
2224 		}
2225 	}
2226 	else
2227 	{
2228 		utf8.appendUCS4(pChars + iCharOffset, iLength);
2229 	}
2230 
2231 	// this function expect indexes in bytes !!! (stupid)
2232 	GList * pItems = pango_itemize(getContext(),
2233 								   utf8.utf8_str(),
2234 								   0, utf8.byteLength(),
2235 								   NULL, NULL);
2236 
2237 	int iItemCount = g_list_length(pItems);
2238 	PangoGlyphString * pGstring = pango_glyph_string_new();
2239 
2240 	double xoffD = _tdudX(xoff);
2241 	double yoffD = _tdudY(yoff+getFontAscent());
2242 
2243 	PangoFont * pf = m_pPFont->getPangoFont();
2244 	PangoRectangle LR;
2245 	PangoFontset *pfs = NULL;
2246 	bool bDoFontSubstitution = false;
2247 	bool bClear_pf = false;
2248 
2249 	for(int i = 0; i < iItemCount; ++i)
2250 	{
2251 		PangoItem *pItem = (PangoItem *)g_list_nth(pItems, i)->data;
2252 
2253 		if(!pItem)
2254 		{
2255 			UT_ASSERT(pItem);
2256 			if(pGstring)
2257 				pango_glyph_string_free(pGstring);
2258 			_pango_item_list_free(pItems);
2259 			return;
2260 		}
2261 
2262 		if (bDoFontSubstitution)
2263 		{
2264 			if (bClear_pf)
2265 			{
2266 				g_object_unref(pf);
2267 			}
2268 			UT_ASSERT(pfs);
2269 			UT_sint32 fontSize = pango_font_description_get_size(pango_font_describe(m_pPFont->getPangoFont()));
2270 			pf = pango_fontset_get_font (pfs, g_utf8_get_char (utf8.utf8_str()+pItem->offset));
2271 			PangoFontDescription * pfd = pango_font_describe (pf);
2272 			pango_font_description_set_size (pfd, fontSize*m_iDeviceResolution/getResolution());
2273 			UT_ASSERT(pfd);
2274 			pf = pango_context_load_font(getLayoutContext(), pfd);
2275 			pango_font_description_free(pfd);
2276 			bClear_pf = true;
2277 		}
2278 		g_object_unref(pItem->analysis.font);
2279 		pItem->analysis.font = (PangoFont*)g_object_ref((GObject*)pf);
2280 
2281 		pango_shape(utf8.utf8_str()+ pItem->offset,
2282 					pItem->length,
2283 					&(pItem->analysis),
2284 					pGstring);
2285 
2286 		if (!bDoFontSubstitution)
2287 		{
2288 			// The following code only checks the first character of utf8
2289 			// to see if a font substitution is needed
2290 			// TODO: modify code so that it can handle a string where multiple
2291 			//       fonts are needed.
2292 			if (pGstring->glyphs[0].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
2293 			{
2294 				bDoFontSubstitution = true;
2295 				pfs = pango_font_map_load_fontset (getFontMap(),getContext(),
2296 												   m_pPFont->getPangoDescription(),
2297 												   pItem->analysis.language);
2298 				i--;
2299 				continue;
2300 			}
2301 		}
2302 
2303 		if(pCharWidth)
2304 		{
2305 			for(int j=0; j<pGstring->num_glyphs; j++)
2306 			{
2307 
2308 				pGstring->glyphs[j].geometry.width = _tduX(pCharWidth[j]*PANGO_SCALE);
2309 			}
2310 		}
2311 
2312 		cairo_save(m_cr);
2313 		cairo_translate(m_cr, xoffD, yoffD);
2314 		pango_cairo_show_glyph_string(m_cr, pf, pGstring);
2315 		cairo_restore(m_cr);
2316 
2317 		// now advance xoff
2318 		pango_glyph_string_extents(pGstring, pf, NULL, &LR);
2319 		xoffD += PANGO_PIXELS(LR.width);
2320 	}
2321 
2322 	if(pGstring)
2323 		pango_glyph_string_free(pGstring);
2324 	_pango_item_list_free(pItems);
2325 	if(pfs)
2326 	{
2327 		g_object_unref((GObject*)pfs);
2328 		pfs = NULL;
2329 	}
2330 	if (bClear_pf)
2331 	{
2332 		g_object_unref(pf);
2333 	}
2334 }
2335 
measureString(const UT_UCSChar * pChars,int iCharOffset,int iLength,UT_GrowBufElement * pWidths,UT_uint32 * height)2336 UT_uint32 GR_CairoGraphics::measureString(const UT_UCSChar * pChars,
2337 											  int iCharOffset,
2338 											  int iLength,
2339 											  UT_GrowBufElement* pWidths,
2340 											  UT_uint32 * height)
2341 {
2342 	UT_UTF8String utf8;
2343 	UT_uint32 iWidth = 0;
2344 
2345 	if (!iLength || iLength <= iCharOffset)
2346 		return 0;
2347 
2348 	if(isSymbol())
2349 	{
2350 		for(int i = iCharOffset; i < iCharOffset + iLength; ++i)
2351 		{
2352 			utf8 += adobeToUnicode(pChars[i]);
2353 		}
2354 	}
2355 	else if(isDingbat())
2356 	{
2357 		for(int i = iCharOffset; i < iCharOffset + iLength; ++i)
2358 		{
2359 			utf8 += adobeDingbatsToUnicode(pChars[i]);
2360 		}
2361 	}
2362 	else
2363 	{
2364 		utf8.appendUCS4(pChars + iCharOffset, iLength);
2365 	}
2366 
2367 	// this function expect indexes in bytes !!! (stupid)
2368 	GList * pItems = pango_itemize(getLayoutContext(),
2369 								   utf8.utf8_str(),
2370 								   0, utf8.byteLength(),
2371 								   NULL, NULL);
2372 
2373 	PangoGlyphString * pGstring = pango_glyph_string_new();
2374 
2375 	PangoFont * pf = m_pPFont->getPangoLayoutFont();
2376 	PangoRectangle LR;
2377 	UT_uint32 iOffset = 0;
2378 	GList * l = pItems;
2379 
2380 	if (height)
2381 		*height = 0;
2382 
2383 	PangoFontset *pfs = NULL;
2384 	bool bDoFontSubstitution = false;
2385 	bool bClear_pf = false;
2386 
2387 	while (l)
2388 	{
2389 		PangoItem *pItem = (PangoItem*)l->data;
2390 
2391 		if(!pItem)
2392 		{
2393 			UT_ASSERT(pItem);
2394 			iWidth = 0;
2395 			goto cleanup;
2396 		}
2397 
2398 		if (bDoFontSubstitution)
2399 		{
2400 			if (bClear_pf)
2401 			{
2402 				g_object_unref(pf);
2403 			}
2404 			UT_ASSERT(pfs);
2405 			UT_sint32 fontSize = pango_font_description_get_size(pango_font_describe(m_pPFont->getPangoFont()));
2406 			pf = pango_fontset_get_font (pfs, g_utf8_get_char (utf8.utf8_str()+pItem->offset));
2407 			PangoFontDescription * pfd = pango_font_describe (pf);
2408 			pango_font_description_set_size (pfd, fontSize);
2409 			UT_ASSERT(pfd);
2410 			pf = pango_context_load_font(getLayoutContext(), pfd);
2411 			pango_font_description_free(pfd);
2412 			bClear_pf = true;
2413 		}
2414 
2415 		// the PangoItem has to take ownership of that.
2416 		g_object_unref(pItem->analysis.font);
2417 		pItem->analysis.font = (PangoFont*)g_object_ref((GObject*)pf);
2418 
2419 		pango_shape(utf8.utf8_str()+ pItem->offset,
2420 					pItem->length,
2421 					&(pItem->analysis),
2422 					pGstring);
2423 
2424 		if (!bDoFontSubstitution)
2425 		{
2426 			// The following code only checks the first character of utf8
2427 			// to see if a font substitution is needed
2428 			// TODO: modify code so that it can handle a string where multiple
2429 			//       fonts are needed.
2430 			if (pGstring->glyphs[0].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
2431 			{
2432 				UT_DEBUGMSG(("How MANY TIMES?\n"));
2433 				bDoFontSubstitution = true;
2434 				pfs = pango_font_map_load_fontset (getFontMap(),getContext(),
2435 												   m_pPFont->getPangoDescription(),
2436 												   pItem->analysis.language);
2437 				continue;
2438 			}
2439 		}
2440 
2441 		pango_glyph_string_extents(pGstring, pf, NULL, &LR);
2442 		iWidth += (UT_uint32)(((double) LR.width + (double)LR.x)/PANGO_SCALE);
2443 		UT_uint32 h = LR.height/PANGO_SCALE;
2444 		xxx_UT_DEBUGMSG(("measure string iWidth %d height %d \n",iWidth,h));
2445 		if (height && *height < h)
2446 
2447 
2448 
2449 			*height = h;
2450 
2451 		int * pLogOffsets = NULL;
2452 
2453 		/* this is rather involved, fortunately the width array is not
2454 		 * needed most of the time we use this function in abi
2455 		 */
2456 		if (pWidths)
2457 		{
2458 			int charLength =
2459 				UT_MIN(g_utf8_strlen(utf8.utf8_str() + pItem->offset, -1),
2460 					   pItem->num_chars);
2461 
2462 			xxx_UT_DEBUGMSG(("*** strlen %d, num-chars %d ***\n",
2463 						 g_utf8_strlen(utf8.utf8_str() + pItem->offset, -1),
2464 						 pItem->num_chars));
2465 
2466 			for (int j = 0; j < charLength; /*increment manually in loop*/)
2467 			{
2468 				UT_sint32 iStart = j;
2469 				UT_sint32 iEnd = j + 1;
2470 				UT_BidiCharType iDir = pItem->analysis.level % 2 ?
2471 					UT_BIDI_RTL : UT_BIDI_LTR;
2472 
2473 				UT_uint32 iMyWidth =
2474 					_measureExtent (pGstring, pf, iDir,
2475 									utf8.utf8_str()+pItem->offset,
2476 									pLogOffsets, iStart, iEnd);
2477 
2478 				if (iEnd == j + 1)
2479 				{
2480 					/* this should be the case most of the time */
2481 					pWidths[iOffset++] = iMyWidth;
2482 				}
2483 				else if (iEnd > j+1)
2484 				{
2485 					for (UT_uint32 k = iOffset;
2486 						 k < iOffset + (iEnd - (j + 1)) + 1;
2487 						 ++k)
2488 					{
2489 						pWidths[k] = iMyWidth / (iEnd - (j + 1) + 1);
2490 					}
2491 					iOffset += iEnd - (j + 1) + 1;
2492 				}
2493 				else
2494 				{
2495 					// iEnd < j+1 -- something badly wrong
2496 					UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
2497 					pWidths[iOffset++] = 0;
2498 					++j;
2499 					continue;
2500 				}
2501 
2502 				j = iEnd;
2503 			}
2504 		}
2505 
2506 		delete [] pLogOffsets;
2507 
2508 		l = l->next;
2509 	}
2510 
2511 	if (pWidths)
2512 	{
2513 		/* This is a bit weird, possibly a Pango bug, but it is better
2514 		 * to set any dangling widths to 0 than leave them at randomn values
2515 		 */
2516 		while (iOffset < (UT_uint32)iLength)
2517 		{
2518 			pWidths[iOffset++] = 0;
2519 		}
2520 	}
2521 
2522 	xxx_UT_DEBUGMSG(("Length %d, Offset %d\n", iLength, iOffset));
2523 
2524 cleanup:
2525 	if(pGstring)
2526 		pango_glyph_string_free(pGstring);
2527 
2528 	_pango_item_list_free(pItems);
2529 	if(pfs)
2530 	{
2531 		g_object_unref((GObject*)pfs);
2532 		pfs = NULL;
2533 	}
2534 	if (bClear_pf)
2535 	{
2536 		g_object_unref(pf);
2537 	}
2538 
2539 
2540 	return iWidth;
2541 }
2542 
2543 
2544 
2545 
2546 /*!
2547  * Draw the specified image at the location specified in local units
2548  * (xDest,yDest). xDest and yDest are in logical units.
2549  */
drawImage(GR_Image * pImg,UT_sint32 xDest,UT_sint32 yDest)2550 void GR_CairoGraphics::drawImage(GR_Image* pImg,
2551 									 UT_sint32 xDest, UT_sint32 yDest)
2552 {
2553 	if (m_cr == NULL)
2554 		return;
2555 	_setProps();
2556 	UT_ASSERT(pImg);
2557 
2558 	double idx = _tdudX(xDest);
2559 	double idy = _tdudY(yDest);
2560 
2561 	cairo_save(m_cr);
2562 	_resetClip();
2563 
2564 
2565 	if(!getAntiAliasAlways() && queryProperties(GR_Graphics::DGP_PAPER ))
2566 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
2567 	cairo_translate(m_cr, idx, idy);
2568 
2569 	if (pImg->getType() == GR_Image::GRT_Raster) {
2570 		static_cast<GR_CairoRasterImage*>(pImg)->cairoSetSource(m_cr);
2571 
2572 		cairo_pattern_t *pattern = cairo_get_source(m_cr);
2573 		cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
2574 		cairo_paint(m_cr);
2575 	} else if (pImg->getType() == GR_Image::GRT_Vector) {
2576 		/* for some obscure reason, using cairoSetSource() with an svg image
2577 			sometimes fails when printing, see 13533 */
2578 		static_cast<GR_CairoVectorImage*>(pImg)->renderToCairo(m_cr);
2579 	}
2580 
2581 	cairo_restore(m_cr);
2582 }
2583 
setFont(const GR_Font * pFont)2584 void GR_CairoGraphics::setFont(const GR_Font * pFont)
2585 {
2586 	UT_return_if_fail( pFont && pFont->getType() == GR_FONT_UNIX_PANGO);
2587 
2588 	//PangoFont * pf = (PangoFont*) pFont;
2589 	m_pPFont = const_cast<GR_PangoFont*>(static_cast<const GR_PangoFont*>(pFont));
2590 
2591 	_setIsSymbol(false);
2592 	_setIsDingbat(false);
2593 
2594 	const char* familyName = m_pPFont->getFamily();
2595 
2596 	char * szLCFontName = familyName ? g_utf8_strdown (familyName, -1) : NULL;
2597 
2598 	if (szLCFontName)
2599 	{
2600 		xxx_UT_DEBUGMSG(("GR_CairoGraphics::setFont: %s\n", szLCFontName));
2601 		if(strstr(szLCFontName,"symbol") != NULL)
2602 		{
2603 			/*
2604 			 * I am not too happy about this, and do not want to see the exception
2605 			 * list to grow much more, but cannot think of another simple solution.
2606 			 */
2607 			if(!strstr(szLCFontName,"starsymbol") &&
2608 			   !strstr(szLCFontName,"opensymbol") &&
2609 			   !strstr(szLCFontName,"symbolnerve"))
2610 				_setIsSymbol(true);
2611 		}
2612 
2613 		if(strstr(szLCFontName,"dingbat"))
2614 			_setIsDingbat(true);
2615 		FREEP(szLCFontName);
2616 	}
2617 
2618 	if(!m_pPFont->isGuiFont() && m_pPFont->getZoom() != getZoomPercentage())
2619 	{
2620 		m_pPFont->reloadFont(this);
2621 	}
2622 }
2623 
setZoomPercentage(UT_uint32 iZoom)2624 void GR_CairoGraphics::setZoomPercentage(UT_uint32 iZoom)
2625 {
2626 	// not sure if we should not call GR_UnixGraphics::setZoomPercentage() here
2627 	// instead
2628 	GR_Graphics::setZoomPercentage (iZoom); // chain up
2629 
2630 	if(m_pPFont && !m_pPFont->isGuiFont() && m_pPFont->getZoom() != iZoom)
2631 	{
2632 		m_pPFont->reloadFont(this);
2633 	}
2634 }
2635 
getDefaultFont(UT_String &,const char *)2636 GR_Font* GR_CairoGraphics::getDefaultFont(UT_String& /*fontFamily*/,
2637 											  const char * /*pLang*/)
2638 {
2639 	UT_return_val_if_fail( UT_NOT_IMPLEMENTED, NULL );
2640 }
2641 
getFontAscent()2642 UT_uint32 GR_CairoGraphics::getFontAscent()
2643 {
2644 	return getFontAscent(m_pPFont);
2645 }
2646 
getFontDescent()2647 UT_uint32 GR_CairoGraphics::getFontDescent()
2648 {
2649 	return getFontDescent(m_pPFont);
2650 }
2651 
getFontHeight()2652 UT_uint32 GR_CairoGraphics::getFontHeight()
2653 {
2654 	return getFontHeight(m_pPFont);
2655 }
2656 
getFontAscent(const GR_Font * pFont)2657 UT_uint32 GR_CairoGraphics::getFontAscent(const GR_Font * pFont)
2658 {
2659 	UT_return_val_if_fail( pFont, 0 );
2660 
2661 	const GR_PangoFont * pFP = static_cast<const GR_PangoFont*>(pFont);
2662 	return pFP->getAscent();
2663 }
2664 
getFontDescent(const GR_Font * pFont)2665 UT_uint32 GR_CairoGraphics::getFontDescent(const GR_Font *pFont)
2666 {
2667 	UT_return_val_if_fail( pFont, 0 );
2668 
2669 	const GR_PangoFont * pFP = static_cast<const GR_PangoFont*>(pFont);
2670 	return pFP->getDescent();
2671 }
2672 
getFontHeight(const GR_Font * pFont)2673 UT_uint32 GR_CairoGraphics::getFontHeight(const GR_Font *pFont)
2674 {
2675 	UT_return_val_if_fail( pFont, 0 );
2676 
2677 	const GR_PangoFont * pFP = static_cast<const GR_PangoFont*>(pFont);
2678 	return pFP->getAscent() + pFP->getDescent();
2679 }
2680 
2681 typedef struct
2682 {
2683   int value;
2684   const char str[16];
2685 } FieldMap;
2686 
2687 static const FieldMap style_map[] = {
2688   { PANGO_STYLE_NORMAL, "" },
2689   { PANGO_STYLE_OBLIQUE, "Oblique" },
2690   { PANGO_STYLE_ITALIC, "Italic" }
2691 };
2692 
2693 static const FieldMap variant_map[] = {
2694   { PANGO_VARIANT_NORMAL, "" },
2695   { PANGO_VARIANT_SMALL_CAPS, "Small-Caps" }
2696 };
2697 
2698 static const FieldMap weight_map[] = {
2699   { PANGO_WEIGHT_ULTRALIGHT, "Ultra-Light" },
2700   { PANGO_WEIGHT_ULTRALIGHT, "Ultralight" },
2701   { PANGO_WEIGHT_LIGHT, "Light" },
2702   { PANGO_WEIGHT_NORMAL, "" },
2703   { 500, "Medium" },
2704   { PANGO_WEIGHT_SEMIBOLD, "Semi-Bold" },
2705   { PANGO_WEIGHT_SEMIBOLD, "Semibold" },
2706   { PANGO_WEIGHT_BOLD, "Bold" },
2707   { PANGO_WEIGHT_ULTRABOLD, "Ultra-Bold" },
2708   { PANGO_WEIGHT_HEAVY, "Heavy" }
2709 };
2710 
2711 static const FieldMap stretch_map[] = {
2712   { PANGO_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" },
2713   { PANGO_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" },
2714   { PANGO_STRETCH_CONDENSED,       "Condensed" },
2715   { PANGO_STRETCH_SEMI_CONDENSED,  "Semi-Condensed" },
2716   { PANGO_STRETCH_NORMAL,          "" },
2717   { PANGO_STRETCH_SEMI_EXPANDED,   "Semi-Expanded" },
2718   { PANGO_STRETCH_EXPANDED,        "Expanded" },
2719   { PANGO_STRETCH_EXTRA_EXPANDED,  "Extra-Expanded" },
2720   { PANGO_STRETCH_ULTRA_EXPANDED,  "Ultra-Expanded" }
2721 };
2722 
find_field(const FieldMap * fma,size_t n,const char * elem)2723 static const FieldMap *find_field(const FieldMap *fma, size_t n, const char *elem)
2724 {
2725 	for (size_t i = 0; i < n; i++)
2726 		{
2727 			if (0 == g_ascii_strcasecmp(fma[i].str, elem))
2728 				{
2729 					return &(fma[i]);
2730 				}
2731 		}
2732 
2733 	return NULL;
2734 }
2735 
2736 /* Static 'virtual' function declared in gr_Graphics.h */
findNearestFont(const char * pszFontFamily,const char * pszFontStyle,const char * pszFontVariant,const char * pszFontWeight,const char * pszFontStretch,const char * pszFontSize,const char *)2737 const char* GR_Graphics::findNearestFont(const char* pszFontFamily,
2738 										 const char* pszFontStyle,
2739 										 const char* pszFontVariant,
2740 										 const char* pszFontWeight,
2741 										 const char* pszFontStretch,
2742 										 const char* pszFontSize,
2743 										 const char* /*pszFontLang*/)
2744 {
2745 	static UT_UTF8String s = pszFontFamily;
2746 
2747 	PangoFontDescription *d = pango_font_description_new();
2748 
2749 	if (d)
2750 	{
2751 		const FieldMap *fm;
2752 
2753 		pango_font_description_set_family(d, pszFontFamily);
2754 		pango_font_description_set_size(d, (int)((double)PANGO_SCALE * UT_convertToPoints(pszFontSize)));
2755 
2756 		if ((fm = find_field(style_map, G_N_ELEMENTS(style_map), pszFontStyle)) != 0)
2757 			{
2758 				pango_font_description_set_style(d, (PangoStyle)fm->value);
2759 			}
2760 
2761 		if ((fm = find_field(variant_map, G_N_ELEMENTS(variant_map), pszFontVariant)) != 0)
2762 			{
2763 				pango_font_description_set_variant(d, (PangoVariant)fm->value);
2764 			}
2765 
2766 		if ((fm = find_field(weight_map, G_N_ELEMENTS(weight_map), pszFontWeight)) != 0)
2767 			{
2768 				pango_font_description_set_weight(d, (PangoWeight)fm->value);
2769 			}
2770 
2771 		if ((fm = find_field(stretch_map, G_N_ELEMENTS(stretch_map), pszFontStretch)) != 0)
2772 			{
2773 				pango_font_description_set_stretch(d, (PangoStretch)fm->value);
2774 			}
2775 
2776 		PangoFontMap *fontmap = pango_cairo_font_map_get_default();
2777 		PangoContext *context = pango_font_map_create_context(PANGO_FONT_MAP(fontmap));
2778 		if (fontmap && context)
2779 			{
2780 				PangoFont *font = pango_font_map_load_font(fontmap, context, d);
2781 				if (font)
2782 					{
2783 						PangoFontDescription *new_desc = pango_font_describe(font);
2784 						s = pango_font_description_get_family(new_desc);
2785 						pango_font_description_free(new_desc);
2786 						g_object_unref(font);
2787 					}
2788 				g_object_unref(G_OBJECT (context)), context = NULL;
2789 			}
2790 
2791 		pango_font_description_free(d);
2792 	}
2793 
2794 	xxx_UT_DEBUGMSG(("@@@@ ===== Requested [%s], found [%s]\n",
2795 				 pszFontFamily, s.utf8_str()));
2796 
2797 	return s.utf8_str();
2798 }
2799 
2800 
_findFont(const char * pszFontFamily,const char * pszFontStyle,const char * pszFontVariant,const char * pszFontWeight,const char * pszFontStretch,const char * pszFontSize,const char * pszLang)2801 GR_Font* GR_CairoGraphics::_findFont(const char* pszFontFamily,
2802 										 const char* pszFontStyle,
2803 										 const char* pszFontVariant,
2804 										 const char* pszFontWeight,
2805 										 const char* pszFontStretch,
2806 										 const char* pszFontSize,
2807 										 const char* pszLang)
2808 {
2809 	double dPointSize = UT_convertToPoints(pszFontSize);
2810 	std::string s;
2811 
2812 	// Pango is picky about the string we pass to it -- it cannot handle any
2813 	// 'normal' values, and it will stop parsing when it encounters one.
2814 	const char * pStyle = pszFontStyle;
2815 	const char * pVariant = pszFontVariant;
2816 	const char * pWeight = pszFontWeight;
2817 	const char * pStretch = pszFontStretch;
2818 
2819 	if(pszFontStyle && *pszFontStyle == 'n')
2820 		pStyle = "";
2821 	else if(pszFontStyle == NULL)
2822 	        pStyle = "";
2823 
2824 	if(pszFontVariant && *pszFontVariant == 'n')
2825 		pVariant = "";
2826 	else if(pszFontVariant == NULL)
2827 	        pVariant = "";
2828 
2829 	if(pszFontWeight && *pszFontWeight == 'n')
2830 		pWeight = "";
2831 	else if(pszFontWeight == NULL)
2832 	        pWeight = "";
2833 
2834 	if(pszFontStretch && *pszFontStretch == 'n')
2835 		pStretch = "";
2836 	else if(pszFontStretch == NULL)
2837 	        pStretch = "";
2838 
2839 	if(!pszLang || !*pszLang)
2840 		pszLang = "en-US";
2841 
2842 	s = UT_std_string_sprintf("%s, %s %s %s %s",
2843 					  pszFontFamily,
2844 					  pStyle,
2845 					  pVariant,
2846 					  pWeight,
2847 					  pStretch);
2848 
2849 	return new GR_PangoFont(s.c_str(), dPointSize, this, pszLang);
2850 }
2851 
2852 /*!
2853  *  This is a very ugly hack, but Pango gives me no public API to find out the
2854  *  upper extent of the font coverage, so I either have to do this, or have to
2855  *  iterate over the entire UCS-4 space (which is a non-starter)
2856  *
2857  *  The struct definition below must match _PangoCoverage !!!
2858  *
2859  *  each block represents 256 characters, so the maximum possible character
2860  *  value is n_blocks * 256;
2861  */
2862 
2863 struct _MyPangoCoverage
2864 {
2865   guint ref_count;
2866   int n_blocks;
2867   int data_size;
2868 
2869   void *blocks;
2870 };
2871 
2872 typedef _MyPangoCoverage MyPangoCoverage;
2873 
getCoverage(UT_NumberVector & coverage)2874 void GR_CairoGraphics::getCoverage(UT_NumberVector& coverage)
2875 {
2876 	coverage.clear();
2877 
2878 	UT_return_if_fail(m_pPFont);
2879 
2880 	PangoCoverage * pc = m_pPFont->getPangoCoverage();
2881 
2882 	if(!pc)
2883 		return;
2884 
2885 	MyPangoCoverage * mpc = (MyPangoCoverage*) pc;
2886 	UT_uint32 iMaxChar = mpc->n_blocks * 256;
2887 
2888 	xxx_UT_DEBUGMSG(("GR_CairoGraphics::getCoverage: iMaxChar %d\n", iMaxChar));
2889 
2890 	bool bInRange = false;
2891 	UT_uint32 iRangeStart = 0;
2892 
2893 	// Skip the coverage for character 0 as pango doesn't seem to be able to
2894 	// handle it properly.
2895 	// Note that for almost all fonts pango reports that it has no coverage for
2896 	// character 0, so this is a non-issue there. However, for some (broken?) fonts
2897 	// like 'Fixedsys Excelsior 2.00' pango reports it *has* coverage for character 0.
2898 	// This will lead to crashes when attempting to shape and/or draw it, like
2899 	// the crash in bug 11731 - MARCM
2900 	for(UT_uint32 i = 1; i < iMaxChar; ++i)
2901 	{
2902 		PangoCoverageLevel pl = pango_coverage_get(pc, i);
2903 
2904 		if(PANGO_COVERAGE_NONE == pl || PANGO_COVERAGE_FALLBACK == pl)
2905 		{
2906 			if(bInRange)
2907 			{
2908 				// according to the code in XAP_UnixFont::getCoverage(), the
2909 				// range is of type <x,y)
2910 				coverage.push_back(i - iRangeStart);
2911 				bInRange = false;
2912 			}
2913 		}
2914 		else
2915 		{
2916 			if(!bInRange)
2917 			{
2918 				coverage.push_back(i);
2919 				iRangeStart = i;
2920 				bInRange = true;
2921 			}
2922 		}
2923 	}
2924 }
2925 
getAllFontNames(void)2926 const std::vector<std::string> & GR_CairoGraphics::getAllFontNames(void)
2927 {
2928 	XAP_Prefs * pPrefs = XAP_App::getApp()->getPrefs();
2929 	bool bExclude = false;
2930 	bool bInclude = false;
2931 
2932 	/*
2933 	 * Do this only once
2934 	 */
2935 	static std::vector<std::string> Vec;
2936 
2937 	if (!Vec.empty())
2938 		return Vec;
2939 
2940 	if (pPrefs)
2941 	{
2942 		XAP_FontSettings & Fonts = pPrefs->getFontSettings();
2943 		bExclude = Fonts.haveFontsToExclude();
2944 		bInclude = Fonts.haveFontsToInclude();
2945 
2946 		if (bInclude)
2947 		{
2948 			for (UT_uint32 k = 0; k < Fonts.getFonts().size(); ++k)
2949 				Vec.push_back (Fonts.getFonts()[k].utf8_str());
2950 
2951 			return Vec;
2952 		}
2953 	}
2954 
2955 
2956 	UT_DEBUGMSG(("@@@@ ===== Loading system fonts =====\n"));
2957 	PangoFontMap *fontmap = pango_cairo_font_map_get_default();
2958 	PangoContext *context = pango_font_map_create_context(PANGO_FONT_MAP(fontmap));
2959 	if (fontmap && context)
2960 	{
2961 		PangoFontFamily **font_families;
2962 		int n_families;
2963 		pango_font_map_list_families(fontmap, &font_families, &n_families);
2964 
2965 		UT_DEBUGMSG(("@@@@ ===== Loading system fonts n_families: %d =====\n", n_families));
2966 		for(UT_sint32 i = 0; i < n_families; ++i)
2967 		{
2968 			const char *family = pango_font_family_get_name(font_families[i]);
2969 
2970 			if (bExclude)
2971 			{
2972 				XAP_FontSettings & Fonts = pPrefs->getFontSettings();
2973 				if (Fonts.isOnExcludeList(family))
2974 				{
2975 					UT_DEBUGMSG(("@@@@ ===== Excluding font [%s] =====\n",
2976 								 family));
2977 					continue;
2978 				}
2979 			}
2980 
2981 			PangoFontFace ** faces;
2982 			int n_faces;
2983 			bool is_scalable = true;
2984 			pango_font_family_list_faces(font_families[i], &faces, &n_faces);
2985 			for(int j = 0; j < n_faces; j++)
2986 			{
2987 				int * sizes = NULL;
2988 				int n_sizes = 0;
2989 				pango_font_face_list_sizes(faces[j], &sizes, &n_sizes);
2990 				if(sizes)
2991 				{
2992 					g_free(sizes);
2993 					is_scalable = false;
2994 					UT_DEBUGMSG(("@@@@ ===== Excluding NON scalable font [%s] =====\n",
2995 								 family));
2996 					break;
2997 				}
2998 			}
2999 			g_free(faces);
3000 			if(is_scalable)
3001 			{
3002 				Vec.push_back(family);
3003 			}
3004 		}
3005 		g_free(font_families);
3006 	}
3007 	if(context)
3008 	{
3009 		g_object_unref (G_OBJECT (context));
3010 		context = NULL;
3011 	}
3012     std::sort(Vec.begin(), Vec.end());
3013 
3014 	return Vec;
3015 }
3016 
getAllFontCount()3017 UT_uint32 GR_CairoGraphics::getAllFontCount()
3018 {
3019 	return getAllFontNames().size();
3020 }
3021 
getDefaultFont(GR_Font::FontFamilyEnum f,const char * pszLang)3022 GR_Font * GR_CairoGraphics::getDefaultFont(GR_Font::FontFamilyEnum f,
3023 											   const char * pszLang)
3024 {
3025 	const char* pszFontFamily = NULL;
3026 	const char* pszFontStyle = "normal";
3027 	const char* pszFontVariant = "normal";
3028 	const char* pszFontWeight = "normal";
3029 	const char* pszFontStretch = "normal";
3030 	const char* pszFontSize = "12pt";
3031 
3032 	if(!pszLang)
3033 		pszLang = "en-US";
3034 
3035 	switch (f)
3036 	{
3037 		case GR_Font::FF_Roman:
3038 			pszFontFamily = "Times";
3039 			break;
3040 
3041 		case GR_Font::FF_Swiss:
3042 			pszFontFamily = "Helvetica";
3043 			break;
3044 
3045 		case GR_Font::FF_Modern:
3046 			pszFontFamily = "Courier";
3047 			break;
3048 
3049 		case GR_Font::FF_Script:
3050 			pszFontFamily = "Cursive";
3051 			break;
3052 
3053 		case GR_Font::FF_Decorative:
3054 			pszFontFamily = "Old English";
3055 			break;
3056 
3057 		case GR_Font::FF_Technical:
3058 		case GR_Font::FF_BiDi:
3059 			pszFontFamily = "Arial";
3060 			break;
3061 
3062 		default:
3063 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3064 	}
3065 
3066 	return findFont(pszFontFamily,
3067 					pszFontStyle,
3068 					pszFontVariant,
3069 					pszFontWeight,
3070 					pszFontStretch,
3071 					pszFontSize,
3072 					pszLang);
3073 }
3074 
getColor(UT_RGBColor & clr)3075 void GR_CairoGraphics::getColor(UT_RGBColor& clr)
3076 {
3077 	clr = m_curColor;
3078 }
3079 
setColor(const UT_RGBColor & clr)3080 void GR_CairoGraphics::setColor(const UT_RGBColor& clr)
3081 {
3082 	m_curColor = clr;
3083 	m_curColorDirty = true;
3084 }
3085 
drawLine(UT_sint32 x1,UT_sint32 y1,UT_sint32 x2,UT_sint32 y2)3086 void GR_CairoGraphics::drawLine(UT_sint32 x1, UT_sint32 y1,
3087 							   UT_sint32 x2, UT_sint32 y2)
3088 {
3089 	if (m_cr == NULL)
3090 		return;
3091 	_setProps();
3092 
3093 	UT_sint32 idx1 = _tduX(x1);
3094 	UT_sint32 idx2 = _tduX(x2);
3095 
3096 	UT_sint32 idy1 = _tduY(y1);
3097 	UT_sint32 idy2 = _tduY(y2);
3098 
3099 	cairo_save(m_cr);
3100 	if(!getAntiAliasAlways())
3101 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3102 	cairo_move_to (m_cr,idx1, idy1);
3103 	cairo_line_to (m_cr,idx2, idy2);
3104 	cairo_stroke (m_cr);
3105 	cairo_restore(m_cr);
3106 }
3107 
setLineWidth(UT_sint32 iLineWidth)3108 void GR_CairoGraphics::setLineWidth(UT_sint32 iLineWidth)
3109 {
3110 	m_lineWidth = iLineWidth;
3111 	m_linePropsDirty = true;
3112 }
3113 
3114 
setLineProperties(double inWidth,GR_Graphics::JoinStyle inJoinStyle,GR_Graphics::CapStyle inCapStyle,GR_Graphics::LineStyle inLineStyle)3115 void GR_CairoGraphics::setLineProperties ( double inWidth,
3116 										  GR_Graphics::JoinStyle inJoinStyle,
3117 										  GR_Graphics::CapStyle inCapStyle,
3118 										  GR_Graphics::LineStyle inLineStyle )
3119 {
3120 	m_lineWidth = inWidth;
3121 	m_joinStyle = inJoinStyle;
3122 	m_capStyle = inCapStyle;
3123 	m_lineStyle = inLineStyle;
3124 	m_linePropsDirty = true;
3125 }
3126 
3127 /*
3128  * This method looks the way it does because the Cairo XOR does not behave like
3129  * the bitwise xor used in the other graphics classes. We use this method
3130  * to draw temperary lines  across the page when dragging ruler controls.
3131  * The hack below preserves this behaviour.
3132  */
xorLine(UT_sint32 x1,UT_sint32 y1,UT_sint32 x2,UT_sint32 y2)3133 void GR_CairoGraphics::xorLine(UT_sint32 x1, UT_sint32 y1, UT_sint32 x2,
3134 							  UT_sint32 y2)
3135 {
3136 	if (m_cr == NULL)
3137 		return;
3138 	_setProps();
3139 
3140 	UT_sint32 idx1 = _tduX(x1);
3141 	UT_sint32 idx2 = _tduX(x2);
3142 
3143 	UT_sint32 idy1 = _tduY(y1);
3144 	UT_sint32 idy2 = _tduY(y2);
3145 	if((idx1 == m_iPrevX1) && (idx2 == m_iPrevX2) && (idy1 == m_iPrevY1) && (idy2 == m_iPrevY2) && (m_iXORCount == 1))
3146 	{
3147 		//
3148 		// We've XOR'd a previously written line, restore the content from
3149 		// m_iPrevRect
3150 		//
3151 		m_iXORCount = 0;
3152 		restoreRectangle(m_iPrevRect);
3153 
3154 	}
3155 	else
3156 	{
3157 		//
3158 		// Save the contents of the screen before drawing a line across it.
3159 		//
3160 		m_iPrevX1 = idx1;
3161 		m_iPrevX2 = idx2;
3162 		m_iPrevY1 = idy1;
3163 		m_iPrevY2 = idy2;
3164 		m_iXORCount = 1;
3165 		UT_Rect r;
3166 		UT_sint32 swap = 0;
3167 		if(idx1 > idx2)
3168 		{
3169 			swap = idx1;
3170 			idx1 = idx2;
3171 			idx2 = swap;
3172 		}
3173 		if(idy1 > idy2)
3174 		{
3175 			swap = idy1;
3176 			idy1 = idy2;
3177 			idy2 = swap;
3178 		}
3179 		r.left = tlu(idx1);
3180 		r.top = tlu(idy1);
3181 		r.width = tlu(idx2 - idx1 +2);
3182 		r.height = tlu(idy2 - idy1+2);
3183 		saveRectangle(r,m_iPrevRect);
3184 
3185 		cairo_save(m_cr);
3186 		if(!getAntiAliasAlways())
3187 			cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3188 		cairo_set_source_rgb(m_cr, 0.0 , 0.0 , 0.0);
3189 
3190 		cairo_move_to(m_cr, idx1, idy1);
3191 		cairo_line_to(m_cr, idx2, idy2);
3192 		cairo_stroke(m_cr);
3193 		cairo_restore(m_cr);
3194 	}
3195 }
3196 
polyLine(UT_Point * pts,UT_uint32 nPoints)3197 void GR_CairoGraphics::polyLine(UT_Point * pts, UT_uint32 nPoints)
3198 {
3199 	if (m_cr == NULL)
3200 		return;
3201 	_setProps();
3202 
3203 	UT_uint32 i;
3204 
3205 	UT_return_if_fail(nPoints > 1);
3206 
3207 	cairo_save(m_cr);
3208 	if(!getAntiAliasAlways())
3209 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3210 
3211 	i = 0;
3212 	cairo_move_to(m_cr, _tdudX(pts[i].x), _tdudY(pts[i].y));
3213 	i++;
3214 	for (; i < nPoints; i++)
3215 	{
3216 		cairo_line_to(m_cr, _tdudX(pts[i].x), _tdudY(pts[i].y));
3217 	}
3218 	cairo_stroke(m_cr);
3219 	cairo_restore(m_cr);
3220 }
3221 
invertRect(const UT_Rect *)3222 void GR_CairoGraphics::invertRect(const UT_Rect* /* pRect */)
3223 {
3224 /* TODO Rob
3225 	UT_ASSERT(pRect);
3226 
3227 	UT_sint32 idy = _tduY(pRect->top);
3228 	UT_sint32 idx = _tduX(pRect->left);
3229 	UT_sint32 idw = _tduR(pRect->width);
3230 	UT_sint32 idh = _tduR(pRect->height);
3231 */
3232 	UT_ASSERT_NOT_REACHED ();
3233 }
3234 /**
3235  * This appears to fix off-by-1 bugs in setting rectangles and drawing text
3236  */
_tdudX(UT_sint32 layoutUnits) const3237 double GR_CairoGraphics::_tdudX(UT_sint32 layoutUnits) const
3238 {
3239 	return _tduX(layoutUnits) -0.5;
3240 }
3241 
3242 /**
3243  * This appears to fix off-by-1 bugs in setting rectangles and drawing text
3244  */
_tdudY(UT_sint32 layoutUnits) const3245 double GR_CairoGraphics::_tdudY(UT_sint32 layoutUnits) const
3246 {
3247 	return _tduY(layoutUnits) -0.5;
3248 }
3249 
setClipRect(const UT_Rect * pRect)3250 void GR_CairoGraphics::setClipRect(const UT_Rect* pRect)
3251 {
3252 	if (pRect) {
3253 		m_pRect.reset(new UT_Rect(*pRect));
3254 	} else {
3255 		m_pRect.reset();
3256 	}
3257 	m_clipRectDirty = true;
3258 }
3259 
fillRect(const UT_RGBColor & c,UT_sint32 x,UT_sint32 y,UT_sint32 w,UT_sint32 h)3260 void GR_CairoGraphics::fillRect(const UT_RGBColor& c, UT_sint32 x, UT_sint32 y,
3261 							   UT_sint32 w, UT_sint32 h)
3262 {
3263 	if (m_cr == NULL)
3264 		return;
3265 	_setProps();
3266 
3267 	cairo_save(m_cr);
3268 	if(!getAntiAliasAlways())
3269 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3270 
3271 	_setSource(m_cr, c);
3272 	cairo_rectangle(m_cr, _tdudX(x), _tdudY(y), _tduR(w), _tduR(h));
3273 	cairo_fill(m_cr);
3274 
3275 	cairo_restore(m_cr);
3276 }
3277 
3278 /*!
3279     Convert device units to pango units
3280 */
dtpu(int d) const3281 inline int GR_CairoGraphics::dtpu(int d) const
3282 {
3283 	return d * PANGO_SCALE;
3284 }
3285 
3286 /*!
3287     Convert pango units to device units
3288 */
ptdu(int p) const3289 inline int GR_CairoGraphics::ptdu(int p) const
3290 {
3291 	return PANGO_PIXELS(p);
3292 }
3293 
3294 /*!
3295     Convert pango units to layout units
3296 */
ptlu(int p) const3297 inline int GR_CairoGraphics::ptlu(int p) const
3298 {
3299 	double d = (double)p * (double) getResolution() * 100.0 /
3300 		((double)getDeviceResolution()*(double)getZoomPercentage()*(double) PANGO_SCALE) + .5;
3301 
3302 	return (int) d;
3303 }
3304 
3305 
3306 /*!
3307     Convert pango units to layout units without zoom
3308 */
ptlunz(int p) const3309 inline int GR_CairoGraphics::ptlunz(int p) const
3310 {
3311 	double d = ((double)p / ((double) PANGO_SCALE)) + .5; //getDeviceResolution
3312 
3313 	return (int) d;
3314 }
3315 
3316 /*!
3317     Convert layout units to pango units
3318 */
ltpu(int l) const3319 inline int GR_CairoGraphics::ltpu(int l) const
3320 {
3321 	double d = (double)l *
3322 		(double)getDeviceResolution() * (double)PANGO_SCALE * (double)getZoomPercentage()/
3323 		(100.0 * (double) getResolution()) + .5;
3324 
3325 	return (int) d;
3326 }
3327 
3328 
3329 /*!
3330     Convert layout units to pango units without zoom
3331 */
ltpunz(int l) const3332 inline int GR_CairoGraphics::ltpunz(int l) const
3333 {
3334 	double d = (double)l * PANGO_SCALE  + .5; //getDeviceResolution()
3335 
3336 	return (int) d;
3337 }
3338 
3339 
3340 /*!
3341     Convert pango font units to layout units
3342 
3343     (Pango font units == point size * PANGO_SCALE, hence at zoom of 100% there
3344     are 20/PANGO_SCALE layout units to each pango font unit.)
3345 */
pftlu(int pf) const3346 inline int GR_CairoGraphics::pftlu(int pf) const
3347 {
3348 	double d = (double)pf * 2000.0 / ((double)getZoomPercentage() * (double)PANGO_SCALE);
3349 	return (int) d;
3350 }
3351 
fillRect(GR_Color3D c,UT_Rect & r)3352 void GR_CairoGraphics::fillRect(GR_Color3D c, UT_Rect &r)
3353 {
3354 	UT_ASSERT(m_bHave3DColors && c < COUNT_3D_COLORS);
3355 
3356 	fillRect(c,r.left,r.top,r.width,r.height);
3357 }
3358 
3359 /*!
3360  * Rob sez: the original (before cairo) implementation did not restore colours after drawing.
3361  * We're trying to do the right thing here instead.
3362  *
3363  * On Gtk we have an override for this since Gtk Theming broke during 3.x
3364  */
fillRect(GR_Color3D c,UT_sint32 x,UT_sint32 y,UT_sint32 w,UT_sint32 h)3365 void GR_CairoGraphics::fillRect(GR_Color3D c, UT_sint32 x, UT_sint32 y, UT_sint32 w, UT_sint32 h)
3366 {
3367 	if (m_cr == NULL)
3368 		return;
3369 	_setProps();
3370 //	UT_ASSERT(m_bHave3DColors && c < COUNT_3D_COLORS);
3371 
3372 	cairo_save (m_cr);
3373 
3374 	if(!getAntiAliasAlways())
3375 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3376 
3377 	_setSource(m_cr, m_3dColors[c]);
3378 	cairo_rectangle(m_cr, tdu(x), tdu(y), tdu(w), tdu(h));
3379 	cairo_fill(m_cr);
3380 
3381 	cairo_restore (m_cr);
3382 }
3383 
3384 /*!
3385  * \todo Rob find out how to have this function used, and test.
3386  */
polygon(UT_RGBColor & c,UT_Point * pts,UT_uint32 nPoints)3387 void GR_CairoGraphics::polygon(UT_RGBColor& c, UT_Point *pts,
3388 								   UT_uint32 nPoints)
3389 {
3390 	if (m_cr == NULL)
3391 		return;
3392 	_setProps();
3393 	UT_uint32 i;
3394 
3395 	UT_return_if_fail(nPoints > 1);
3396 	cairo_save(m_cr);
3397 	if(!getAntiAliasAlways())
3398 		cairo_set_antialias(m_cr,CAIRO_ANTIALIAS_NONE);
3399 
3400 	i = 0;
3401 	cairo_move_to(m_cr, _tdudX(pts[i].x), _tdudY(pts[i].y));
3402 	i++;
3403 	for (; i < nPoints; i++) {
3404 		cairo_line_to(m_cr, _tdudX(pts[i].x), _tdudY(pts[i].y));
3405 	}
3406 	_setSource(m_cr, c);
3407 	cairo_fill(m_cr);
3408 
3409 	cairo_restore(m_cr);
3410 }
3411 
saveRectangle(UT_Rect & r,UT_uint32 iIndex)3412 void GR_CairoGraphics::saveRectangle(UT_Rect &r, UT_uint32 iIndex)
3413 {
3414 	if(iIndex >= m_vSaveRect.size())
3415 		m_vSaveRect.resize(iIndex + 1, NULL);
3416 	if(iIndex >= m_vSaveRectBuf.size())
3417 		m_vSaveRectBuf.resize(iIndex + 1, NULL);
3418 
3419 	delete m_vSaveRect[iIndex];
3420 	m_vSaveRect[iIndex] = new UT_Rect(r);
3421 
3422 	cairo_save(m_cr);
3423 	cairo_reset_clip(m_cr);
3424 
3425 	cairo_rectangle_t cacheRect;
3426 	cacheRect.x = -static_cast<double>(_tduX(r.left));
3427 	cacheRect.y = -static_cast<double>(_tduY(r.top ));
3428 	cacheRect.width  = static_cast<double>(_tduR(r.width ));
3429 	cacheRect.height = static_cast<double>(_tduR(r.height));
3430 
3431 	cairo_surface_flush(cairo_get_target(m_cr));
3432 	cairo_surface_t* newC = _getCairoSurfaceFromContext(m_cr, cacheRect);
3433 
3434 	cairo_surface_destroy(m_vSaveRectBuf[iIndex]);
3435 	m_vSaveRectBuf[iIndex] = newC;
3436 
3437 	cairo_restore(m_cr);
3438 }
3439 
restoreRectangle(UT_uint32 iIndex)3440 void GR_CairoGraphics::restoreRectangle(UT_uint32 iIndex)
3441 {
3442 	cairo_save(m_cr);
3443 	cairo_reset_clip(m_cr);
3444 	UT_Rect *r = m_vSaveRect[iIndex];
3445 	cairo_surface_t *s = m_vSaveRectBuf[iIndex];
3446 	double idx = static_cast<double>(_tduX(r->left)) - 0.5;
3447 	double idy = static_cast<double>(_tduY(r->top)) - 0.5;
3448 	cairo_surface_flush(cairo_get_target(m_cr));
3449 	if(s && r)
3450 	{
3451 		cairo_set_source_surface(m_cr, s, idx, idy);
3452 		cairo_paint(m_cr);
3453 	}
3454 	cairo_restore(m_cr);
3455 }
3456 
clearArea(UT_sint32 x,UT_sint32 y,UT_sint32 width,UT_sint32 height)3457 void GR_CairoGraphics::clearArea(UT_sint32 x, UT_sint32 y,
3458 									 UT_sint32 width, UT_sint32 height)
3459 {
3460 	if (width > 0)
3461 	{
3462 		static const UT_RGBColor clrWhite(255,255,255);
3463 		fillRect(clrWhite, x, y, width, height);
3464 	}
3465 }
3466 
getCairo()3467 cairo_t *GR_CairoGraphics::getCairo()
3468 {
3469 	if (m_paintCount <= 0)
3470 	{
3471 		UT_DEBUGMSG(("GR_CairoGraphics::getCairo() called outside beginPaint/endPaint!\n"));
3472 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3473 		UT_DEBUGMSG(("GR_CairoGraphics::getCairo: calling beginPaint() for you, expect strange side-effects.\n"));
3474 		beginPaint();
3475 	}
3476 	UT_ASSERT(m_cr);
3477 	return m_cr;
3478 }
3479 
setCairo(cairo_t * cr)3480 void GR_CairoGraphics::setCairo(cairo_t *cr)
3481 {
3482 	m_cr = cr;
3483 }
3484 
_DeviceContext_SwitchToBuffer()3485 void GR_CairoGraphics::_DeviceContext_SwitchToBuffer()
3486 {
3487 	cairo_push_group(m_cr);
3488 }
3489 
_DeviceContext_SwitchToScreen()3490 void GR_CairoGraphics::_DeviceContext_SwitchToScreen()
3491 {
3492 	cairo_pop_group_to_source(m_cr);
3493 	cairo_paint(m_cr);
3494 }
3495 
_DeviceContext_SuspendDrawing()3496 void GR_CairoGraphics::_DeviceContext_SuspendDrawing()
3497 {
3498 	cairo_push_group(m_cr);
3499 }
3500 
_DeviceContext_ResumeDrawing()3501 void GR_CairoGraphics::_DeviceContext_ResumeDrawing()
3502 {
3503 	cairo_pattern_destroy (cairo_pop_group(m_cr));
3504 }
3505 
3506 ////////////////////////////////////////////////////////////////////////////////////////////////////
3507 //
3508 // GR_UnixPangFont implementation
3509 //
GR_PangoFont(const char * pDesc,double dSize,GR_CairoGraphics * pG,const char * pLang,bool bGuiFont)3510 GR_PangoFont::GR_PangoFont(const char * pDesc, double dSize,
3511 								   GR_CairoGraphics * pG,
3512 								   const char * pLang,
3513 								   bool bGuiFont):
3514 	m_dPointSize(dSize),
3515 	m_iZoom(0), // forces creation of font by reloadFont()
3516 	m_pf(NULL),
3517 	m_bGuiFont(bGuiFont),
3518 	m_pCover(NULL),
3519 	m_pfdDev(NULL),
3520 	m_pfdLay(NULL),
3521 	m_pPLang(NULL),
3522 	m_iAscent(0),
3523 	m_iDescent(0),
3524 	m_pLayoutF(NULL)
3525 {
3526 	m_eType = GR_FONT_UNIX_PANGO;
3527 	UT_return_if_fail( pDesc && pG && pLang);
3528 
3529 	m_sLayoutDesc = pDesc;
3530 	m_sDesc = pDesc;
3531 	setLanguage(pLang);
3532 	reloadFont(pG);
3533 	UT_DEBUGMSG(("Created UnixPangOFont %p \n",this));
3534 }
3535 
~GR_PangoFont()3536 GR_PangoFont::~GR_PangoFont()
3537 {
3538 	if(m_pCover)
3539 		pango_coverage_unref(m_pCover);
3540 	if (m_pf)
3541 	{
3542 		g_object_unref(m_pf);
3543 	}
3544 	if (m_pLayoutF)
3545 	{
3546 		g_object_unref(m_pLayoutF);
3547 	}
3548 	pango_font_description_free(m_pfdDev);
3549 	pango_font_description_free(m_pfdLay);
3550 }
3551 
setLanguage(const char * pLang)3552 void GR_PangoFont::setLanguage(const char * pLang)
3553 {
3554 	UT_return_if_fail( pLang );
3555 
3556 	m_pPLang = pango_language_from_string(pLang);
3557 }
3558 
3559 /*!
3560     Reloads the Pango font associated with this font, taking into account the
3561     current level of zoom
3562 */
reloadFont(GR_CairoGraphics * pG)3563 void GR_PangoFont::reloadFont(GR_CairoGraphics * pG)
3564 {
3565 	UT_return_if_fail( pG );
3566 
3567 	UT_uint32 iZoom = pG->getZoomPercentage();
3568 	if(m_pf && (m_bGuiFont || m_iZoom == iZoom))
3569 		return;
3570 
3571 	m_iZoom = iZoom;
3572 
3573 	UT_DEBUGMSG(("GR_PangoFont::reloadFont() zoom %% %d\n", iZoom));
3574 
3575 	UT_LocaleTransactor t(LC_NUMERIC, "C");
3576  	std::string sLay;
3577  	std::string sDev;
3578 	if(!m_bGuiFont && pG->queryProperties(GR_Graphics::DGP_SCREEN))
3579  	{
3580  		sDev = UT_std_string_sprintf("%s %f", m_sDesc.c_str(), m_dPointSize * (double)m_iZoom / 100.0);
3581  		sLay = UT_std_string_sprintf("%s %f", m_sLayoutDesc.c_str(), m_dPointSize);
3582  	}
3583 	else
3584  	{
3585  		sDev = UT_std_string_sprintf("%s %f", m_sDesc.c_str(), m_dPointSize);
3586  		sLay = UT_std_string_sprintf("%s %f", m_sLayoutDesc.c_str(), m_dPointSize);
3587  	}
3588 
3589  	if(m_pfdLay)
3590   	{
3591  		pango_font_description_free(m_pfdLay);
3592  		m_pfdLay = NULL;
3593   	}
3594 
3595 
3596  	if(m_pfdDev)
3597  	{
3598  		pango_font_description_free(m_pfdDev);
3599  		m_pfdDev = NULL;
3600  	}
3601 
3602  	m_pfdLay = pango_font_description_from_string(sLay.c_str());
3603  	UT_return_if_fail(m_pfdLay);
3604 
3605  	m_pfdDev = pango_font_description_from_string(sDev.c_str());
3606  	UT_return_if_fail(m_pfdDev);
3607 
3608 	if (m_pf) {
3609 		g_object_unref(m_pf);
3610 	}
3611 	m_pf = pango_context_load_font(pG->getContext(), m_pfdDev);
3612 	if(m_pLayoutF) {
3613 		g_object_unref(m_pLayoutF);
3614 	}
3615 	m_pLayoutF = pango_context_load_font(pG->getLayoutContext(), m_pfdLay);
3616 
3617 	UT_return_if_fail( m_pf );
3618  	UT_return_if_fail( m_pLayoutF );
3619 	// FIXME: we probably want the real language from somewhere
3620  	PangoFontMetrics * pfm = pango_font_get_metrics(m_pLayoutF, m_pPLang);
3621 	UT_return_if_fail( pfm);
3622 
3623 	// pango_metrics_ functions return in points * PANGO_SCALE (points * 1024)
3624  	m_iAscent = (UT_uint32) pango_font_metrics_get_ascent(pfm)/PANGO_SCALE;
3625  	m_iDescent = (UT_uint32) pango_font_metrics_get_descent(pfm)/PANGO_SCALE;
3626 	UT_DEBUGMSG(("metrics asc %d desc %d\n", m_iAscent, m_iDescent));
3627 
3628  	xxx_UT_DEBUGMSG(("Layout Font Ascent %d point size %f zoom %d \n",m_iAscent, m_dPointSize, m_iZoom));
3629 	pango_font_metrics_unref(pfm);
3630 
3631 	UT_return_if_fail( pfm);
3632 }
3633 
3634 
3635 /*!
3636 	Measure the unremapped char to be put into the cache.
3637 	That means measuring it for a font size of 120
3638 */
measureUnremappedCharForCache(UT_UCS4Char) const3639 UT_sint32 GR_PangoFont::measureUnremappedCharForCache(UT_UCS4Char /*cChar*/) const
3640 {
3641 	// this is not implemented because we do not use the width cache (when
3642 	// shaping, it is not possible to measure characters, only glyphs)
3643 	UT_ASSERT_HARMLESS( UT_NOT_IMPLEMENTED );
3644 	return 0;
3645 }
3646 
getPangoCoverage() const3647 PangoCoverage * GR_PangoFont::getPangoCoverage() const
3648 {
3649 	if(!m_pCover)
3650 	{
3651 		m_pCover = pango_font_get_coverage(m_pf, m_pPLang);
3652 		UT_return_val_if_fail(m_pCover, NULL);
3653 	}
3654 
3655 	return m_pCover;
3656 }
3657 
3658 
3659 /*!
3660     Determine if character g exists in this font.  We assume here that coverage
3661     is not affected by font size -- since we only operate with single fonts and
3662     assume scalable fonts, this should be OK.
3663 
3664     NB: it is essential that this function is fast
3665 */
doesGlyphExist(UT_UCS4Char g) const3666 bool GR_PangoFont::doesGlyphExist(UT_UCS4Char g) const
3667 {
3668 	UT_return_val_if_fail( m_pf, false );
3669 
3670 	PangoCoverage * pc = getPangoCoverage();
3671 	UT_return_val_if_fail(pc, false);
3672 
3673 	PangoCoverageLevel eLevel = pango_coverage_get(pc, g);
3674 
3675 	if(PANGO_COVERAGE_NONE == eLevel || PANGO_COVERAGE_FALLBACK == eLevel)
3676 		return false;
3677 
3678 	return true;
3679 }
3680 
fontPoints2float(double dSize,UT_sint32 iFontPoints)3681 static double fontPoints2float(double dSize, UT_sint32 iFontPoints)
3682 {
3683 	return dSize * ((double)iFontPoints / PANGO_SCALE) * 1.44/20.; // Last 20 is points to inches
3684 }
3685 
getGlyphForChar(UT_UCS4Char g,PangoFont * pf,PangoContext * context)3686 static PangoGlyph getGlyphForChar(UT_UCS4Char g,
3687 								  PangoFont *pf,
3688 								  PangoContext *context)
3689 {
3690 	UT_UTF8String utf8(&g, 1);
3691 
3692 	// this function expect indexes in bytes !!! (stupid)
3693 	GList * pItems = pango_itemize(context,
3694 								   utf8.utf8_str(),
3695 								   0, utf8.byteLength(),
3696 								   NULL, NULL);
3697 
3698 	int iItemCount = g_list_length(pItems);
3699 	PangoGlyphString * pGstring = pango_glyph_string_new();
3700 
3701 	for(int i = 0; i < iItemCount; ++i)
3702 	{
3703 		PangoItem *pItem = (PangoItem *)g_list_nth(pItems, i)->data;
3704 
3705 		if(!pItem)
3706 		{
3707 			UT_ASSERT(pItem);
3708 			if(pGstring)
3709 				pango_glyph_string_free(pGstring);
3710 			_pango_item_list_free(pItems);
3711 			return PANGO_GLYPH_EMPTY;
3712 		}
3713 
3714 		g_object_unref(pItem->analysis.font);
3715 		pItem->analysis.font = (PangoFont*)g_object_ref((GObject*)pf);
3716 
3717 		pango_shape(utf8.utf8_str()+ pItem->offset,
3718 					pItem->length,
3719 					&(pItem->analysis),
3720 					pGstring);
3721 	}
3722 
3723 	PangoGlyph glyph = pGstring->glyphs[0].glyph;
3724 	if(pGstring)
3725 		pango_glyph_string_free(pGstring);
3726 	_pango_item_list_free(pItems);
3727 	return glyph;
3728 }
3729 
glyphBox(UT_UCS4Char g,UT_Rect & rec,GR_Graphics * pG)3730 bool GR_PangoFont::glyphBox(UT_UCS4Char g, UT_Rect & rec, GR_Graphics * pG)
3731 {
3732 	UT_return_val_if_fail( m_pf, false );
3733 
3734 	double resRatio = pG->getResolutionRatio();
3735 
3736 	guint iGlyphIndx = getGlyphForChar(g, m_pLayoutF, (static_cast<GR_CairoGraphics *>(pG))->getContext());
3737 
3738 	PangoRectangle ink_rect;
3739 	pango_font_get_glyph_extents(m_pLayoutF, iGlyphIndx, &ink_rect, NULL);
3740 
3741 	double dSize = resRatio *(double)pG->getResolution() /
3742 								  (double)pG->getDeviceResolution();
3743 
3744 	rec.left   = static_cast<UT_sint32>(0.5 + fontPoints2float(dSize, ink_rect.x));
3745 
3746 	rec.width  = static_cast<UT_sint32>(0.5 + fontPoints2float(dSize, ink_rect.width));
3747 
3748 	rec.top    = static_cast<UT_sint32>(0.5 + fontPoints2float(dSize, -ink_rect.y));
3749 
3750 	rec.height = static_cast<UT_sint32>(0.5 + fontPoints2float(dSize, ink_rect.height));
3751 
3752 	UT_DEBUGMSG(("GlyphBox: %c [l:%d, w:%d, t:%d, h:%d\n",
3753 				 (char)g, rec.left,rec.width,rec.top,rec.height));
3754 
3755 	return true;
3756 }
3757 
getFamily() const3758 const char* GR_PangoFont::getFamily() const
3759 {
3760 	UT_return_val_if_fail( m_pfdLay, NULL );
3761 
3762 	return pango_font_description_get_family(m_pfdLay);
3763 }
3764 
3765 
3766 //////////////////////////////////////////////////////////////////////////////
3767 //
3768 // GR_PangoRenderInfo Implementation
3769 //
3770 
canAppend(GR_RenderInfo & ri) const3771 bool GR_PangoRenderInfo::canAppend(GR_RenderInfo &ri) const
3772 {
3773 	GR_PangoRenderInfo & RI = (GR_PangoRenderInfo &)ri;
3774 	GR_CairoPangoItem * pItem1 = (GR_CairoPangoItem *)m_pItem;
3775 	GR_CairoPangoItem * pItem2 = (GR_CairoPangoItem *)RI.m_pItem;
3776 
3777 	/* Do not merger runs that have not been shapped yet */
3778 	if (!pItem1 || !pItem2)
3779 		return false;
3780 
3781 	/* If the shapping resulted in font substitution we cannot merge */
3782 	if (pItem1->m_pi->analysis.font == pItem2->m_pi->analysis.font)
3783 		return true;
3784 
3785 	return false;
3786 }
3787 
3788 
append(GR_RenderInfo &,bool)3789 bool GR_PangoRenderInfo::append(GR_RenderInfo &/*ri*/, bool /*bReverse*/)
3790 {
3791 	if(s_pOwnerUTF8 == this)
3792 		s_pOwnerUTF8 = NULL;
3793 
3794 	if(s_pOwnerLogAttrs == this)
3795 		s_pOwnerLogAttrs = NULL;
3796 
3797 	delete [] m_pLogOffsets; m_pLogOffsets = NULL;
3798 
3799 	// will be set when shaping
3800 	m_iCharCount = 0;
3801 	return false;
3802 }
3803 
split(GR_RenderInfo * & pri,bool)3804 bool GR_PangoRenderInfo::split (GR_RenderInfo *&pri, bool /*bReverse*/)
3805 {
3806 	UT_return_val_if_fail(m_pGraphics && m_pFont, false);
3807 
3808 	UT_ASSERT_HARMLESS(!pri);
3809 
3810 	// create a new RI and make a copy of item into
3811 	if(!pri)
3812 	{
3813 		pri = new GR_PangoRenderInfo(m_eScriptType);
3814 		UT_return_val_if_fail(pri,false);
3815 	}
3816 
3817 	pri->m_pItem = m_pItem->makeCopy();
3818 	UT_return_val_if_fail(pri->m_pItem, false);
3819 
3820 	if(s_pOwnerUTF8 == this)
3821 		s_pOwnerUTF8 = NULL;
3822 
3823 	if(s_pOwnerLogAttrs == this)
3824 		s_pOwnerLogAttrs = NULL;
3825 
3826 	delete [] m_pLogOffsets; m_pLogOffsets = NULL;
3827 
3828 	// will be set when shaping
3829 	m_iCharCount = 0;
3830 
3831 	return false;
3832 }
3833 
cut(UT_uint32,UT_uint32,bool)3834 bool GR_PangoRenderInfo::cut(UT_uint32 /*offset*/, UT_uint32 /*iLen*/, bool /*bReverse*/)
3835 {
3836 
3837 	if(s_pOwnerUTF8 == this)
3838 		s_pOwnerUTF8 = NULL;
3839 
3840 	if(s_pOwnerLogAttrs == this)
3841 		s_pOwnerLogAttrs = NULL;
3842 
3843 	delete [] m_pLogOffsets; m_pLogOffsets = NULL;
3844 
3845 	// will be set when shaping
3846 	m_iCharCount = 0;
3847 	return false;
3848 }
3849 
3850 
isJustified() const3851 bool GR_PangoRenderInfo::isJustified() const
3852 {
3853     return (m_pJustify != NULL);
3854 }
3855 
3856 
adobeToUnicode(UT_uint32 c)3857 UT_uint32 adobeToUnicode(UT_uint32 c)
3858 {
3859 	/*
3860 	 * generated from
3861 	 * http://www.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/symbol.txt
3862 	 * maps Adobe Symbol Encoding to Unicode
3863 	 */
3864 	static const UT_uint32 map[256] = {
3865 		/* 0x00 */      0,     0,     0,     0,     0,     0,     0,     0,
3866 				0,     0,     0,     0,     0,     0,     0,     0,
3867 		/* 0x10 */      0,     0,     0,     0,     0,     0,     0,     0,
3868 				0,     0,     0,     0,     0,     0,     0,     0,
3869 		/* 0x20 */ 0x0020,0x0021,0x2200,0x0023,0x2203,0x0025,0x0026,0x220B,
3870 			   0x0028,0x0029,0x2217,0x002B,0x002C,0x2212,0x002E,0x002F,
3871 		/* 0x30 */ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
3872 			   0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,
3873 		/* 0x40 */ 0x2245,0x0391,0x0392,0x03A7,0x0394,0x0395,0x03A6,0x0393,
3874 			   0x0397,0x0399,0x03D1,0x039A,0x039B,0x039C,0x039D,0x039F,
3875 		/* 0x50 */ 0x03A0,0x0398,0x03A1,0x03A3,0x03A4,0x03A5,0x03C2,0x03A9,
3876 			   0x039E,0x03A8,0x0396,0x005B,0x2234,0x005D,0x22A5,0x005F,
3877 		/* 0x60 */ 0xF8E5,0x03B1,0x03B2,0x03C7,0x03B4,0x03B5,0x03C6,0x03B3,
3878 			   0x03B7,0x03B9,0x03D5,0x03BA,0x03BB,0x00B5,0x03BD,0x03BF,
3879 		/* 0x70 */ 0x03C0,0x03B8,0x03C1,0x03C3,0x03C4,0x03C5,0x03D6,0x03C9,
3880 			   0x03BE,0x03C8,0x03B6,0x007B,0x007C,0x007D,0x223C,     0,
3881 		/* 0x80 */      0,     0,     0,     0,     0,     0,     0,     0,
3882 				0,     0,     0,     0,     0,     0,     0,     0,
3883 		/* 0x90 */      0,     0,     0,     0,     0,     0,     0,     0,
3884 				0,     0,     0,     0,     0,     0,     0,     0,
3885 		/* 0xA0 */ 0x20AC,0x03D2,0x2032,0x2264,0x2044,0x221E,0x0192,0x2663,
3886 			   0x2666,0x2665,0x2660,0x2194,0x2190,0x2191,0x2192,0x2193,
3887 		/* 0xB0 */ 0x00B0,0x00B1,0x2033,0x2265,0x00D7,0x221D,0x2202,0x2022,
3888 			   0x00F7,0x2260,0x2261,0x2248,0x2026,0xF8E6,0xF8E7,0x21B5,
3889 		/* 0xC0 */ 0x2135,0x2111,0x211C,0x2118,0x2297,0x2295,0x2205,0x2229,
3890 			   0x222A,0x2283,0x2287,0x2284,0x2282,0x2286,0x2208,0x2209,
3891 		/* 0xD0 */ 0x2220,0x2207,0xF6DA,0xF6D9,0xF6DB,0x220F,0x221A,0x22C5,
3892 			   0x00AC,0x2227,0x2228,0x21D4,0x21D0,0x21D1,0x21D2,0x21D3,
3893 		/* 0xE0 */ 0x25CA,0x2329,0xF8E8,0xF8E9,0xF8EA,0x2211,0xF8EB,0xF8EC,
3894 			   0xF8ED,0xF8EE,0xF8EF,0xF8F0,0xF8F1,0xF8F2,0xF8F3,0xF8F4,
3895 		/* 0xF0 */      0,0x232A,0x222B,0x2320,0xF8F5,0x2321,0xF8F6,0xF8F7,
3896 			   0xF8F8,0xF8F9,0xF8FA,0xF8FB,0xF8FC,0xF8FD,0xF8FE,     0,
3897 		};
3898 	if (c <= 0xFF && map[c] != 0)
3899 		return map[c];
3900 	else
3901 		return c;
3902 }
3903 
adobeDingbatsToUnicode(UT_uint32 c)3904 UT_uint32 adobeDingbatsToUnicode(UT_uint32 c)
3905 {
3906 	/*
3907 	 * generated from
3908 	 * http://www.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/zdingbat.txt
3909 	 * maps Adobe Zapf Dingbats Encoding to Unicode
3910 	 */
3911 	static const UT_uint32 map[256] = {
3912 		/* 0x00 */      0,     0,     0,     0,     0,     0,     0,     0,
3913 		                0,     0,     0,     0,     0,     0,     0,     0,
3914 		/* 0x10 */      0,     0,     0,     0,     0,     0,     0,     0,
3915 		                0,     0,     0,     0,     0,     0,     0,     0,
3916 		/* 0x20 */ 0x0020,0x2701,0x2702,0x2703,0x2704,0x260E,0x2706,0x2707,
3917 		           0x2708,0x2709,0x261B,0x261E,0x270C,0x270D,0x270E,0x270F,
3918 		/* 0x30 */ 0x2710,0x2711,0x2712,0x2713,0x2714,0x2715,0x2716,0x2717,
3919 		           0x2718,0x2719,0x271A,0x271B,0x271C,0x271D,0x271E,0x271F,
3920 		/* 0x40 */ 0x2720,0x2721,0x2722,0x2723,0x2724,0x2725,0x2726,0x2727,
3921 		           0x2605,0x2729,0x272A,0x272B,0x272C,0x272D,0x272E,0x272F,
3922 		/* 0x50 */ 0x2730,0x2731,0x2732,0x2733,0x2734,0x2735,0x2736,0x2737,
3923 		           0x2738,0x2739,0x273A,0x273B,0x273C,0x273D,0x273E,0x273F,
3924 		/* 0x60 */ 0x2740,0x2741,0x2742,0x2743,0x2744,0x2745,0x2746,0x2747,
3925 		           0x2748,0x2749,0x274A,0x274B,0x25CF,0x274D,0x25A0,0x274F,
3926 		/* 0x70 */ 0x2750,0x2751,0x2752,0x25B2,0x25BC,0x25C6,0x2756,0x25D7,
3927 		           0x2758,0x2759,0x275A,0x275B,0x275C,0x275D,0x275E,     0,
3928 		/* 0x80 */ 0xF8D7,0xF8D8,0xF8D9,0xF8DA,0xF8DB,0xF8DC,0xF8DD,0xF8DE,
3929 		           0xF8DF,0xF8E0,0xF8E1,0xF8E2,0xF8E3,0xF8E4,     0,     0,
3930 		/* 0x90 */      0,     0,     0,     0,     0,     0,     0,     0,
3931 		                0,     0,     0,     0,     0,     0,     0,     0,
3932 		/* 0xA0 */      0,0x2761,0x2762,0x2763,0x2764,0x2765,0x2766,0x2767,
3933 		           0x2663,0x2666,0x2665,0x2660,0x2460,0x2461,0x2462,0x2463,
3934 		/* 0xB0 */ 0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,0x2776,0x2777,
3935 		           0x2778,0x2779,0x277A,0x277B,0x277C,0x277D,0x277E,0x277F,
3936 		/* 0xC0 */ 0x2780,0x2781,0x2782,0x2783,0x2784,0x2785,0x2786,0x2787,
3937 		           0x2788,0x2789,0x278A,0x278B,0x278C,0x278D,0x278E,0x278F,
3938 		/* 0xD0 */ 0x2790,0x2791,0x2792,0x2793,0x2794,0x2192,0x2194,0x2195,
3939 		           0x2798,0x2799,0x279A,0x279B,0x279C,0x279D,0x279E,0x279F,
3940 		/* 0xE0 */ 0x27A0,0x27A1,0x27A2,0x27A3,0x27A4,0x27A5,0x27A6,0x27A7,
3941 		           0x27A8,0x27A9,0x27AA,0x27AB,0x27AC,0x27AD,0x27AE,0x27AF,
3942 		/* 0xF0 */      0,0x27B1,0x27B2,0x27B3,0x27B4,0x27B5,0x27B6,0x27B7,
3943 		           0x27B8,0x27B9,0x27BA,0x27BB,0x27BC,0x27BD,0x27BE,     0,
3944 		};
3945 	if (c <= 0xFF && map[c] != 0)
3946 		return map[c];
3947 	else
3948 		return c;
3949 }
3950 
s_getGenericFontProperties(const char *,FontFamilyEnum * pff,FontPitchEnum * pfp,bool * pbTrueType)3951 void GR_Font::s_getGenericFontProperties(const char * /*szFontName*/,
3952 										 FontFamilyEnum * pff,
3953 										 FontPitchEnum * pfp,
3954 										 bool * pbTrueType)
3955 {
3956 	// describe in generic terms the named font.
3957 
3958 	// Note: most of the unix font handling code is in abi/src/af/xap/unix
3959 	// Note: rather than in the graphics class.  i'm not sure this matters,
3960 	// Note: but it is just different....
3961 
3962 	// TODO add code to map the given font name into one of the
3963 	// TODO enums in GR_Font and set *pff and *pft.
3964 
3965 	*pff = FF_Unknown;
3966 	*pfp = FP_Unknown;
3967 	*pbTrueType = true;
3968 }
3969