1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cairo_canvashelper_text.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <canvas/canvastools.hxx>
36 #include <tools/diagnose_ex.h>
38 #include <vcl/virdev.hxx>
39 #include <vcl/metric.hxx>
40 #include <vcl/canvastools.hxx>
42 #include <basegfx/polygon/b2dpolypolygon.hxx>
43 #include <basegfx/tools/canvastools.hxx>
45 #include "cairo_canvasfont.hxx"
46 #include "cairo_textlayout.hxx"
47 #include "cairo_canvashelper.hxx"
49 using namespace ::cairo
;
50 using namespace ::com::sun::star
;
56 LINE_COLOR
, FILL_COLOR
, TEXT_COLOR
, IGNORE_COLOR
59 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* ,
60 const rendering::FontRequest
& fontRequest
,
61 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
62 const geometry::Matrix2D
& fontMatrix
)
64 return uno::Reference
< rendering::XCanvasFont
>( new CanvasFont( fontRequest
, extraFontProperties
, fontMatrix
, mpSurfaceProvider
));
67 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* ,
68 const rendering::FontInfo
& /*aFilter*/,
69 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
72 return uno::Sequence
< rendering::FontInfo
>();
76 setupFontTransform( ::OutputDevice
& rOutDev
,
79 const rendering::ViewState
& rViewState
,
80 const rendering::RenderState
& rRenderState
)
82 ::basegfx::B2DHomMatrix aMatrix
;
84 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
88 ::basegfx::B2DTuple aScale
;
89 ::basegfx::B2DTuple aTranslate
;
90 double nRotate
, nShearX
;
92 aMatrix
.decompose( aScale
, aTranslate
, nRotate
, nShearX
);
94 // query font metric _before_ tampering with width and height
95 if( !::rtl::math::approxEqual(aScale
.getX(), aScale
.getY()) )
97 // retrieve true font width
98 const sal_Int32
nFontWidth( rOutDev
.GetFontMetric( io_rVCLFont
).GetWidth() );
100 const sal_Int32
nScaledFontWidth( ::basegfx::fround(nFontWidth
* aScale
.getX()) );
102 if( !nScaledFontWidth
)
104 // scale is smaller than one pixel - disable text
109 io_rVCLFont
.SetWidth( nScaledFontWidth
);
112 if( !::rtl::math::approxEqual(aScale
.getY(), 1.0) )
114 const sal_Int32
nFontHeight( io_rVCLFont
.GetHeight() );
115 io_rVCLFont
.SetHeight( ::basegfx::fround(nFontHeight
* aScale
.getY()) );
118 io_rVCLFont
.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate
, 2*M_PI
)*(1800.0/M_PI
)) ) );
120 // TODO(F2): Missing functionality in VCL: shearing
121 o_rPoint
.X() = ::basegfx::fround(aTranslate
.getX());
122 o_rPoint
.Y() = ::basegfx::fround(aTranslate
.getY());
128 setupOutDevState( OutputDevice
& rOutDev
,
129 const rendering::XCanvas
* pOwner
,
130 const rendering::ViewState
& viewState
,
131 const rendering::RenderState
& renderState
,
132 ColorType eColorType
)
134 ::canvas::tools::verifyInput( renderState
,
135 BOOST_CURRENT_FUNCTION
,
136 const_cast<rendering::XCanvas
*>(pOwner
), // only for refcount
138 eColorType
== IGNORE_COLOR
? 0 : 3 );
140 int nTransparency(0);
142 // TODO(P2): Don't change clipping all the time, maintain current clip
143 // state and change only when update is necessary
145 // accumulate non-empty clips into one region
146 // ==========================================
150 if( viewState
.Clip
.is() )
152 ::basegfx::B2DPolyPolygon
aClipPoly(
153 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
156 if( aClipPoly
.count() )
158 // setup non-empty clipping
159 ::basegfx::B2DHomMatrix aMatrix
;
161 ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix
,
162 viewState
.AffineTransform
) );
164 aClipRegion
= Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly
) );
168 if( renderState
.Clip
.is() )
170 ::basegfx::B2DPolyPolygon
aClipPoly(
171 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
174 ::basegfx::B2DHomMatrix aMatrix
;
176 ::canvas::tools::mergeViewAndRenderTransform( aMatrix
,
180 if( aClipPoly
.count() )
182 // setup non-empty clipping
183 Region aRegion
= Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly
) );
185 if( aClipRegion
.IsEmpty() )
186 aClipRegion
= aRegion
;
188 aClipRegion
.Intersect( aRegion
);
192 // clip polygon is empty
193 aClipRegion
.SetEmpty();
197 // setup accumulated clip region. Note that setting an
198 // empty clip region denotes "clip everything" on the
199 // OutputDevice (which is why we translate that into
200 // SetClipRegion() here). When both view and render clip
201 // are empty, aClipRegion remains default-constructed,
203 if( aClipRegion
.IsEmpty() )
205 rOutDev
.SetClipRegion();
209 rOutDev
.SetClipRegion( aClipRegion
);
212 if( eColorType
!= IGNORE_COLOR
)
214 Color
aColor( COL_WHITE
);
216 if( renderState
.DeviceColor
.getLength() > 2 )
218 aColor
= ::vcl::unotools::stdColorSpaceSequenceToColor( renderState
.DeviceColor
);
221 // extract alpha, and make color opaque
222 // afterwards. Otherwise, OutputDevice won't draw anything
223 nTransparency
= aColor
.GetTransparency();
224 aColor
.SetTransparency(0);
229 rOutDev
.SetLineColor( aColor
);
230 rOutDev
.SetFillColor();
235 rOutDev
.SetFillColor( aColor
);
236 rOutDev
.SetLineColor();
241 rOutDev
.SetTextColor( aColor
);
246 ENSURE_OR_THROW( false,
247 "CanvasHelper::setupOutDevState(): Unexpected color type");
252 return nTransparency
;
255 bool setupTextOutput( OutputDevice
& rOutDev
,
256 const rendering::XCanvas
* pOwner
,
258 const rendering::ViewState
& viewState
,
259 const rendering::RenderState
& renderState
,
260 const uno::Reference
< rendering::XCanvasFont
>& xFont
)
262 setupOutDevState( rOutDev
, pOwner
, viewState
, renderState
, TEXT_COLOR
);
266 CanvasFont
* pFont
= dynamic_cast< CanvasFont
* >( xFont
.get() );
268 ENSURE_ARG_OR_THROW( pFont
,
269 "CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
271 aVCLFont
= pFont
->getVCLFont();
273 Color
aColor( COL_BLACK
);
275 if( renderState
.DeviceColor
.getLength() > 2 )
277 aColor
= ::vcl::unotools::stdColorSpaceSequenceToColor(renderState
.DeviceColor
);
281 aVCLFont
.SetColor( aColor
);
282 aVCLFont
.SetFillColor( aColor
);
284 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
285 if( !setupFontTransform( rOutDev
, o_rOutPos
, aVCLFont
, viewState
, renderState
) )
288 rOutDev
.SetFont( aVCLFont
);
294 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* pOwner
,
295 const rendering::StringContext
& text
,
296 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
297 const rendering::ViewState
& viewState
,
298 const rendering::RenderState
& renderState
,
299 sal_Int8 textDirection
)
301 #ifdef CAIRO_CANVAS_PERF_TRACE
302 struct timespec aTimer
;
303 mxDevice
->startPerfTrace( &aTimer
);
306 ENSURE_ARG_OR_THROW( xFont
.is(),
307 "CanvasHelper::drawText(): font is NULL");
309 if( !mpVirtualDevice
)
310 mpVirtualDevice
= mpSurface
->createVirtualDevice();
312 if( mpVirtualDevice
)
314 #if defined CAIRO_HAS_WIN32_SURFACE
315 // FIXME: Some kind of work-araound...
316 cairo_rectangle (mpSurface
->getCairo().get(), 0, 0, 0, 0);
317 cairo_fill(mpSurface
->getCairo().get());
320 if( !setupTextOutput( *mpVirtualDevice
, pOwner
, aOutpos
, viewState
, renderState
, xFont
) )
321 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
); // no output necessary
323 // change text direction and layout mode
324 ULONG
nLayoutMode(0);
325 switch( textDirection
)
327 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
328 nLayoutMode
|= TEXT_LAYOUT_BIDI_LTR
;
329 // FALLTHROUGH intended
330 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
331 nLayoutMode
|= TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
;
332 nLayoutMode
|= TEXT_LAYOUT_TEXTORIGIN_LEFT
;
335 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
336 nLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
;
337 // FALLTHROUGH intended
338 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
339 nLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
;
340 nLayoutMode
|= TEXT_LAYOUT_TEXTORIGIN_RIGHT
;
345 mpVirtualDevice
->SetLayoutMode( nLayoutMode
);
347 OSL_TRACE(":cairocanvas::CanvasHelper::drawText(O,t,f,v,r,d): %s", ::rtl::OUStringToOString( text
.Text
.copy( text
.StartPosition
, text
.Length
),
348 RTL_TEXTENCODING_UTF8
).getStr());
350 TextLayout
* pTextLayout
= new TextLayout(text
, textDirection
, 0, CanvasFont::Reference(dynamic_cast< CanvasFont
* >( xFont
.get() )), mpSurfaceProvider
);
351 pTextLayout
->draw( mpSurface
, *mpVirtualDevice
, aOutpos
, viewState
, renderState
);
354 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
357 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* pOwner
,
358 const uno::Reference
< rendering::XTextLayout
>& xLayoutedText
,
359 const rendering::ViewState
& viewState
,
360 const rendering::RenderState
& renderState
)
362 ENSURE_ARG_OR_THROW( xLayoutedText
.is(),
363 "CanvasHelper::drawTextLayout(): layout is NULL");
365 TextLayout
* pTextLayout
= dynamic_cast< TextLayout
* >( xLayoutedText
.get() );
369 if( !mpVirtualDevice
)
370 mpVirtualDevice
= mpSurface
->createVirtualDevice();
372 if( mpVirtualDevice
)
374 #if defined CAIRO_HAS_WIN32_SURFACE
375 // FIXME: Some kind of work-araound...
376 cairo_rectangle( mpSurface
->getCairo().get(), 0, 0, 0, 0);
377 cairo_fill(mpSurface
->getCairo().get());
379 // TODO(T3): Race condition. We're taking the font
380 // from xLayoutedText, and then calling draw() at it,
381 // without exclusive access. Move setupTextOutput(),
382 // e.g. to impltools?
385 if( !setupTextOutput( *mpVirtualDevice
, pOwner
, aOutpos
, viewState
, renderState
, xLayoutedText
->getFont() ) )
386 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
); // no output necessary
388 // TODO(F2): What about the offset scalings?
389 pTextLayout
->draw( mpSurface
, *mpVirtualDevice
, aOutpos
, viewState
, renderState
);
394 ENSURE_ARG_OR_THROW( false,
395 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
398 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);