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