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