Move setting of LD_LIBRARY_PATH closer to invocation of cppunittester
[LibreOffice.git] / canvas / source / vcl / textlayout.cxx
blob024edd1f8e678d6f42b6c45b7d84926bb3954e8f
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 .
21 #include <sal/config.h>
23 #include <comphelper/diagnose_ex.hxx>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/numeric/ftools.hxx>
26 #include <basegfx/utils/canvastools.hxx>
27 #include <com/sun/star/rendering/CompositeOperation.hpp>
28 #include <com/sun/star/rendering/RenderState.hpp>
29 #include <com/sun/star/rendering/TextDirection.hpp>
30 #include <com/sun/star/rendering/ViewState.hpp>
31 #include <comphelper/sequence.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <utility>
34 #include <vcl/kernarray.hxx>
35 #include <vcl/metric.hxx>
36 #include <vcl/virdev.hxx>
38 #include <canvas/canvastools.hxx>
40 #include "textlayout.hxx"
42 #include <memory>
44 using namespace ::com::sun::star;
46 namespace vclcanvas
48 namespace
50 void setupLayoutMode( OutputDevice& rOutDev,
51 sal_Int8 nTextDirection )
53 // TODO(P3): avoid if already correctly set
54 vcl::text::ComplexTextLayoutFlags nLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
55 switch( nTextDirection )
57 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
58 break;
59 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
60 nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiStrong;
61 break;
62 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
63 nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl;
64 break;
65 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
66 nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
67 break;
68 default:
69 break;
72 // set calculated layout mode. Origin is always the left edge,
73 // as required at the API spec
74 rOutDev.SetLayoutMode( nLayoutMode | vcl::text::ComplexTextLayoutFlags::TextOriginLeft );
78 TextLayout::TextLayout( rendering::StringContext aText,
79 sal_Int8 nDirection,
80 CanvasFont::Reference rFont,
81 uno::Reference<rendering::XGraphicDevice> xDevice,
82 OutDevProviderSharedPtr xOutDev ) :
83 maText(std::move( aText )),
84 mpFont(std::move( rFont )),
85 mxDevice(std::move( xDevice )),
86 mpOutDevProvider(std::move( xOutDev )),
87 mnTextDirection( nDirection )
90 void TextLayout::disposing(std::unique_lock<std::mutex>& rGuard)
92 rGuard.unlock();
94 SolarMutexGuard aGuard;
95 mpOutDevProvider.reset();
96 mxDevice.clear();
97 mpFont.clear();
99 rGuard.lock();
102 // XTextLayout
103 uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
105 SolarMutexGuard aGuard;
107 OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
108 ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
109 pVDev->SetFont( mpFont->getVCLFont() );
111 setupLayoutMode( *pVDev, mnTextDirection );
113 const rendering::ViewState aViewState(
114 geometry::AffineMatrix2D(1,0,0, 0,1,0),
115 nullptr);
117 rendering::RenderState aRenderState (
118 geometry::AffineMatrix2D(1,0,0,0,1,0),
119 nullptr,
120 uno::Sequence<double>(4),
121 rendering::CompositeOperation::SOURCE);
123 KernArray aOffsets(setupTextOffsets(maLogicalAdvancements, aViewState, aRenderState));
124 std::span<const sal_Bool> aKashidaArray(maKashidaPositions.getArray(), maKashidaPositions.getLength());
126 std::vector< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
127 ::basegfx::B2DPolyPolygonVector aOutlines;
128 if (pVDev->GetTextOutlines(
129 aOutlines,
130 maText.Text,
131 maText.StartPosition,
132 maText.StartPosition,
133 maText.Length,
135 aOffsets,
136 aKashidaArray))
138 aOutlineSequence.reserve(aOutlines.size());
139 sal_Int32 nIndex (0);
140 for (auto const& outline : aOutlines)
142 aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
143 mxDevice,
144 outline);
148 return comphelper::containerToSequence(aOutlineSequence);
151 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
153 SolarMutexGuard aGuard;
156 OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
157 ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
158 pVDev->SetFont( mpFont->getVCLFont() );
160 setupLayoutMode( *pVDev, mnTextDirection );
162 const rendering::ViewState aViewState(
163 geometry::AffineMatrix2D(1,0,0, 0,1,0),
164 nullptr);
166 rendering::RenderState aRenderState (
167 geometry::AffineMatrix2D(1,0,0,0,1,0),
168 nullptr,
169 uno::Sequence<double>(4),
170 rendering::CompositeOperation::SOURCE);
172 std::vector< ::tools::Rectangle > 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))
181 aBoundingBoxes.realloc(aMetricVector.size());
182 auto pBoundingBoxes = aBoundingBoxes.getArray();
183 sal_Int32 nIndex (0);
184 for (auto const& metric : aMetricVector)
186 pBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
187 metric.Left(),
188 metric.Top(),
189 metric.Right(),
190 metric.Bottom());
193 return aBoundingBoxes;
196 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( )
198 // TODO(F1)
199 return uno::Sequence< geometry::RealRectangle2D >();
202 uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( )
204 SolarMutexGuard aGuard;
206 return maLogicalAdvancements;
209 void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements )
211 SolarMutexGuard aGuard;
213 ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length,
214 "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
216 maLogicalAdvancements = aAdvancements;
219 uno::Sequence< sal_Bool > SAL_CALL TextLayout::queryKashidaPositions( )
221 SolarMutexGuard aGuard;
223 return maKashidaPositions;
226 void SAL_CALL TextLayout::applyKashidaPositions( const uno::Sequence< sal_Bool >& aPositions )
228 SolarMutexGuard aGuard;
230 ENSURE_ARG_OR_THROW( !aPositions.hasElements() || aPositions.getLength() == maText.Length,
231 "TextLayout::applyKashidaPositions(): mismatching number of positions" );
233 maKashidaPositions = aPositions;
236 geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
238 SolarMutexGuard aGuard;
240 if( !mpOutDevProvider )
241 return geometry::RealRectangle2D();
243 OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
245 ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
246 pVDev->SetFont( mpFont->getVCLFont() );
248 // need metrics for Y offset, the XCanvas always renders
249 // relative to baseline
250 const ::FontMetric aMetric( pVDev->GetFontMetric() );
252 setupLayoutMode( *pVDev, mnTextDirection );
254 const sal_Int32 nAboveBaseline( -aMetric.GetAscent() );
255 const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
257 if( maLogicalAdvancements.hasElements() )
259 return geometry::RealRectangle2D( 0, nAboveBaseline,
260 maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
261 nBelowBaseline );
263 else
265 return geometry::RealRectangle2D( 0, nAboveBaseline,
266 pVDev->GetTextWidth(
267 maText.Text,
268 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
269 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
270 nBelowBaseline );
274 double SAL_CALL TextLayout::justify( double )
276 // TODO(F1)
277 return 0.0;
280 double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >&,
281 double )
283 // TODO(F1)
284 return 0.0;
287 rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& )
289 // TODO(F1)
290 return rendering::TextHit();
293 rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32, sal_Bool )
295 // TODO(F1)
296 return rendering::Caret();
299 sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32, sal_Int32, sal_Bool )
301 // TODO(F1)
302 return 0;
305 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32, sal_Int32 )
307 // TODO(F1)
308 return uno::Reference< rendering::XPolyPolygon2D >();
311 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32, sal_Int32 )
313 // TODO(F1)
314 return uno::Reference< rendering::XPolyPolygon2D >();
317 double SAL_CALL TextLayout::getBaselineOffset( )
319 // TODO(F1)
320 return 0.0;
323 sal_Int8 SAL_CALL TextLayout::getMainTextDirection( )
325 return mnTextDirection;
328 uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( )
330 SolarMutexGuard aGuard;
332 return mpFont;
335 rendering::StringContext SAL_CALL TextLayout::getText( )
337 return maText;
340 void TextLayout::draw( OutputDevice& rOutDev,
341 const Point& rOutpos,
342 const rendering::ViewState& viewState,
343 const rendering::RenderState& renderState ) const
345 SolarMutexGuard aGuard;
347 #ifdef _WIN32
348 // tdf#147999
349 // On Windows we get the wrong font width for fallback fonts unless we setup again here.
350 vcl::Font aFont(rOutDev.GetFont());
351 tools::setupFontWidth(mpFont->getFontMatrix(), aFont, rOutDev);
352 rOutDev.SetFont(aFont);
353 #endif
355 setupLayoutMode( rOutDev, mnTextDirection );
357 if( maLogicalAdvancements.hasElements() )
359 // TODO(P2): cache that
360 KernArray aOffsets(setupTextOffsets(maLogicalAdvancements, viewState, renderState));
361 std::span<const sal_Bool> aKashidaArray(maKashidaPositions.getConstArray(), maKashidaPositions.getLength());
363 // TODO(F3): ensure correct length and termination for DX
364 // array (last entry _must_ contain the overall width)
366 rOutDev.DrawTextArray( rOutpos,
367 maText.Text,
368 aOffsets,
369 aKashidaArray,
370 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
371 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
373 else
375 rOutDev.DrawText( rOutpos,
376 maText.Text,
377 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
378 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
382 namespace
384 class OffsetTransformer
386 public:
387 explicit OffsetTransformer( ::basegfx::B2DHomMatrix aMat ) :
388 maMatrix(std::move( aMat ))
392 sal_Int32 operator()( const double& rOffset )
394 // This is an optimization of the normal rMat*[x,0]
395 // transformation of the advancement vector (in x
396 // direction), followed by a length calculation of the
397 // resulting vector: advancement' =
398 // ||rMat*[x,0]||. Since advancements are vectors, we
399 // can ignore translational components, thus if [x,0],
400 // it follows that rMat*[x,0]=[x',0] holds. Thus, we
401 // just have to calc the transformation of the x
402 // component.
404 // TODO(F2): Handle non-horizontal advancements!
405 return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
406 maMatrix.get(1,0)*rOffset) );
409 private:
410 ::basegfx::B2DHomMatrix maMatrix;
414 KernArray TextLayout::setupTextOffsets(
415 const uno::Sequence< double >& inputOffsets,
416 const rendering::ViewState& viewState,
417 const rendering::RenderState& renderState ) const
419 ::basegfx::B2DHomMatrix aMatrix;
421 ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
422 viewState,
423 renderState);
425 // fill integer offsets
426 KernArray outputOffsets;
427 OffsetTransformer aTransform(aMatrix);
428 std::for_each(inputOffsets.begin(), inputOffsets.end(),
429 [&outputOffsets, &aTransform](double n) {outputOffsets.push_back(aTransform(n)); } );
430 return outputOffsets;
433 OUString SAL_CALL TextLayout::getImplementationName()
435 return u"VCLCanvas::TextLayout"_ustr;
438 sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName )
440 return cppu::supportsService( this, ServiceName );
443 uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames()
445 return { u"com.sun.star.rendering.TextLayout"_ustr };
449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */