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