Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / cairo / cairo_canvashelper_text.cxx
blob92782af3cb498659a710459e0fff684dda07275f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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;
38 namespace cairocanvas
40 enum ColorType
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*/ )
57 // TODO
58 return uno::Sequence< rendering::FontInfo >();
61 static bool
62 setupFontTransform( ::OutputDevice& rOutDev,
63 ::Point& o_rPoint,
64 vcl::Font& io_rVCLFont,
65 const rendering::ViewState& rViewState,
66 const rendering::RenderState& rRenderState )
68 ::basegfx::B2DHomMatrix aMatrix;
70 ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
71 rViewState,
72 rRenderState);
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
91 // output altogether
92 return false;
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());
110 return true;
113 static int
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
130 ::canvas::tools::clipOutDev(viewState, renderState, rOutDev);
132 if( eColorType != IGNORE_COLOR )
134 Color aColor( COL_WHITE );
136 if( renderState.DeviceColor.getLength() > 2 )
138 aColor = vcl::unotools::stdColorSpaceSequenceToColor( renderState.DeviceColor );
141 // extract alpha, and make color opaque
142 // afterwards. Otherwise, OutputDevice won't draw anything
143 nTransparency = aColor.GetTransparency();
144 aColor.SetTransparency(0);
146 switch( eColorType )
148 case LINE_COLOR:
149 rOutDev.SetLineColor( aColor );
150 rOutDev.SetFillColor();
152 break;
154 case FILL_COLOR:
155 rOutDev.SetFillColor( aColor );
156 rOutDev.SetLineColor();
158 break;
160 case TEXT_COLOR:
161 rOutDev.SetTextColor( aColor );
163 break;
165 default:
166 ENSURE_OR_THROW( false,
167 "CanvasHelper::setupOutDevState(): Unexpected color type");
168 break;
172 return nTransparency;
175 class DeviceSettingsGuard
177 private:
178 VclPtr<OutputDevice> mpVirtualDevice;
179 cairo_t *mpCairo;
180 bool mbMappingWasEnabled;
181 public:
182 DeviceSettingsGuard(OutputDevice *pVirtualDevice, cairo_t *pCairo)
183 : mpVirtualDevice(pVirtualDevice)
184 , mpCairo(pCairo)
185 , mbMappingWasEnabled(mpVirtualDevice->IsMapModeEnabled())
187 cairo_save(mpCairo);
188 mpVirtualDevice->Push();
189 mpVirtualDevice->EnableMapMode(false);
192 ~DeviceSettingsGuard()
194 mpVirtualDevice->EnableMapMode(mbMappingWasEnabled);
195 mpVirtualDevice->Pop();
196 cairo_restore(mpCairo);
200 bool setupTextOutput( OutputDevice& rOutDev,
201 const rendering::XCanvas* pOwner,
202 ::Point& o_rOutPos,
203 const rendering::ViewState& viewState,
204 const rendering::RenderState& renderState,
205 const uno::Reference< rendering::XCanvasFont >& xFont )
207 setupOutDevState( rOutDev, pOwner, viewState, renderState, TEXT_COLOR );
209 CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
211 ENSURE_ARG_OR_THROW( pFont,
212 "CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
214 vcl::Font aVCLFont = pFont->getVCLFont();
216 Color aColor( COL_BLACK );
218 if( renderState.DeviceColor.getLength() > 2 )
220 aColor = vcl::unotools::stdColorSpaceSequenceToColor(renderState.DeviceColor );
223 // setup font color
224 aVCLFont.SetColor( aColor );
225 aVCLFont.SetFillColor( aColor );
227 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
228 if( !setupFontTransform( rOutDev, o_rOutPos, aVCLFont, viewState, renderState ) )
229 return false;
231 rOutDev.SetFont( aVCLFont );
233 return true;
236 //set the clip of the rOutDev to the cairo surface
237 void CanvasHelper::clip_cairo_from_dev(::OutputDevice& rOutDev)
239 vcl::Region aRegion(rOutDev.GetClipRegion());
240 if (!aRegion.IsEmpty() && !aRegion.IsNull())
242 doPolyPolygonImplementation(aRegion.GetAsB2DPolyPolygon(), Clip, mpCairo.get(),
243 NULL, mpSurfaceProvider, rendering::FillRule_EVEN_ODD);
247 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* pOwner,
248 const rendering::StringContext& text,
249 const uno::Reference< rendering::XCanvasFont >& xFont,
250 const rendering::ViewState& viewState,
251 const rendering::RenderState& renderState,
252 sal_Int8 textDirection )
254 #ifdef CAIRO_CANVAS_PERF_TRACE
255 struct timespec aTimer;
256 mxDevice->startPerfTrace( &aTimer );
257 #endif
259 ENSURE_ARG_OR_THROW( xFont.is(),
260 "CanvasHelper::drawText(): font is NULL");
262 if( !mpVirtualDevice )
263 mpVirtualDevice = mpSurface->createVirtualDevice();
265 if( mpVirtualDevice )
267 DeviceSettingsGuard aGuard(mpVirtualDevice.get(), mpCairo.get());
269 #if defined CAIRO_HAS_WIN32_SURFACE
270 // FIXME: Some kind of work-araound...
271 cairo_rectangle (mpCairo.get(), 0, 0, 0, 0);
272 cairo_fill(mpCairo.get());
273 #endif
274 ::Point aOutpos;
275 if( !setupTextOutput( *mpVirtualDevice.get(), pOwner, aOutpos, viewState, renderState, xFont ) )
276 return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
278 // change text direction and layout mode
279 ComplexTextLayoutMode nLayoutMode(TEXT_LAYOUT_DEFAULT);
280 switch( textDirection )
282 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
283 // FALLTHROUGH intended
284 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
285 nLayoutMode |= TEXT_LAYOUT_BIDI_STRONG;
286 nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_LEFT;
287 break;
289 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
290 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
291 // FALLTHROUGH intended
292 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
293 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
294 nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_RIGHT;
295 break;
298 // TODO(F2): alpha
299 mpVirtualDevice->SetLayoutMode( nLayoutMode );
301 clip_cairo_from_dev(*mpVirtualDevice);
303 OSL_TRACE(":cairocanvas::CanvasHelper::drawText(O,t,f,v,r,d): %s", OUStringToOString( text.Text.copy( text.StartPosition, text.Length ),
304 RTL_TEXTENCODING_UTF8 ).getStr());
306 rtl::Reference< TextLayout > pTextLayout( new TextLayout(text, textDirection, 0, CanvasFont::Reference(dynamic_cast< CanvasFont* >( xFont.get() )), mpSurfaceProvider) );
307 pTextLayout->draw(mpCairo, *mpVirtualDevice, aOutpos, viewState, renderState);
310 return uno::Reference< rendering::XCachedPrimitive >(NULL);
313 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* pOwner,
314 const uno::Reference< rendering::XTextLayout >& xLayoutedText,
315 const rendering::ViewState& viewState,
316 const rendering::RenderState& renderState )
318 ENSURE_ARG_OR_THROW( xLayoutedText.is(),
319 "CanvasHelper::drawTextLayout(): layout is NULL");
321 TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
323 if( pTextLayout )
325 if( !mpVirtualDevice )
326 mpVirtualDevice = mpSurface->createVirtualDevice();
328 if( mpVirtualDevice )
330 DeviceSettingsGuard aGuard(mpVirtualDevice.get(), mpCairo.get());
332 #if defined CAIRO_HAS_WIN32_SURFACE
333 // FIXME: Some kind of work-araound...
334 cairo_rectangle(mpCairo.get(), 0, 0, 0, 0);
335 cairo_fill(mpCairo.get());
336 #endif
337 // TODO(T3): Race condition. We're taking the font
338 // from xLayoutedText, and then calling draw() at it,
339 // without exclusive access. Move setupTextOutput(),
340 // e.g. to impltools?
342 ::Point aOutpos;
343 if( !setupTextOutput( *mpVirtualDevice, pOwner, aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
344 return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
346 clip_cairo_from_dev(*mpVirtualDevice);
348 // TODO(F2): What about the offset scalings?
349 pTextLayout->draw(mpCairo, *mpVirtualDevice, aOutpos, viewState, renderState);
352 else
354 ENSURE_ARG_OR_THROW( false,
355 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
358 return uno::Reference< rendering::XCachedPrimitive >(NULL);
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */