LibreOffice Module canvas (master)  1
cairo_canvashelper_text.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <com/sun/star/rendering/TextDirection.hpp>
23 
25 #include <tools/diagnose_ex.h>
26 #include <vcl/canvastools.hxx>
27 #include <vcl/metric.hxx>
28 #include <vcl/virdev.hxx>
29 
30 #include <canvas/canvastools.hxx>
31 #include <verifyinput.hxx>
32 #include <cairo.h>
33 
34 #include "cairo_canvasfont.hxx"
35 #include "cairo_canvashelper.hxx"
36 #include "cairo_textlayout.hxx"
37 
38 using namespace ::cairo;
39 using namespace ::com::sun::star;
40 
41 namespace cairocanvas
42 {
43  uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* ,
44  const rendering::FontRequest& fontRequest,
45  const uno::Sequence< beans::PropertyValue >& extraFontProperties,
46  const geometry::Matrix2D& fontMatrix )
47  {
48  return uno::Reference< rendering::XCanvasFont >( new CanvasFont( fontRequest, extraFontProperties, fontMatrix, mpSurfaceProvider ));
49  }
50 
51  uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* ,
52  const rendering::FontInfo& /*aFilter*/,
53  const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
54  {
55  // TODO
56  return uno::Sequence< rendering::FontInfo >();
57  }
58 
59  static bool
60  setupFontTransform( ::OutputDevice const & rOutDev,
61  ::Point& o_rPoint,
62  vcl::Font& io_rVCLFont,
63  const rendering::ViewState& rViewState,
64  const rendering::RenderState& rRenderState )
65  {
67 
69  rViewState,
70  rRenderState);
71 
72  ::basegfx::B2DTuple aScale;
73  ::basegfx::B2DTuple aTranslate;
74  double nRotate, nShearX;
75 
76  aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
77 
78  // query font metric _before_ tampering with width and height
79  if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
80  {
81  // retrieve true font width
82  const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetAverageFontWidth() );
83 
84  const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
85 
86  if( !nScaledFontWidth )
87  {
88  // scale is smaller than one pixel - disable text
89  // output altogether
90  return false;
91  }
92 
93  io_rVCLFont.SetAverageFontWidth( nScaledFontWidth );
94  }
95 
96  if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
97  {
98  const sal_Int32 nFontHeight( io_rVCLFont.GetFontHeight() );
99  io_rVCLFont.SetFontHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
100  }
101 
102  io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) );
103 
104  // TODO(F2): Missing functionality in VCL: shearing
105  o_rPoint.setX( ::basegfx::fround(aTranslate.getX()) );
106  o_rPoint.setY( ::basegfx::fround(aTranslate.getY()) );
107 
108  return true;
109  }
110 
111  static int
113  const rendering::XCanvas* pOwner,
114  const rendering::ViewState& viewState,
115  const rendering::RenderState& renderState )
116  {
117  ::canvas::tools::verifyInput( renderState,
118  OSL_THIS_FUNC,
119  const_cast<rendering::XCanvas*>(pOwner), // only for refcount
120  2,
121  3 /* text */ );
122 
123  int nTransparency(0);
124 
125  // TODO(P2): Don't change clipping all the time, maintain current clip
126  // state and change only when update is necessary
127  ::canvas::tools::clipOutDev(viewState, renderState, rOutDev);
128 
129  Color aColor( COL_WHITE );
130 
131  if( renderState.DeviceColor.getLength() > 2 )
132  {
133  aColor = vcl::unotools::stdColorSpaceSequenceToColor( renderState.DeviceColor );
134  }
135 
136  // extract alpha, and make color opaque
137  // afterwards. Otherwise, OutputDevice won't draw anything
138  nTransparency = aColor.GetTransparency();
139  aColor.SetTransparency(0);
140 
141  rOutDev.SetTextColor( aColor );
142 
143  return nTransparency;
144  }
145 
146  namespace {
147 
148  class DeviceSettingsGuard
149  {
150  private:
154  public:
155  DeviceSettingsGuard(OutputDevice *pVirtualDevice, cairo_t *pCairo)
156  : mpVirtualDevice(pVirtualDevice)
157  , mpCairo(pCairo)
158  , mbMappingWasEnabled(mpVirtualDevice->IsMapModeEnabled())
159  {
160  cairo_save(mpCairo);
161  mpVirtualDevice->Push();
162  mpVirtualDevice->EnableMapMode(false);
163  }
164 
165  ~DeviceSettingsGuard()
166  {
167  mpVirtualDevice->EnableMapMode(mbMappingWasEnabled);
168  mpVirtualDevice->Pop();
169  cairo_restore(mpCairo);
170  }
171  };
172 
173  }
174 
175  static bool setupTextOutput( OutputDevice& rOutDev,
176  const rendering::XCanvas* pOwner,
177  ::Point& o_rOutPos,
178  const rendering::ViewState& viewState,
179  const rendering::RenderState& renderState,
180  const uno::Reference< rendering::XCanvasFont >& xFont )
181  {
182  setupOutDevState( rOutDev, pOwner, viewState, renderState );
183 
184  CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
185 
186  ENSURE_ARG_OR_THROW( pFont,
187  "CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
188 
189  vcl::Font aVCLFont = pFont->getVCLFont();
190 
191  Color aColor( COL_BLACK );
192 
193  if( renderState.DeviceColor.getLength() > 2 )
194  {
195  aColor = vcl::unotools::stdColorSpaceSequenceToColor(renderState.DeviceColor );
196  }
197 
198  // setup font color
199  aVCLFont.SetColor( aColor );
200  aVCLFont.SetFillColor( aColor );
201 
202  // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
203  if( !setupFontTransform( rOutDev, o_rOutPos, aVCLFont, viewState, renderState ) )
204  return false;
205 
206  rOutDev.SetFont( aVCLFont );
207 
208  return true;
209  }
210 
211  //set the clip of the rOutDev to the cairo surface
213  {
214  vcl::Region aRegion(rOutDev.GetClipRegion());
215  if (!aRegion.IsEmpty() && !aRegion.IsNull())
216  {
217  doPolyPolygonImplementation(aRegion.GetAsB2DPolyPolygon(), Clip, mpCairo.get(),
218  nullptr, mpSurfaceProvider, rendering::FillRule_EVEN_ODD);
219  }
220  }
221 
222  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* pOwner,
223  const rendering::StringContext& text,
224  const uno::Reference< rendering::XCanvasFont >& xFont,
225  const rendering::ViewState& viewState,
226  const rendering::RenderState& renderState,
227  sal_Int8 textDirection )
228  {
229 #ifdef CAIRO_CANVAS_PERF_TRACE
230  struct timespec aTimer;
231  mxDevice->startPerfTrace( &aTimer );
232 #endif
233 
234  ENSURE_ARG_OR_THROW( xFont.is(),
235  "CanvasHelper::drawText(): font is NULL");
236 
237  if( !mpVirtualDevice )
238  mpVirtualDevice = mpSurface->createVirtualDevice();
239 
240  if( mpVirtualDevice )
241  {
242  DeviceSettingsGuard aGuard(mpVirtualDevice.get(), mpCairo.get());
243 
244 #if defined CAIRO_HAS_WIN32_SURFACE
245  // FIXME: Some kind of work-around...
246  cairo_rectangle (mpCairo.get(), 0, 0, 0, 0);
247  cairo_fill(mpCairo.get());
248 #endif
249  ::Point aOutpos;
250  if( !setupTextOutput( *mpVirtualDevice, pOwner, aOutpos, viewState, renderState, xFont ) )
251  return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
252 
253  // change text direction and layout mode
254  ComplexTextLayoutFlags nLayoutMode(ComplexTextLayoutFlags::Default);
255  switch( textDirection )
256  {
257  case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
258  case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
259  nLayoutMode |= ComplexTextLayoutFlags::BiDiStrong;
260  nLayoutMode |= ComplexTextLayoutFlags::TextOriginLeft;
261  break;
262 
263  case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
264  nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl;
265  [[fallthrough]];
266  case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
267  nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::BiDiStrong;
268  nLayoutMode |= ComplexTextLayoutFlags::TextOriginRight;
269  break;
270  }
271 
272  // TODO(F2): alpha
273  mpVirtualDevice->SetLayoutMode( nLayoutMode );
274 
275  clip_cairo_from_dev(*mpVirtualDevice);
276 
277  rtl::Reference pTextLayout( new TextLayout(text, textDirection, 0, CanvasFont::Reference(dynamic_cast< CanvasFont* >( xFont.get() )), mpSurfaceProvider) );
278  pTextLayout->draw(mpCairo, *mpVirtualDevice, aOutpos, viewState, renderState);
279  }
280 
281  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
282  }
283 
284  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* pOwner,
285  const uno::Reference< rendering::XTextLayout >& xLayoutedText,
286  const rendering::ViewState& viewState,
287  const rendering::RenderState& renderState )
288  {
289  ENSURE_ARG_OR_THROW( xLayoutedText.is(),
290  "CanvasHelper::drawTextLayout(): layout is NULL");
291 
292  TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
293 
294  if( pTextLayout )
295  {
296  if( !mpVirtualDevice )
297  mpVirtualDevice = mpSurface->createVirtualDevice();
298 
299  if( mpVirtualDevice )
300  {
301  DeviceSettingsGuard aGuard(mpVirtualDevice.get(), mpCairo.get());
302 
303 #if defined CAIRO_HAS_WIN32_SURFACE
304  // FIXME: Some kind of work-around...
305  cairo_rectangle(mpCairo.get(), 0, 0, 0, 0);
306  cairo_fill(mpCairo.get());
307 #endif
308  // TODO(T3): Race condition. We're taking the font
309  // from xLayoutedText, and then calling draw() at it,
310  // without exclusive access. Move setupTextOutput(),
311  // e.g. to impltools?
312 
313  ::Point aOutpos;
314  if( !setupTextOutput( *mpVirtualDevice, pOwner, aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
315  return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
316 
317  clip_cairo_from_dev(*mpVirtualDevice);
318 
319  // TODO(F2): What about the offset scalings?
320  pTextLayout->draw(mpCairo, *mpVirtualDevice, aOutpos, viewState, renderState);
321  }
322  }
323  else
324  {
325  ENSURE_ARG_OR_THROW( false,
326  "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
327  }
328 
329  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
330  }
331 
332 }
333 
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
vcl::Region GetClipRegion() const
bool mbMappingWasEnabled
void SetFillColor(const Color &)
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
void SetAverageFontWidth(long nWidth)
void SetTransparency(sal_uInt8 nTransparency)
long GetFontHeight() const
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
signed char sal_Int8
vcl::Font const & getVCLFont() const
sal_uInt8 GetTransparency() const
void verifyInput(const geometry::RealPoint2D &rPoint, const char *pStr, const uno::Reference< uno::XInterface > &xIf,::sal_Int16 nArgPos)
Definition: verifyinput.cxx:51
double getX() const
double getY() const
void SetLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)
void EnableMapMode(bool bEnable=true)
Color stdColorSpaceSequenceToColor(const uno::Sequence< double > &rColor)
void SetOrientation(short nLineOrientation)
css::uno::Reference< css::rendering::XCachedPrimitive > drawTextLayout(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XTextLayout > &laidOutText, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
rtl::Reference< CanvasFont > Reference
void clip_cairo_from_dev(::OutputDevice const &rOutDev)
VclPtr< OutputDevice > mpVirtualDevice
FontMetric GetFontMetric() const
B2IRange fround(const B2DRange &rRange)
css::uno::Sequence< css::rendering::FontInfo > queryAvailableFonts(const css::rendering::XCanvas *pCanvas, const css::rendering::FontInfo &aFilter, const css::uno::Sequence< css::beans::PropertyValue > &aFontProperties)
void clipOutDev(const rendering::ViewState &viewState, const rendering::RenderState &renderState, OutputDevice &rOutDev, OutputDevice *p2ndOutDev)
void doPolyPolygonImplementation(const ::basegfx::B2DPolyPolygon &aPolyPolygon, Operation aOperation, cairo_t *pCairo, const uno::Sequence< rendering::Texture > *pTextures, const SurfaceProviderRef &pDevice, rendering::FillRule eFillrule)
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
ComplexTextLayoutFlags
void SetTextColor(const Color &rColor)
css::uno::Reference< css::rendering::XCanvasFont > createFont(const css::rendering::XCanvas *pCanvas, const css::rendering::FontRequest &fontRequest, const css::uno::Sequence< css::beans::PropertyValue > &extraFontProperties, const css::geometry::Matrix2D &fontMatrix)
static int setupOutDevState(OutputDevice &rOutDev, const rendering::XCanvas *pOwner, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
struct _cairo cairo_t
#define ENSURE_ARG_OR_THROW(c, m)
void SetColor(const Color &)
::cairo::SurfaceSharedPtr mpSurface
SurfaceProvider * mpSurfaceProvider
Surface provider.
cairo_t * mpCairo
static bool setupFontTransform(::OutputDevice const &rOutDev,::Point &o_rPoint, vcl::Font &io_rVCLFont, const rendering::ViewState &rViewState, const rendering::RenderState &rRenderState)
void SetFont(const vcl::Font &rNewFont)
void SetFontHeight(long nHeight)
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
static bool setupTextOutput(OutputDevice &rOutDev, const rendering::XCanvas *pOwner,::Point &o_rOutPos, const rendering::ViewState &viewState, const rendering::RenderState &renderState, const uno::Reference< rendering::XCanvasFont > &xFont)
OutputDevice * get() const
long GetAverageFontWidth() const
void Push(PushFlags nFlags=PushFlags::ALL)
css::uno::Reference< css::rendering::XCachedPrimitive > drawText(const css::rendering::XCanvas *pCanvas, const css::rendering::StringContext &text, const css::uno::Reference< css::rendering::XCanvasFont > &xFont, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, sal_Int8 textDirection)