1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <canvas/debug.hxx>
21 #include <canvas/canvastools.hxx>
22 #include <tools/diagnose_ex.h>
24 #include <vcl/virdev.hxx>
25 #include <vcl/metric.hxx>
26 #include <vcl/canvastools.hxx>
28 #include <basegfx/polygon/b2dpolypolygon.hxx>
29 #include <basegfx/tools/canvastools.hxx>
31 #include "cairo_canvasfont.hxx"
32 #include "cairo_textlayout.hxx"
33 #include "cairo_canvashelper.hxx"
35 using namespace ::cairo
;
36 using namespace ::com::sun::star
;
42 LINE_COLOR
, FILL_COLOR
, TEXT_COLOR
, IGNORE_COLOR
45 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* ,
46 const rendering::FontRequest
& fontRequest
,
47 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
48 const geometry::Matrix2D
& fontMatrix
)
50 return uno::Reference
< rendering::XCanvasFont
>( new CanvasFont( fontRequest
, extraFontProperties
, fontMatrix
, mpSurfaceProvider
));
53 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* ,
54 const rendering::FontInfo
& /*aFilter*/,
55 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
58 return uno::Sequence
< rendering::FontInfo
>();
62 setupFontTransform( ::OutputDevice
& rOutDev
,
65 const rendering::ViewState
& rViewState
,
66 const rendering::RenderState
& rRenderState
)
68 ::basegfx::B2DHomMatrix aMatrix
;
70 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
74 ::basegfx::B2DTuple aScale
;
75 ::basegfx::B2DTuple aTranslate
;
76 double nRotate
, nShearX
;
78 aMatrix
.decompose( aScale
, aTranslate
, nRotate
, nShearX
);
80 // query font metric _before_ tampering with width and height
81 if( !::rtl::math::approxEqual(aScale
.getX(), aScale
.getY()) )
83 // retrieve true font width
84 const sal_Int32
nFontWidth( rOutDev
.GetFontMetric( io_rVCLFont
).GetWidth() );
86 const sal_Int32
nScaledFontWidth( ::basegfx::fround(nFontWidth
* aScale
.getX()) );
88 if( !nScaledFontWidth
)
90 // scale is smaller than one pixel - disable text
95 io_rVCLFont
.SetWidth( nScaledFontWidth
);
98 if( !::rtl::math::approxEqual(aScale
.getY(), 1.0) )
100 const sal_Int32
nFontHeight( io_rVCLFont
.GetHeight() );
101 io_rVCLFont
.SetHeight( ::basegfx::fround(nFontHeight
* aScale
.getY()) );
104 io_rVCLFont
.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate
, 2*M_PI
)*(1800.0/M_PI
)) ) );
106 // TODO(F2): Missing functionality in VCL: shearing
107 o_rPoint
.X() = ::basegfx::fround(aTranslate
.getX());
108 o_rPoint
.Y() = ::basegfx::fround(aTranslate
.getY());
114 setupOutDevState( OutputDevice
& rOutDev
,
115 const rendering::XCanvas
* pOwner
,
116 const rendering::ViewState
& viewState
,
117 const rendering::RenderState
& renderState
,
118 ColorType eColorType
)
120 ::canvas::tools::verifyInput( renderState
,
121 BOOST_CURRENT_FUNCTION
,
122 const_cast<rendering::XCanvas
*>(pOwner
), // only for refcount
124 eColorType
== IGNORE_COLOR
? 0 : 3 );
126 int nTransparency(0);
128 // TODO(P2): Don't change clipping all the time, maintain current clip
129 // state and change only when update is necessary
131 // accumulate non-empty clips into one region
132 // ==========================================
136 if( viewState
.Clip
.is() )
138 ::basegfx::B2DPolyPolygon
aClipPoly(
139 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
142 if( aClipPoly
.count() )
144 // setup non-empty clipping
145 ::basegfx::B2DHomMatrix aMatrix
;
147 ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix
,
148 viewState
.AffineTransform
) );
150 aClipRegion
= Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly
) );
154 if( renderState
.Clip
.is() )
156 ::basegfx::B2DPolyPolygon
aClipPoly(
157 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
160 ::basegfx::B2DHomMatrix aMatrix
;
162 ::canvas::tools::mergeViewAndRenderTransform( aMatrix
,
166 if( aClipPoly
.count() )
168 // setup non-empty clipping
169 Region aRegion
= Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly
) );
171 if( aClipRegion
.IsEmpty() )
172 aClipRegion
= aRegion
;
174 aClipRegion
.Intersect( aRegion
);
178 // clip polygon is empty
179 aClipRegion
.SetEmpty();
183 // setup accumulated clip region. Note that setting an
184 // empty clip region denotes "clip everything" on the
185 // OutputDevice (which is why we translate that into
186 // SetClipRegion() here). When both view and render clip
187 // are empty, aClipRegion remains default-constructed,
189 if( aClipRegion
.IsEmpty() )
191 rOutDev
.SetClipRegion();
195 rOutDev
.SetClipRegion( aClipRegion
);
198 if( eColorType
!= IGNORE_COLOR
)
200 Color
aColor( COL_WHITE
);
202 if( renderState
.DeviceColor
.getLength() > 2 )
204 aColor
= ::vcl::unotools::stdColorSpaceSequenceToColor( renderState
.DeviceColor
);
207 // extract alpha, and make color opaque
208 // afterwards. Otherwise, OutputDevice won't draw anything
209 nTransparency
= aColor
.GetTransparency();
210 aColor
.SetTransparency(0);
215 rOutDev
.SetLineColor( aColor
);
216 rOutDev
.SetFillColor();
221 rOutDev
.SetFillColor( aColor
);
222 rOutDev
.SetLineColor();
227 rOutDev
.SetTextColor( aColor
);
232 ENSURE_OR_THROW( false,
233 "CanvasHelper::setupOutDevState(): Unexpected color type");
238 return nTransparency
;
241 bool setupTextOutput( OutputDevice
& rOutDev
,
242 const rendering::XCanvas
* pOwner
,
244 const rendering::ViewState
& viewState
,
245 const rendering::RenderState
& renderState
,
246 const uno::Reference
< rendering::XCanvasFont
>& xFont
)
248 setupOutDevState( rOutDev
, pOwner
, viewState
, renderState
, TEXT_COLOR
);
252 CanvasFont
* pFont
= dynamic_cast< CanvasFont
* >( xFont
.get() );
254 ENSURE_ARG_OR_THROW( pFont
,
255 "CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
257 aVCLFont
= pFont
->getVCLFont();
259 Color
aColor( COL_BLACK
);
261 if( renderState
.DeviceColor
.getLength() > 2 )
263 aColor
= ::vcl::unotools::stdColorSpaceSequenceToColor(renderState
.DeviceColor
);
267 aVCLFont
.SetColor( aColor
);
268 aVCLFont
.SetFillColor( aColor
);
270 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
271 if( !setupFontTransform( rOutDev
, o_rOutPos
, aVCLFont
, viewState
, renderState
) )
274 rOutDev
.SetFont( aVCLFont
);
280 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* pOwner
,
281 const rendering::StringContext
& text
,
282 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
283 const rendering::ViewState
& viewState
,
284 const rendering::RenderState
& renderState
,
285 sal_Int8 textDirection
)
287 #ifdef CAIRO_CANVAS_PERF_TRACE
288 struct timespec aTimer
;
289 mxDevice
->startPerfTrace( &aTimer
);
292 ENSURE_ARG_OR_THROW( xFont
.is(),
293 "CanvasHelper::drawText(): font is NULL");
295 if( !mpVirtualDevice
)
296 mpVirtualDevice
= mpSurface
->createVirtualDevice();
298 if( mpVirtualDevice
)
300 #if defined CAIRO_HAS_WIN32_SURFACE
301 // FIXME: Some kind of work-araound...
302 cairo_rectangle (mpSurface
->getCairo().get(), 0, 0, 0, 0);
303 cairo_fill(mpSurface
->getCairo().get());
306 if( !setupTextOutput( *mpVirtualDevice
, pOwner
, aOutpos
, viewState
, renderState
, xFont
) )
307 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
); // no output necessary
309 // change text direction and layout mode
310 sal_uLong
nLayoutMode(0);
311 switch( textDirection
)
313 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
314 nLayoutMode
|= TEXT_LAYOUT_BIDI_LTR
;
315 // FALLTHROUGH intended
316 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
317 nLayoutMode
|= TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
;
318 nLayoutMode
|= TEXT_LAYOUT_TEXTORIGIN_LEFT
;
321 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
322 nLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
;
323 // FALLTHROUGH intended
324 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
325 nLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
;
326 nLayoutMode
|= TEXT_LAYOUT_TEXTORIGIN_RIGHT
;
331 mpVirtualDevice
->SetLayoutMode( nLayoutMode
);
333 OSL_TRACE(":cairocanvas::CanvasHelper::drawText(O,t,f,v,r,d): %s", OUStringToOString( text
.Text
.copy( text
.StartPosition
, text
.Length
),
334 RTL_TEXTENCODING_UTF8
).getStr());
336 TextLayout
* pTextLayout
= new TextLayout(text
, textDirection
, 0, CanvasFont::Reference(dynamic_cast< CanvasFont
* >( xFont
.get() )), mpSurfaceProvider
);
337 pTextLayout
->draw( mpSurface
, *mpVirtualDevice
, aOutpos
, viewState
, renderState
);
340 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
343 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* pOwner
,
344 const uno::Reference
< rendering::XTextLayout
>& xLayoutedText
,
345 const rendering::ViewState
& viewState
,
346 const rendering::RenderState
& renderState
)
348 ENSURE_ARG_OR_THROW( xLayoutedText
.is(),
349 "CanvasHelper::drawTextLayout(): layout is NULL");
351 TextLayout
* pTextLayout
= dynamic_cast< TextLayout
* >( xLayoutedText
.get() );
355 if( !mpVirtualDevice
)
356 mpVirtualDevice
= mpSurface
->createVirtualDevice();
358 if( mpVirtualDevice
)
360 #if defined CAIRO_HAS_WIN32_SURFACE
361 // FIXME: Some kind of work-araound...
362 cairo_rectangle( mpSurface
->getCairo().get(), 0, 0, 0, 0);
363 cairo_fill(mpSurface
->getCairo().get());
365 // TODO(T3): Race condition. We're taking the font
366 // from xLayoutedText, and then calling draw() at it,
367 // without exclusive access. Move setupTextOutput(),
368 // e.g. to impltools?
371 if( !setupTextOutput( *mpVirtualDevice
, pOwner
, aOutpos
, viewState
, renderState
, xLayoutedText
->getFont() ) )
372 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
); // no output necessary
374 // TODO(F2): What about the offset scalings?
375 pTextLayout
->draw( mpSurface
, *mpVirtualDevice
, aOutpos
, viewState
, renderState
);
380 ENSURE_ARG_OR_THROW( false,
381 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
384 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */