LibreOffice Module canvas (master)  1
cairo_textlayout.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 #include <sal/log.hxx>
22 
23 #include <math.h>
24 #include <memory>
25 
26 #include <com/sun/star/rendering/TextDirection.hpp>
27 #include <canvas/canvastools.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <vcl/metric.hxx>
33 #include <vcl/sysdata.hxx>
34 #include <vcl/virdev.hxx>
35 
36 
37 #include "cairo_textlayout.hxx"
38 
39 #include <cairo.h>
40 
41 #if defined CAIRO_HAS_FT_FONT
42 # include <cairo-ft.h>
43 #else
44 # error Native API needed.
45 #endif
46 
47 using namespace ::cairo;
48 using namespace ::com::sun::star;
49 
50 namespace cairocanvas
51 {
52  namespace
53  {
54  void setupLayoutMode( OutputDevice& rOutDev,
55  sal_Int8 nTextDirection )
56  {
57  // TODO(P3): avoid if already correctly set
58  ComplexTextLayoutFlags nLayoutMode = ComplexTextLayoutFlags::Default;
59  switch( nTextDirection )
60  {
61  case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
62  break;
63  case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
64  nLayoutMode = ComplexTextLayoutFlags::BiDiStrong;
65  break;
66  case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
67  nLayoutMode = ComplexTextLayoutFlags::BiDiRtl;
68  break;
69  case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
70  nLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::BiDiStrong;
71  break;
72  default:
73  break;
74  }
75 
76  // set calculated layout mode. Origin is always the left edge,
77  // as required at the API spec
78  rOutDev.SetLayoutMode( nLayoutMode | ComplexTextLayoutFlags::TextOriginLeft );
79  }
80  }
81 
82  TextLayout::TextLayout( const rendering::StringContext& aText,
83  sal_Int8 nDirection,
84  sal_Int64 /*nRandomSeed*/,
85  const CanvasFont::Reference& rFont,
86  const SurfaceProviderRef& rRefDevice ) :
88  maText( aText ),
89  maLogicalAdvancements(),
90  mpFont( rFont ),
91  mpRefDevice( rRefDevice ),
92  mnTextDirection( nDirection )
93  {
94  }
95 
96  TextLayout::~TextLayout()
97  {
98  }
99 
100  void SAL_CALL TextLayout::disposing()
101  {
102  ::osl::MutexGuard aGuard( m_aMutex );
103 
104  mpFont.clear();
105  mpRefDevice.clear();
106  }
107 
108  // XTextLayout
109  uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
110  {
111  // TODO
112  return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >();
113  }
114 
115  uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
116  {
117  // TODO
118  return uno::Sequence< geometry::RealRectangle2D >();
119  }
120 
121  uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( )
122  {
123  // TODO
124  return uno::Sequence< geometry::RealRectangle2D >();
125  }
126 
127  uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( )
128  {
129  ::osl::MutexGuard aGuard( m_aMutex );
130 
131  return maLogicalAdvancements;
132  }
133 
134  void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements )
135  {
136  ::osl::MutexGuard aGuard( m_aMutex );
137 
138  if( aAdvancements.getLength() != maText.Length )
139  {
140  SAL_WARN("canvas.cairo", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
141  throw lang::IllegalArgumentException();
142  }
143 
144  maLogicalAdvancements = aAdvancements;
145  }
146 
147  geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
148  {
149  ::osl::MutexGuard aGuard( m_aMutex );
150 
151  OutputDevice* pOutDev = mpRefDevice->getOutputDevice();
152  if( !pOutDev )
153  return geometry::RealRectangle2D();
154 
155  ScopedVclPtrInstance< VirtualDevice > pVDev( *pOutDev );
156  pVDev->SetFont( mpFont->getVCLFont() );
157 
158  // need metrics for Y offset, the XCanvas always renders
159  // relative to baseline
160  const ::FontMetric& aMetric( pVDev->GetFontMetric() );
161 
162  setupLayoutMode( *pVDev, mnTextDirection );
163 
164  const sal_Int32 nAboveBaseline( -aMetric.GetAscent() );
165  const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
166 
167  if( maLogicalAdvancements.hasElements() )
168  {
169  return geometry::RealRectangle2D( 0, nAboveBaseline,
170  maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
171  nBelowBaseline );
172  }
173  else
174  {
175  return geometry::RealRectangle2D( 0, nAboveBaseline,
176  pVDev->GetTextWidth(
177  maText.Text,
178  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
179  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
180  nBelowBaseline );
181  }
182  }
183 
184  double SAL_CALL TextLayout::justify( double /*nSize*/ )
185  {
186  // TODO
187  return 0.0;
188  }
189 
190  double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/,
191  double /*nSize*/ )
192  {
193  // TODO
194  return 0.0;
195  }
196 
197  rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ )
198  {
199  // TODO
200  return rendering::TextHit();
201  }
202 
203  rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/,
204  sal_Bool /*bExcludeLigatures*/ )
205  {
206  // TODO
207  return rendering::Caret();
208  }
209 
210  sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/,
211  sal_Int32 /*nCaretAdvancement*/,
212  sal_Bool /*bExcludeLigatures*/ )
213  {
214  // TODO
215  return 0;
216  }
217 
218  uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/,
219  sal_Int32 /*nEndIndex*/ )
220  {
221  // TODO
222  return uno::Reference< rendering::XPolyPolygon2D >();
223  }
224 
225  uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/,
226  sal_Int32 /*nEndIndex*/ )
227  {
228  // TODO
229  return uno::Reference< rendering::XPolyPolygon2D >();
230  }
231 
232  double SAL_CALL TextLayout::getBaselineOffset( )
233  {
234  // TODO
235  return 0.0;
236  }
237 
238  sal_Int8 SAL_CALL TextLayout::getMainTextDirection( )
239  {
240  ::osl::MutexGuard aGuard( m_aMutex );
241 
242  return mnTextDirection;
243  }
244 
245  uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( )
246  {
247  ::osl::MutexGuard aGuard( m_aMutex );
248 
249  return mpFont.get();
250  }
251 
252  rendering::StringContext SAL_CALL TextLayout::getText( )
253  {
254  ::osl::MutexGuard aGuard( m_aMutex );
255 
256  return maText;
257  }
258 
268  void TextLayout::draw( OutputDevice& rOutDev,
269  const Point& rOutpos,
270  const rendering::ViewState& viewState,
271  const rendering::RenderState& renderState ) const
272  {
273  ::osl::MutexGuard aGuard( m_aMutex );
274  setupLayoutMode( rOutDev, mnTextDirection );
275 
276  std::unique_ptr< long []> aOffsets(new long[maLogicalAdvancements.getLength()]);
277 
278  if( maLogicalAdvancements.hasElements() )
279  setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState );
280 
281  if (maLogicalAdvancements.hasElements())
282  {
283  rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets.get(),
284  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
285  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
286  }
287  else
288  {
289  rOutDev.DrawText( rOutpos, maText.Text,
290  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
291  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
292  }
293  }
294 
295  namespace
296  {
297  class OffsetTransformer
298  {
299  public:
300  explicit OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) :
301  maMatrix( rMat )
302  {
303  }
304 
305  sal_Int32 operator()( const double& rOffset )
306  {
307  // This is an optimization of the normal rMat*[x,0]
308  // transformation of the advancement vector (in x
309  // direction), followed by a length calculation of the
310  // resulting vector: advancement' =
311  // ||rMat*[x,0]||. Since advancements are vectors, we
312  // can ignore translational components, thus if [x,0],
313  // it follows that rMat*[x,0]=[x',0] holds. Thus, we
314  // just have to calc the transformation of the x
315  // component.
316 
317  // TODO(F2): Handle non-horizontal advancements!
318  return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
319  maMatrix.get(1,0)*rOffset) );
320  }
321 
322  private:
324  };
325  }
326 
327  void TextLayout::setupTextOffsets( long* outputOffsets,
328  const uno::Sequence< double >& inputOffsets,
329  const rendering::ViewState& viewState,
330  const rendering::RenderState& renderState ) const
331  {
332  ENSURE_OR_THROW( outputOffsets!=nullptr,
333  "TextLayout::setupTextOffsets offsets NULL" );
334 
335  ::basegfx::B2DHomMatrix aMatrix;
336 
338  viewState,
339  renderState);
340 
341  // fill integer offsets
342  std::transform( inputOffsets.begin(),
343  inputOffsets.end(),
344  outputOffsets,
345  OffsetTransformer( aMatrix ) );
346  }
347 
348  OUString SAL_CALL TextLayout::getImplementationName()
349  {
350  return "CairoCanvas::TextLayout";
351  }
352 
353  sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName )
354  {
355  return cppu::supportsService( this, ServiceName );
356  }
357 
358  uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames()
359  {
360  return { "com.sun.star.rendering.TextLayout" };
361  }
362 }
363 
364 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, MetricVector *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
osl::Mutex m_aMutex
TextLayout(const TextLayout &)=delete
make noncopyable
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
signed char sal_Int8
void SetLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
rtl::Reference< CanvasFont > Reference
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
::cppu::WeakComponentImplHelper< css::rendering::XTextLayout, css::lang::XServiceInfo > TextLayout_Base
void DrawTextArray(const Point &rStartPt, const OUString &rStr, const long *pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, SalLayoutFlags flags=SalLayoutFlags::NONE, const SalLayoutGlyphs *pLayoutCache=nullptr)
ComplexTextLayoutFlags
Target numeric_cast(Source arg)
Cast numeric value into another (numeric) data type.
unsigned char sal_Bool
std::unique_ptr< vcl::Font > mpFont
#define ENSURE_OR_THROW(c, m)
Text maText
::basegfx::B2DHomMatrix maMatrix
::rtl::Reference< SurfaceProvider > SurfaceProviderRef
#define SAL_WARN(area, stream)