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