1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
25 #include <com/sun/star/rendering/TextDirection.hpp>
26 #include <canvas/canvastools.hxx>
27 #include <basegfx/matrix/b2dhommatrix.hxx>
28 #include <basegfx/numeric/ftools.hxx>
29 #include <cppuhelper/supportsservice.hxx>
30 #include <comphelper/diagnose_ex.hxx>
32 #include <vcl/kernarray.hxx>
33 #include <vcl/metric.hxx>
34 #include <vcl/virdev.hxx>
36 #include "cairo_textlayout.hxx"
38 using namespace ::cairo
;
39 using namespace ::com::sun::star
;
45 void setupLayoutMode( OutputDevice
& rOutDev
,
46 sal_Int8 nTextDirection
)
48 // TODO(P3): avoid if already correctly set
49 vcl::text::ComplexTextLayoutFlags nLayoutMode
= vcl::text::ComplexTextLayoutFlags::Default
;
50 switch( nTextDirection
)
52 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
54 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
55 nLayoutMode
= vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
57 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
58 nLayoutMode
= vcl::text::ComplexTextLayoutFlags::BiDiRtl
;
60 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
61 nLayoutMode
= vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
67 // set calculated layout mode. Origin is always the left edge,
68 // as required at the API spec
69 rOutDev
.SetLayoutMode( nLayoutMode
| vcl::text::ComplexTextLayoutFlags::TextOriginLeft
);
73 TextLayout::TextLayout( rendering::StringContext aText
,
75 sal_Int64
/*nRandomSeed*/,
76 CanvasFont::Reference rFont
,
77 SurfaceProviderRef rRefDevice
) :
78 maText(std::move( aText
)),
79 mpFont(std::move( rFont
)),
80 mpRefDevice(std::move( rRefDevice
)),
81 mnTextDirection( nDirection
)
85 TextLayout::~TextLayout()
89 void TextLayout::disposing(std::unique_lock
<std::mutex
>& /*rGuard*/)
96 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > SAL_CALL
TextLayout::queryTextShapes( )
99 return uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> >();
102 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryInkMeasures( )
105 return uno::Sequence
< geometry::RealRectangle2D
>();
108 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryMeasures( )
111 return uno::Sequence
< geometry::RealRectangle2D
>();
114 uno::Sequence
< double > SAL_CALL
TextLayout::queryLogicalAdvancements( )
116 std::unique_lock
aGuard( m_aMutex
);
118 return maLogicalAdvancements
;
121 void SAL_CALL
TextLayout::applyLogicalAdvancements( const uno::Sequence
< double >& aAdvancements
)
123 std::unique_lock
aGuard( m_aMutex
);
125 if( aAdvancements
.getLength() != maText
.Length
)
127 SAL_WARN("canvas.cairo", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
128 throw lang::IllegalArgumentException(u
"mismatching number of advancements"_ustr
, getXWeak(), 1);
131 maLogicalAdvancements
= aAdvancements
;
134 uno::Sequence
< sal_Bool
> SAL_CALL
TextLayout::queryKashidaPositions( )
136 std::unique_lock
aGuard( m_aMutex
);
138 return maKashidaPositions
;
141 void SAL_CALL
TextLayout::applyKashidaPositions( const uno::Sequence
< sal_Bool
>& aPositions
)
143 std::unique_lock
aGuard( m_aMutex
);
145 if( aPositions
.hasElements() && aPositions
.getLength() != maText
.Length
)
147 SAL_WARN("canvas.cairo", "TextLayout::applyKashidaPositions(): mismatching number of positions" );
148 throw lang::IllegalArgumentException(u
"mismatching number of positions"_ustr
, getXWeak(), 1);
151 maKashidaPositions
= aPositions
;
154 geometry::RealRectangle2D SAL_CALL
TextLayout::queryTextBounds( )
156 std::unique_lock
aGuard( m_aMutex
);
158 OutputDevice
* pOutDev
= mpRefDevice
->getOutputDevice();
160 return geometry::RealRectangle2D();
162 ScopedVclPtrInstance
< VirtualDevice
> pVDev( *pOutDev
);
163 pVDev
->SetFont( mpFont
->getVCLFont() );
165 // need metrics for Y offset, the XCanvas always renders
166 // relative to baseline
167 const ::FontMetric
aMetric( pVDev
->GetFontMetric() );
169 setupLayoutMode( *pVDev
, mnTextDirection
);
171 const sal_Int32
nAboveBaseline( -aMetric
.GetAscent() );
172 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
174 if( maLogicalAdvancements
.hasElements() )
176 return geometry::RealRectangle2D( 0, nAboveBaseline
,
177 maLogicalAdvancements
[ maLogicalAdvancements
.getLength()-1 ],
182 return geometry::RealRectangle2D( 0, nAboveBaseline
,
185 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
186 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) ),
191 double SAL_CALL
TextLayout::justify( double /*nSize*/ )
197 double SAL_CALL
TextLayout::combinedJustify( const uno::Sequence
< uno::Reference
< rendering::XTextLayout
> >& /*aNextLayouts*/,
204 rendering::TextHit SAL_CALL
TextLayout::getTextHit( const geometry::RealPoint2D
& /*aHitPoint*/ )
207 return rendering::TextHit();
210 rendering::Caret SAL_CALL
TextLayout::getCaret( sal_Int32
/*nInsertionIndex*/,
211 sal_Bool
/*bExcludeLigatures*/ )
214 return rendering::Caret();
217 sal_Int32 SAL_CALL
TextLayout::getNextInsertionIndex( sal_Int32
/*nStartIndex*/,
218 sal_Int32
/*nCaretAdvancement*/,
219 sal_Bool
/*bExcludeLigatures*/ )
225 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryVisualHighlighting( sal_Int32
/*nStartIndex*/,
226 sal_Int32
/*nEndIndex*/ )
229 return uno::Reference
< rendering::XPolyPolygon2D
>();
232 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryLogicalHighlighting( sal_Int32
/*nStartIndex*/,
233 sal_Int32
/*nEndIndex*/ )
236 return uno::Reference
< rendering::XPolyPolygon2D
>();
239 double SAL_CALL
TextLayout::getBaselineOffset( )
245 sal_Int8 SAL_CALL
TextLayout::getMainTextDirection( )
247 return mnTextDirection
;
250 uno::Reference
< rendering::XCanvasFont
> SAL_CALL
TextLayout::getFont( )
252 std::unique_lock
aGuard( m_aMutex
);
257 rendering::StringContext SAL_CALL
TextLayout::getText( )
265 * Cairo-based text rendering. Draw text directly on the cairo surface with cairo fonts.
266 * Avoid using VCL VirtualDevices for that, bypassing VCL DrawText functions, when possible
268 * Note: some text effects are not rendered due to lacking generic canvas or cairo canvas
269 * implementation. See issues 92657, 92658, 92659, 92660, 97529
271 void TextLayout::draw( OutputDevice
& rOutDev
,
272 const Point
& rOutpos
,
273 const rendering::ViewState
& viewState
,
274 const rendering::RenderState
& renderState
) const
276 std::unique_lock
aGuard( m_aMutex
);
277 setupLayoutMode( rOutDev
, mnTextDirection
);
279 if (maLogicalAdvancements
.hasElements())
281 KernArray
aOffsets(setupTextOffsets(maLogicalAdvancements
, viewState
, renderState
));
282 std::span
<const sal_Bool
> aKashidaArray(maKashidaPositions
.getConstArray(), maKashidaPositions
.getLength());
284 rOutDev
.DrawTextArray( rOutpos
, maText
.Text
, aOffsets
, aKashidaArray
,
285 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
286 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
290 rOutDev
.DrawText( rOutpos
, maText
.Text
,
291 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
292 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
298 class OffsetTransformer
301 explicit OffsetTransformer( ::basegfx::B2DHomMatrix aMat
) :
302 maMatrix(std::move( aMat
))
306 sal_Int32
operator()( const double& rOffset
)
308 // This is an optimization of the normal rMat*[x,0]
309 // transformation of the advancement vector (in x
310 // direction), followed by a length calculation of the
311 // resulting vector: advancement' =
312 // ||rMat*[x,0]||. Since advancements are vectors, we
313 // can ignore translational components, thus if [x,0],
314 // it follows that rMat*[x,0]=[x',0] holds. Thus, we
315 // just have to calc the transformation of the x
318 // TODO(F2): Handle non-horizontal advancements!
319 return ::basegfx::fround( hypot(maMatrix
.get(0,0)*rOffset
,
320 maMatrix
.get(1,0)*rOffset
) );
324 ::basegfx::B2DHomMatrix maMatrix
;
328 KernArray
TextLayout::setupTextOffsets(
329 const uno::Sequence
< double >& inputOffsets
,
330 const rendering::ViewState
& viewState
,
331 const rendering::RenderState
& renderState
) const
333 ::basegfx::B2DHomMatrix aMatrix
;
335 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
339 // fill integer offsets
340 KernArray outputOffsets
;
341 OffsetTransformer
aTransform(aMatrix
);
342 std::for_each(inputOffsets
.begin(), inputOffsets
.end(),
343 [&outputOffsets
, &aTransform
](double n
) {outputOffsets
.push_back(aTransform(n
)); } );
344 return outputOffsets
;
347 OUString SAL_CALL
TextLayout::getImplementationName()
349 return u
"CairoCanvas::TextLayout"_ustr
;
352 sal_Bool SAL_CALL
TextLayout::supportsService( const OUString
& ServiceName
)
354 return cppu::supportsService( this, ServiceName
);
357 uno::Sequence
< OUString
> SAL_CALL
TextLayout::getSupportedServiceNames()
359 return { u
"com.sun.star.rendering.TextLayout"_ustr
};
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */