Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <math.h>
21 :
22 : #include <canvas/debug.hxx>
23 : #include <canvas/verbosetrace.hxx>
24 : #include <cppuhelper/supportsservice.hxx>
25 : #include <tools/diagnose_ex.h>
26 :
27 : #include <vcl/metric.hxx>
28 : #include <vcl/virdev.hxx>
29 :
30 : #ifdef WNT
31 : #ifdef max
32 : #undef max
33 : #endif
34 : #ifdef min
35 : #undef min
36 : #endif
37 : #endif
38 : #include <vcl/sysdata.hxx>
39 :
40 : #include <basegfx/matrix/b2dhommatrix.hxx>
41 : #include <basegfx/numeric/ftools.hxx>
42 :
43 : #include <boost/scoped_array.hpp>
44 :
45 : #include "cairo_textlayout.hxx"
46 : #include "cairo_spritecanvas.hxx"
47 :
48 : #ifdef CAIRO_HAS_QUARTZ_SURFACE
49 : #include <cairo-quartz.h>
50 : #elif defined CAIRO_HAS_WIN32_SURFACE
51 : # include "cairo_win32_cairo.hxx"
52 : # include <cairo-win32.h>
53 : #elif defined CAIRO_HAS_FT_FONT
54 : # include <cairo-ft.h>
55 : #else
56 : # error Native API needed.
57 : #endif
58 :
59 : #ifdef IOS
60 : #include <CoreText/CoreText.h>
61 : #endif
62 :
63 : using namespace ::cairo;
64 : using namespace ::com::sun::star;
65 :
66 : namespace cairocanvas
67 : {
68 : namespace
69 : {
70 0 : void setupLayoutMode( OutputDevice& rOutDev,
71 : sal_Int8 nTextDirection )
72 : {
73 : // TODO(P3): avoid if already correctly set
74 0 : ComplexTextLayoutMode nLayoutMode = TEXT_LAYOUT_DEFAULT;
75 0 : switch( nTextDirection )
76 : {
77 : case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
78 0 : break;
79 : case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
80 0 : nLayoutMode = TEXT_LAYOUT_BIDI_STRONG;
81 0 : break;
82 : case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
83 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL;
84 0 : break;
85 : case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
86 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
87 0 : break;
88 : default:
89 0 : break;
90 : }
91 :
92 : // set calculated layout mode. Origin is always the left edge,
93 : // as required at the API spec
94 0 : rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT );
95 0 : }
96 :
97 0 : bool compareFallbacks(const SystemGlyphData&rA, const SystemGlyphData &rB)
98 : {
99 0 : return rA.fallbacklevel < rB.fallbacklevel;
100 : }
101 : }
102 :
103 0 : TextLayout::TextLayout( const rendering::StringContext& aText,
104 : sal_Int8 nDirection,
105 : sal_Int64 /*nRandomSeed*/,
106 : const CanvasFont::Reference& rFont,
107 : const SurfaceProviderRef& rRefDevice ) :
108 : TextLayout_Base( m_aMutex ),
109 : maText( aText ),
110 : maLogicalAdvancements(),
111 : mpFont( rFont ),
112 : mpRefDevice( rRefDevice ),
113 0 : mnTextDirection( nDirection )
114 : {
115 0 : }
116 :
117 0 : TextLayout::~TextLayout()
118 : {
119 0 : }
120 :
121 0 : void SAL_CALL TextLayout::disposing()
122 : {
123 0 : ::osl::MutexGuard aGuard( m_aMutex );
124 :
125 0 : mpFont.clear();
126 0 : mpRefDevice.clear();
127 0 : }
128 :
129 : // XTextLayout
130 0 : uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException, std::exception)
131 : {
132 0 : ::osl::MutexGuard aGuard( m_aMutex );
133 :
134 : // TODO
135 0 : return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >();
136 : }
137 :
138 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException, std::exception)
139 : {
140 0 : ::osl::MutexGuard aGuard( m_aMutex );
141 :
142 : // TODO
143 0 : return uno::Sequence< geometry::RealRectangle2D >();
144 : }
145 :
146 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException, std::exception)
147 : {
148 0 : ::osl::MutexGuard aGuard( m_aMutex );
149 :
150 : // TODO
151 0 : return uno::Sequence< geometry::RealRectangle2D >();
152 : }
153 :
154 0 : uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException, std::exception)
155 : {
156 0 : ::osl::MutexGuard aGuard( m_aMutex );
157 :
158 0 : return maLogicalAdvancements;
159 : }
160 :
161 0 : void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
162 : {
163 0 : ::osl::MutexGuard aGuard( m_aMutex );
164 :
165 0 : if( aAdvancements.getLength() != maText.Length )
166 : {
167 : OSL_TRACE( "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
168 0 : throw lang::IllegalArgumentException();
169 : }
170 :
171 0 : maLogicalAdvancements = aAdvancements;
172 0 : }
173 :
174 0 : geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException, std::exception)
175 : {
176 0 : ::osl::MutexGuard aGuard( m_aMutex );
177 :
178 0 : OutputDevice* pOutDev = mpRefDevice->getOutputDevice();
179 0 : if( !pOutDev )
180 0 : return geometry::RealRectangle2D();
181 :
182 0 : ScopedVclPtrInstance< VirtualDevice > pVDev( *pOutDev );
183 0 : pVDev->SetFont( mpFont->getVCLFont() );
184 :
185 : // need metrics for Y offset, the XCanvas always renders
186 : // relative to baseline
187 0 : const ::FontMetric& aMetric( pVDev->GetFontMetric() );
188 :
189 0 : setupLayoutMode( *pVDev.get(), mnTextDirection );
190 :
191 0 : const sal_Int32 nAboveBaseline( -aMetric.GetIntLeading() - aMetric.GetAscent() );
192 0 : const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
193 :
194 0 : if( maLogicalAdvancements.getLength() )
195 : {
196 : return geometry::RealRectangle2D( 0, nAboveBaseline,
197 0 : maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
198 0 : nBelowBaseline );
199 : }
200 : else
201 : {
202 : return geometry::RealRectangle2D( 0, nAboveBaseline,
203 0 : pVDev->GetTextWidth(
204 : maText.Text,
205 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
206 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
207 0 : nBelowBaseline );
208 0 : }
209 : }
210 :
211 0 : double SAL_CALL TextLayout::justify( double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
212 : {
213 0 : ::osl::MutexGuard aGuard( m_aMutex );
214 :
215 : // TODO
216 0 : return 0.0;
217 : }
218 :
219 0 : double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/,
220 : double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
221 : {
222 0 : ::osl::MutexGuard aGuard( m_aMutex );
223 :
224 : // TODO
225 0 : return 0.0;
226 : }
227 :
228 0 : rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ ) throw (uno::RuntimeException, std::exception)
229 : {
230 0 : ::osl::MutexGuard aGuard( m_aMutex );
231 :
232 : // TODO
233 0 : return rendering::TextHit();
234 : }
235 :
236 0 : rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/,
237 : sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
238 : {
239 0 : ::osl::MutexGuard aGuard( m_aMutex );
240 :
241 : // TODO
242 0 : return rendering::Caret();
243 : }
244 :
245 0 : sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/,
246 : sal_Int32 /*nCaretAdvancement*/,
247 : sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
248 : {
249 0 : ::osl::MutexGuard aGuard( m_aMutex );
250 :
251 : // TODO
252 0 : return 0;
253 : }
254 :
255 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/,
256 : sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
257 : {
258 0 : ::osl::MutexGuard aGuard( m_aMutex );
259 :
260 : // TODO
261 0 : return uno::Reference< rendering::XPolyPolygon2D >();
262 : }
263 :
264 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/,
265 : sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
266 : {
267 0 : ::osl::MutexGuard aGuard( m_aMutex );
268 :
269 : // TODO
270 0 : return uno::Reference< rendering::XPolyPolygon2D >();
271 : }
272 :
273 0 : double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException, std::exception)
274 : {
275 0 : ::osl::MutexGuard aGuard( m_aMutex );
276 :
277 : // TODO
278 0 : return 0.0;
279 : }
280 :
281 0 : sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException, std::exception)
282 : {
283 0 : ::osl::MutexGuard aGuard( m_aMutex );
284 :
285 0 : return mnTextDirection;
286 : }
287 :
288 0 : uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException, std::exception)
289 : {
290 0 : ::osl::MutexGuard aGuard( m_aMutex );
291 :
292 0 : return mpFont.get();
293 : }
294 :
295 0 : rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException, std::exception)
296 : {
297 0 : ::osl::MutexGuard aGuard( m_aMutex );
298 :
299 0 : return maText;
300 : }
301 :
302 : /**
303 : * TextLayout::isCairoRenderable
304 : *
305 : * Features currently not supported by Cairo (VCL rendering is used as fallback):
306 : * - vertical glyphs
307 : *
308 : * @return true, if text/font can be rendered with cairo
309 : **/
310 0 : bool TextLayout::isCairoRenderable(SystemFontData aSysFontData) const
311 : {
312 : #if defined UNX && !defined MACOSX && !defined IOS
313 : // is font usable?
314 0 : if (!aSysFontData.nFontId)
315 0 : return false;
316 : #endif
317 :
318 : // vertical glyph rendering is not supported in cairo for now
319 0 : if (aSysFontData.bVerticalCharacterType)
320 : {
321 : OSL_TRACE(":cairocanvas::TextLayout::isCairoRenderable(): ***************** VERTICAL CHARACTER STYLE!!! ****************");
322 0 : return false;
323 : }
324 :
325 0 : return true;
326 : }
327 :
328 : #ifdef CAIRO_HAS_WIN32_SURFACE
329 : namespace
330 : {
331 : /**
332 : * cairo::ucs4toindex: Convert ucs4 char to glyph index
333 : * @param ucs4 an ucs4 char
334 : * @param hfont current font
335 : *
336 : * @return true if successful
337 : **/
338 : unsigned long ucs4toindex(unsigned int ucs4, HFONT hfont)
339 : {
340 : wchar_t unicode[2];
341 : WORD glyph_index;
342 : HDC hdc = NULL;
343 :
344 : hdc = CreateCompatibleDC (NULL);
345 :
346 : if (!hdc) return 0;
347 : if (!SetGraphicsMode (hdc, GM_ADVANCED))
348 : {
349 : DeleteDC (hdc);
350 : return 0;
351 : }
352 :
353 : SelectObject (hdc, hfont);
354 : SetMapMode (hdc, MM_TEXT);
355 :
356 : unicode[0] = ucs4;
357 : unicode[1] = 0;
358 : if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR)
359 : {
360 : glyph_index = 0;
361 : }
362 :
363 : DeleteDC (hdc);
364 : return glyph_index;
365 : }
366 : }
367 : #endif
368 :
369 : /**
370 : * TextLayout::draw
371 : *
372 : * Cairo-based text rendering. Draw text directly on the cairo surface with cairo fonts.
373 : * Avoid using VCL VirtualDevices for that, bypassing VCL DrawText functions, when possible
374 : *
375 : * Note: some text effects are not rendered due to lacking generic canvas or cairo canvas
376 : * implementation. See issues 92657, 92658, 92659, 92660, 97529
377 : *
378 : * @return true, if successful
379 : **/
380 0 : bool TextLayout::draw( CairoSharedPtr& pSCairo,
381 : OutputDevice& rOutDev,
382 : const Point& rOutpos,
383 : const rendering::ViewState& viewState,
384 : const rendering::RenderState& renderState ) const
385 : {
386 0 : ::osl::MutexGuard aGuard( m_aMutex );
387 0 : SystemTextLayoutData aSysLayoutData;
388 : #if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1)
389 : LOGFONTW logfont;
390 : #endif
391 0 : setupLayoutMode( rOutDev, mnTextDirection );
392 :
393 : // TODO(P2): cache that
394 0 : ::boost::scoped_array< long > aOffsets(new long[maLogicalAdvancements.getLength()]);
395 :
396 0 : if( maLogicalAdvancements.getLength() )
397 : {
398 0 : setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState );
399 :
400 : // TODO(F3): ensure correct length and termination for DX
401 : // array (last entry _must_ contain the overall width)
402 : }
403 :
404 0 : aSysLayoutData = rOutDev.GetSysTextLayoutData(rOutpos, maText.Text,
405 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
406 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
407 0 : maLogicalAdvancements.getLength() ? aOffsets.get() : NULL);
408 :
409 : // Sort them so that all glyphs on the same glyph fallback level are consecutive
410 0 : std::sort(aSysLayoutData.rGlyphData.begin(), aSysLayoutData.rGlyphData.end(), compareFallbacks);
411 0 : bool bCairoRenderable = true;
412 :
413 : //Pull all the fonts we need to render the text
414 : typedef std::pair<SystemFontData,int> FontLevel;
415 : typedef std::vector<FontLevel> FontLevelVector;
416 0 : FontLevelVector aFontData;
417 0 : SystemGlyphDataVector::const_iterator aGlyphIter=aSysLayoutData.rGlyphData.begin();
418 0 : const SystemGlyphDataVector::const_iterator aGlyphEnd=aSysLayoutData.rGlyphData.end();
419 0 : for( ; aGlyphIter != aGlyphEnd; ++aGlyphIter )
420 : {
421 0 : if( aFontData.empty() || aGlyphIter->fallbacklevel != aFontData.back().second )
422 : {
423 0 : aFontData.push_back(FontLevel(rOutDev.GetSysFontData(aGlyphIter->fallbacklevel),
424 0 : aGlyphIter->fallbacklevel));
425 0 : if( !isCairoRenderable(aFontData.back().first) )
426 : {
427 0 : bCairoRenderable = false;
428 : OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): VCL FALLBACK %s%s%s%s - %s",
429 : maLogicalAdvancements.getLength() ? "ADV " : "",
430 : aFontData.back().first.bAntialias ? "AA " : "",
431 : aFontData.back().first.bFakeBold ? "FB " : "",
432 : aFontData.back().first.bFakeItalic ? "FI " : "",
433 : OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ),
434 : RTL_TEXTENCODING_UTF8 ).getStr());
435 0 : break;
436 : }
437 : }
438 : }
439 :
440 : // The ::GetSysTextLayoutData(), i.e. layouting of text to glyphs can change the font being used.
441 : // The fallback checks need to be done after final font is known.
442 0 : if (!bCairoRenderable) // VCL FALLBACKS
443 : {
444 0 : if (maLogicalAdvancements.getLength()) // VCL FALLBACK - with glyph advances
445 : {
446 0 : rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets.get(),
447 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
448 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
449 0 : return true;
450 : }
451 : else // VCL FALLBACK - without advances
452 : {
453 : rOutDev.DrawText( rOutpos, maText.Text,
454 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
455 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
456 0 : return true;
457 : }
458 : }
459 :
460 0 : if (aSysLayoutData.rGlyphData.empty())
461 0 : return false; //??? false?
462 :
463 : /**
464 : * Setup platform independent glyph vector into cairo-based glyphs vector.
465 : **/
466 :
467 : // Loop through the fonts used and render the matching glyphs for each
468 0 : FontLevelVector::const_iterator aFontDataIter = aFontData.begin();
469 0 : const FontLevelVector::const_iterator aFontDataEnd = aFontData.end();
470 0 : for( ; aFontDataIter != aFontDataEnd; ++aFontDataIter )
471 : {
472 0 : const SystemFontData &rSysFontData = aFontDataIter->first;
473 :
474 : // setup glyphs
475 0 : std::vector<cairo_glyph_t> cairo_glyphs;
476 0 : cairo_glyphs.reserve( 256 );
477 :
478 0 : aGlyphIter=aSysLayoutData.rGlyphData.begin();
479 0 : for( ; aGlyphIter != aGlyphEnd; ++aGlyphIter )
480 : {
481 0 : SystemGlyphData systemGlyph = *aGlyphIter;
482 0 : if( systemGlyph.fallbacklevel != aFontDataIter->second )
483 0 : continue;
484 :
485 : cairo_glyph_t aGlyph;
486 0 : aGlyph.index = systemGlyph.index;
487 : #ifdef CAIRO_HAS_WIN32_SURFACE
488 : // Cairo requires standard glyph indexes (ETO_GLYPH_INDEX), while vcl/win/* uses ucs4 chars.
489 : // Convert to standard indexes
490 : aGlyph.index = ucs4toindex((unsigned int) aGlyph.index, rSysFontData.hFont);
491 : #endif
492 0 : aGlyph.x = systemGlyph.x;
493 0 : aGlyph.y = systemGlyph.y;
494 0 : cairo_glyphs.push_back(aGlyph);
495 : }
496 :
497 0 : if (cairo_glyphs.empty())
498 0 : continue;
499 :
500 : /**
501 : * Setup font
502 : **/
503 0 : cairo_font_face_t* font_face = NULL;
504 :
505 : #ifdef CAIRO_HAS_QUARTZ_SURFACE
506 : # ifdef MACOSX
507 : // TODO: use cairo_quartz_font_face_create_for_cgfont(cgFont)
508 : // when CGFont (Mac OS X 10.5 API) is provided by the AQUA VCL backend.
509 : font_face = cairo_quartz_font_face_create_for_atsu_font_id((ATSUFontID) rSysFontData.aATSUFontID);
510 : # else // iOS
511 : font_face = cairo_quartz_font_face_create_for_cgfont( CTFontCopyGraphicsFont( rSysFontData.rCTFont, NULL ) );
512 : # endif
513 :
514 : #elif defined CAIRO_HAS_WIN32_SURFACE
515 : # if (OSL_DEBUG_LEVEL > 1)
516 : GetObjectW( rSysFontData.hFont, sizeof(logfont), &logfont );
517 : # endif
518 : // Note: cairo library uses logfont fallbacks when lfEscapement, lfOrientation and lfWidth are not zero.
519 : // VCL always has non-zero value for lfWidth
520 : font_face = cairo_win32_font_face_create_for_hfont(rSysFontData.hFont);
521 :
522 : #elif defined CAIRO_HAS_FT_FONT
523 : font_face = cairo_ft_font_face_create_for_ft_face(static_cast<FT_Face>(rSysFontData.nFontId),
524 0 : rSysFontData.nFontFlags);
525 : #else
526 : # error Native API needed.
527 : #endif
528 :
529 0 : cairo_set_font_face( pSCairo.get(), font_face);
530 :
531 : // create default font options. cairo_get_font_options() does not retrieve the surface defaults,
532 : // only what has been set before with cairo_set_font_options()
533 0 : cairo_font_options_t* options = cairo_font_options_create();
534 0 : if (rSysFontData.bAntialias)
535 : {
536 : // CAIRO_ANTIALIAS_GRAY provides more similar result to VCL Canvas,
537 : // so we're not using CAIRO_ANTIALIAS_SUBPIXEL
538 0 : cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
539 : }
540 0 : cairo_set_font_options( pSCairo.get(), options);
541 :
542 : // Font color
543 0 : Color mTextColor = rOutDev.GetTextColor();
544 : cairo_set_source_rgb(pSCairo.get(),
545 0 : mTextColor.GetRed()/255.0,
546 0 : mTextColor.GetGreen()/255.0,
547 0 : mTextColor.GetBlue()/255.0);
548 :
549 : // Font rotation and scaling
550 : cairo_matrix_t m;
551 0 : vcl::Font aFont = rOutDev.GetFont();
552 :
553 0 : cairo_matrix_init_identity(&m);
554 :
555 0 : if (aSysLayoutData.orientation)
556 0 : cairo_matrix_rotate(&m, (3600 - aSysLayoutData.orientation) * M_PI / 1800.0);
557 :
558 0 : long nWidth = aFont.GetWidth();
559 0 : long nHeight = aFont.GetHeight();
560 0 : if (nWidth == 0)
561 0 : nWidth = nHeight;
562 0 : cairo_matrix_scale(&m, nWidth, nHeight);
563 :
564 : //faux italics
565 0 : if (rSysFontData.bFakeItalic)
566 0 : m.xy = -m.xx * 0x6000L / 0x10000L;
567 :
568 0 : cairo_set_font_matrix(pSCairo.get(), &m);
569 :
570 : #if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1)
571 : # define TEMP_TRACE_FONT OUString(reinterpret_cast<const sal_Unicode*> (logfont.lfFaceName))
572 : #else
573 : # define TEMP_TRACE_FONT aFont.GetName()
574 : #endif
575 : SAL_INFO(
576 : "canvas.cairo",
577 : "Size:(" << aFont.GetWidth() << "," << aFont.GetHeight()
578 : << "), Pos (" << rOutpos.X() << "," << rOutpos.Y()
579 : << "), G("
580 : << (cairo_glyphs.size() > 0 ? cairo_glyphs[0].index : -1)
581 : << ","
582 : << (cairo_glyphs.size() > 1 ? cairo_glyphs[1].index : -1)
583 : << ","
584 : << (cairo_glyphs.size() > 2 ? cairo_glyphs[2].index : -1)
585 : << ") " << (maLogicalAdvancements.getLength() ? "ADV " : "")
586 : << (rSysFontData.bAntialias ? "AA " : "")
587 : << (rSysFontData.bFakeBold ? "FB " : "")
588 : << (rSysFontData.bFakeItalic ? "FI " : "") << " || Name:"
589 : << TEMP_TRACE_FONT << " - "
590 : << maText.Text.copy(maText.StartPosition, maText.Length));
591 : #undef TEMP_TRACE_FONT
592 :
593 0 : cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size());
594 :
595 : //faux bold
596 0 : if (rSysFontData.bFakeBold)
597 : {
598 0 : double bold_dx = 0.5 * sqrt( 0.7 * aFont.GetHeight() );
599 0 : int total_steps = 1 * ((int) (bold_dx + 0.5));
600 :
601 : // loop to draw the text for every half pixel of displacement
602 0 : for (int nSteps = 0; nSteps < total_steps; nSteps++)
603 : {
604 0 : for(int nGlyphIdx = 0; nGlyphIdx < (int) cairo_glyphs.size(); nGlyphIdx++)
605 : {
606 0 : cairo_glyphs[nGlyphIdx].x += (bold_dx * nSteps / total_steps) / 4;
607 0 : cairo_glyphs[nGlyphIdx].y -= (bold_dx * nSteps / total_steps) / 4;
608 : }
609 0 : cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size());
610 : }
611 : OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): FAKEBOLD - dx:%d", (int) bold_dx);
612 : }
613 :
614 0 : cairo_font_face_destroy(font_face);
615 0 : cairo_font_options_destroy(options);
616 0 : }
617 0 : return true;
618 : }
619 :
620 : namespace
621 : {
622 0 : class OffsetTransformer
623 : {
624 : public:
625 0 : explicit OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) :
626 0 : maMatrix( rMat )
627 : {
628 0 : }
629 :
630 0 : sal_Int32 operator()( const double& rOffset )
631 : {
632 : // This is an optimization of the normal rMat*[x,0]
633 : // transformation of the advancement vector (in x
634 : // direction), followed by a length calculation of the
635 : // resulting vector: advancement' =
636 : // ||rMat*[x,0]||. Since advancements are vectors, we
637 : // can ignore translational components, thus if [x,0],
638 : // it follows that rMat*[x,0]=[x',0] holds. Thus, we
639 : // just have to calc the transformation of the x
640 : // component.
641 :
642 : // TODO(F2): Handle non-horizontal advancements!
643 0 : return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
644 0 : maMatrix.get(1,0)*rOffset) );
645 : }
646 :
647 : private:
648 : ::basegfx::B2DHomMatrix maMatrix;
649 : };
650 : }
651 :
652 0 : void TextLayout::setupTextOffsets( long* outputOffsets,
653 : const uno::Sequence< double >& inputOffsets,
654 : const rendering::ViewState& viewState,
655 : const rendering::RenderState& renderState ) const
656 : {
657 0 : ENSURE_OR_THROW( outputOffsets!=NULL,
658 : "TextLayout::setupTextOffsets offsets NULL" );
659 :
660 0 : ::basegfx::B2DHomMatrix aMatrix;
661 :
662 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
663 : viewState,
664 0 : renderState);
665 :
666 : // fill integer offsets
667 : ::std::transform( inputOffsets.getConstArray(),
668 0 : inputOffsets.getConstArray()+inputOffsets.getLength(),
669 : outputOffsets,
670 0 : OffsetTransformer( aMatrix ) );
671 0 : }
672 :
673 0 : OUString SAL_CALL TextLayout::getImplementationName() throw( uno::RuntimeException, std::exception )
674 : {
675 0 : return OUString( "CairoCanvas::TextLayout" );
676 : }
677 :
678 0 : sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName ) throw( uno::RuntimeException, std::exception )
679 : {
680 0 : return cppu::supportsService( this, ServiceName );
681 : }
682 :
683 0 : uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames() throw( uno::RuntimeException, std::exception )
684 : {
685 0 : uno::Sequence< OUString > aRet(1);
686 0 : aRet[0] = "com.sun.star.rendering.TextLayout";
687 :
688 0 : return aRet;
689 : }
690 6 : }
691 :
692 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|