LibreOffice Module canvas (master)  1
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 
21 #include <sal/config.h>
22 
23 #include <tools/diagnose_ex.h>
24 
28 #include <com/sun/star/rendering/CompositeOperation.hpp>
29 #include <com/sun/star/rendering/RenderState.hpp>
30 #include <com/sun/star/rendering/TextDirection.hpp>
31 #include <com/sun/star/rendering/ViewState.hpp>
32 #include <comphelper/sequence.hxx>
34 #include <vcl/metric.hxx>
35 #include <vcl/virdev.hxx>
36 
37 #include <canvas/canvastools.hxx>
38 
39 #include "textlayout.hxx"
40 
41 #include <memory>
42 
43 using namespace ::com::sun::star;
44 
45 namespace vclcanvas
46 {
47  namespace
48  {
49  void setupLayoutMode( OutputDevice& rOutDev,
50  sal_Int8 nTextDirection )
51  {
52  // TODO(P3): avoid if already correctly set
53  ComplexTextLayoutFlags nLayoutMode = ComplexTextLayoutFlags::Default;
54  switch( nTextDirection )
55  {
56  case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
57  break;
58  case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
59  nLayoutMode = ComplexTextLayoutFlags::BiDiStrong;
60  break;
61  case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
62  nLayoutMode = ComplexTextLayoutFlags::BiDiRtl;
63  break;
64  case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
65  nLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::BiDiStrong;
66  break;
67  default:
68  break;
69  }
70 
71  // set calculated layout mode. Origin is always the left edge,
72  // as required at the API spec
73  rOutDev.SetLayoutMode( nLayoutMode | ComplexTextLayoutFlags::TextOriginLeft );
74  }
75  }
76 
77  TextLayout::TextLayout( const rendering::StringContext& aText,
78  sal_Int8 nDirection,
79  const CanvasFont::Reference& rFont,
80  const uno::Reference<rendering::XGraphicDevice>& xDevice,
81  const OutDevProviderSharedPtr& rOutDev ) :
83  maText( aText ),
84  maLogicalAdvancements(),
85  mpFont( rFont ),
86  mxDevice( xDevice ),
87  mpOutDevProvider( rOutDev ),
88  mnTextDirection( nDirection )
89  {}
90 
91  void SAL_CALL TextLayout::disposing()
92  {
93  SolarMutexGuard aGuard;
94 
95  mpOutDevProvider.reset();
96  mxDevice.clear();
97  mpFont.clear();
98  }
99 
100  // XTextLayout
101  uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
102  {
103  SolarMutexGuard aGuard;
104 
105  OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
106  ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
107  pVDev->SetFont( mpFont->getVCLFont() );
108 
109  setupLayoutMode( *pVDev, mnTextDirection );
110 
111  const rendering::ViewState aViewState(
112  geometry::AffineMatrix2D(1,0,0, 0,1,0),
113  nullptr);
114 
115  rendering::RenderState aRenderState (
116  geometry::AffineMatrix2D(1,0,0,0,1,0),
117  nullptr,
118  uno::Sequence<double>(4),
119  rendering::CompositeOperation::SOURCE);
120 
121  std::unique_ptr< long []> aOffsets(new long[maLogicalAdvancements.getLength()]);
122  setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
123 
124  std::vector< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
126  if (pVDev->GetTextOutlines(
127  aOutlines,
128  maText.Text,
129  maText.StartPosition,
130  maText.StartPosition,
131  maText.Length,
132  0,
133  aOffsets.get()))
134  {
135  aOutlineSequence.reserve(aOutlines.size());
136  sal_Int32 nIndex (0);
137  for (auto const& outline : aOutlines)
138  {
139  aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
140  mxDevice,
141  outline);
142  }
143  }
144 
145  return comphelper::containerToSequence(aOutlineSequence);
146  }
147 
148  uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
149  {
150  SolarMutexGuard aGuard;
151 
152 
153  OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
154  ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
155  pVDev->SetFont( mpFont->getVCLFont() );
156 
157  setupLayoutMode( *pVDev, mnTextDirection );
158 
159  const rendering::ViewState aViewState(
160  geometry::AffineMatrix2D(1,0,0, 0,1,0),
161  nullptr);
162 
163  rendering::RenderState aRenderState (
164  geometry::AffineMatrix2D(1,0,0,0,1,0),
165  nullptr,
166  uno::Sequence<double>(4),
167  rendering::CompositeOperation::SOURCE);
168 
169  std::unique_ptr< long []> aOffsets(new long[maLogicalAdvancements.getLength()]);
170  setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
171 
172  MetricVector aMetricVector;
173  uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes;
174  if (pVDev->GetGlyphBoundRects(
175  Point(0,0),
176  maText.Text,
177  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
178  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
179  aMetricVector))
180  {
181  aBoundingBoxes.realloc(aMetricVector.size());
182  sal_Int32 nIndex (0);
183  for (auto const& metric : aMetricVector)
184  {
185  aBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
186  metric.getX(),
187  metric.getY(),
188  metric.getX() + metric.getWidth(),
189  metric.getY() + metric.getHeight());
190  }
191  }
192  return aBoundingBoxes;
193  }
194 
195  uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( )
196  {
197  // TODO(F1)
198  return uno::Sequence< geometry::RealRectangle2D >();
199  }
200 
201  uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( )
202  {
203  SolarMutexGuard aGuard;
204 
205  return maLogicalAdvancements;
206  }
207 
208  void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements )
209  {
210  SolarMutexGuard aGuard;
211 
212  ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length,
213  "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
214 
215  maLogicalAdvancements = aAdvancements;
216  }
217 
218  geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
219  {
220  SolarMutexGuard aGuard;
221 
222  if( !mpOutDevProvider )
223  return geometry::RealRectangle2D();
224 
225  OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
226 
227  ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
228  pVDev->SetFont( mpFont->getVCLFont() );
229 
230  // need metrics for Y offset, the XCanvas always renders
231  // relative to baseline
232  const ::FontMetric& aMetric( pVDev->GetFontMetric() );
233 
234  setupLayoutMode( *pVDev, mnTextDirection );
235 
236  const sal_Int32 nAboveBaseline( -aMetric.GetAscent() );
237  const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
238 
239  if( maLogicalAdvancements.hasElements() )
240  {
241  return geometry::RealRectangle2D( 0, nAboveBaseline,
242  maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
243  nBelowBaseline );
244  }
245  else
246  {
247  return geometry::RealRectangle2D( 0, nAboveBaseline,
248  pVDev->GetTextWidth(
249  maText.Text,
250  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
251  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
252  nBelowBaseline );
253  }
254  }
255 
256  double SAL_CALL TextLayout::justify( double )
257  {
258  // TODO(F1)
259  return 0.0;
260  }
261 
262  double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >&,
263  double )
264  {
265  // TODO(F1)
266  return 0.0;
267  }
268 
269  rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& )
270  {
271  // TODO(F1)
272  return rendering::TextHit();
273  }
274 
275  rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32, sal_Bool )
276  {
277  // TODO(F1)
278  return rendering::Caret();
279  }
280 
281  sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32, sal_Int32, sal_Bool )
282  {
283  // TODO(F1)
284  return 0;
285  }
286 
287  uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32, sal_Int32 )
288  {
289  // TODO(F1)
290  return uno::Reference< rendering::XPolyPolygon2D >();
291  }
292 
293  uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32, sal_Int32 )
294  {
295  // TODO(F1)
296  return uno::Reference< rendering::XPolyPolygon2D >();
297  }
298 
299  double SAL_CALL TextLayout::getBaselineOffset( )
300  {
301  // TODO(F1)
302  return 0.0;
303  }
304 
305  sal_Int8 SAL_CALL TextLayout::getMainTextDirection( )
306  {
307  SolarMutexGuard aGuard;
308 
309  return mnTextDirection;
310  }
311 
312  uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( )
313  {
314  SolarMutexGuard aGuard;
315 
316  return mpFont.get();
317  }
318 
319  rendering::StringContext SAL_CALL TextLayout::getText( )
320  {
321  SolarMutexGuard aGuard;
322 
323  return maText;
324  }
325 
326  void TextLayout::draw( OutputDevice& rOutDev,
327  const Point& rOutpos,
328  const rendering::ViewState& viewState,
329  const rendering::RenderState& renderState ) const
330  {
331  SolarMutexGuard aGuard;
332 
333  setupLayoutMode( rOutDev, mnTextDirection );
334 
335  if( maLogicalAdvancements.hasElements() )
336  {
337  // TODO(P2): cache that
338  std::unique_ptr< long []> aOffsets(new long[maLogicalAdvancements.getLength()]);
339  setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState );
340 
341  // TODO(F3): ensure correct length and termination for DX
342  // array (last entry _must_ contain the overall width)
343 
344  rOutDev.DrawTextArray( rOutpos,
345  maText.Text,
346  aOffsets.get(),
347  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
348  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
349  }
350  else
351  {
352  rOutDev.DrawText( rOutpos,
353  maText.Text,
354  ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
355  ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
356  }
357  }
358 
359  namespace
360  {
361  class OffsetTransformer
362  {
363  public:
364  explicit OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) :
365  maMatrix( rMat )
366  {
367  }
368 
369  sal_Int32 operator()( const double& rOffset )
370  {
371  // This is an optimization of the normal rMat*[x,0]
372  // transformation of the advancement vector (in x
373  // direction), followed by a length calculation of the
374  // resulting vector: advancement' =
375  // ||rMat*[x,0]||. Since advancements are vectors, we
376  // can ignore translational components, thus if [x,0],
377  // it follows that rMat*[x,0]=[x',0] holds. Thus, we
378  // just have to calc the transformation of the x
379  // component.
380 
381  // TODO(F2): Handle non-horizontal advancements!
382  return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
383  maMatrix.get(1,0)*rOffset) );
384  }
385 
386  private:
388  };
389  }
390 
391  void TextLayout::setupTextOffsets( long* outputOffsets,
392  const uno::Sequence< double >& inputOffsets,
393  const rendering::ViewState& viewState,
394  const rendering::RenderState& renderState ) const
395  {
396  ENSURE_OR_THROW( outputOffsets!=nullptr,
397  "TextLayout::setupTextOffsets offsets NULL" );
398 
399  ::basegfx::B2DHomMatrix aMatrix;
400 
402  viewState,
403  renderState);
404 
405  // fill integer offsets
406  std::transform( inputOffsets.begin(),
407  inputOffsets.end(),
408  outputOffsets,
409  OffsetTransformer( aMatrix ) );
410  }
411 
412  OUString SAL_CALL TextLayout::getImplementationName()
413  {
414  return "VCLCanvas::TextLayout";
415  }
416 
417  sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName )
418  {
419  return cppu::supportsService( this, ServiceName );
420  }
421 
422  uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames()
423  {
424  return { "com.sun.star.rendering.TextLayout" };
425  }
426 }
427 
428 /* 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)
std::shared_ptr< OutDevProvider > OutDevProviderSharedPtr
::osl::Mutex m_aMutex
std::vector< tools::Rectangle > MetricVector
::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
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
::cppu::WeakComponentImplHelper< css::rendering::XTextLayout, css::lang::XServiceInfo > TextLayout_Base
Definition: textlayout.hxx:39
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
TextLayout(const TextLayout &)=delete
make noncopyable
Target numeric_cast(Source arg)
Cast numeric value into another (numeric) data type.
unsigned char sal_Bool
::basegfx::B2DHomMatrix maMatrix
Definition: textlayout.cxx:387
#define ENSURE_ARG_OR_THROW(c, m)
#define ENSURE_OR_THROW(c, m)
PresenterTheme::SharedFontDescriptor mpFont
Text maText
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
rtl::Reference< CanvasFont > Reference
Definition: canvasfont.hxx:50
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector