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>
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>
33#include <utility>
34#include <vcl/metric.hxx>
35#include <vcl/virdev.hxx>
36
38
39#include "textlayout.hxx"
40
41#include <memory>
42
43using namespace ::com::sun::star;
44
45namespace vclcanvas
46{
47 namespace
48 {
49 void setupLayoutMode( OutputDevice& rOutDev,
50 sal_Int8 nTextDirection )
51 {
52 // TODO(P3): avoid if already correctly set
54 switch( nTextDirection )
55 {
56 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
57 break;
58 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
60 break;
61 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
63 break;
64 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
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
74 }
75 }
76
77 TextLayout::TextLayout( rendering::StringContext aText,
78 sal_Int8 nDirection,
80 uno::Reference<rendering::XGraphicDevice> xDevice,
81 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 )
88 {}
89
90 void SAL_CALL TextLayout::disposing()
91 {
92 SolarMutexGuard aGuard;
93
94 mpOutDevProvider.reset();
95 mxDevice.clear();
96 mpFont.clear();
97 }
98
99 // XTextLayout
100 uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
101 {
102 SolarMutexGuard aGuard;
103
104 OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
106 pVDev->SetFont( mpFont->getVCLFont() );
107
108 setupLayoutMode( *pVDev, mnTextDirection );
109
110 const rendering::ViewState aViewState(
111 geometry::AffineMatrix2D(1,0,0, 0,1,0),
112 nullptr);
113
114 rendering::RenderState aRenderState (
115 geometry::AffineMatrix2D(1,0,0,0,1,0),
116 nullptr,
117 uno::Sequence<double>(4),
118 rendering::CompositeOperation::SOURCE);
119
120 std::vector<sal_Int32> aOffsets(maLogicalAdvancements.getLength());
121 setupTextOffsets(aOffsets.data(), maLogicalAdvancements, aViewState, aRenderState);
122
123 std::vector< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
125 if (pVDev->GetTextOutlines(
126 aOutlines,
127 maText.Text,
128 maText.StartPosition,
129 maText.StartPosition,
130 maText.Length,
131 0,
132 aOffsets))
133 {
134 aOutlineSequence.reserve(aOutlines.size());
135 sal_Int32 nIndex (0);
136 for (auto const& outline : aOutlines)
137 {
138 aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
139 mxDevice,
140 outline);
141 }
142 }
143
144 return comphelper::containerToSequence(aOutlineSequence);
145 }
146
147 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
148 {
149 SolarMutexGuard aGuard;
150
151
152 OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
154 pVDev->SetFont( mpFont->getVCLFont() );
155
156 setupLayoutMode( *pVDev, mnTextDirection );
157
158 const rendering::ViewState aViewState(
159 geometry::AffineMatrix2D(1,0,0, 0,1,0),
160 nullptr);
161
162 rendering::RenderState aRenderState (
163 geometry::AffineMatrix2D(1,0,0,0,1,0),
164 nullptr,
165 uno::Sequence<double>(4),
166 rendering::CompositeOperation::SOURCE);
167
168 std::unique_ptr< sal_Int32 []> aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]);
169 setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
170
171 std::vector< ::tools::Rectangle > aMetricVector;
172 uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes;
173 if (pVDev->GetGlyphBoundRects(
174 Point(0,0),
175 maText.Text,
176 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
177 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
178 aMetricVector))
179 {
180 aBoundingBoxes.realloc(aMetricVector.size());
181 auto pBoundingBoxes = aBoundingBoxes.getArray();
182 sal_Int32 nIndex (0);
183 for (auto const& metric : aMetricVector)
184 {
185 pBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
186 metric.Left(),
187 metric.Top(),
188 metric.Right(),
189 metric.Bottom());
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
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;
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::vector<sal_Int32> aOffsets(maLogicalAdvancements.getLength());
339 setupTextOffsets( aOffsets.data(), 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,
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( ::basegfx::B2DHomMatrix aMat ) :
365 maMatrix(std::move( aMat ))
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( sal_Int32* 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
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: */
Text maText
void DrawTextArray(const Point &rStartPt, const OUString &rStr, o3tl::span< const sal_Int32 > pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, SalLayoutFlags flags=SalLayoutFlags::NONE, const SalLayoutGlyphs *pLayoutCache=nullptr)
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)
void SetLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
rtl::Reference< CanvasFont > Reference
Definition: canvasfont.hxx:49
TextLayout(const TextLayout &)=delete
make noncopyable
#define ENSURE_OR_THROW(c, m)
#define ENSURE_ARG_OR_THROW(c, m)
std::mutex m_aMutex
sal_Int32 nIndex
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
ComplexTextLayoutFlags
std::shared_ptr< OutDevProvider > OutDevProviderSharedPtr
::cppu::WeakComponentImplHelper< css::rendering::XTextLayout, css::lang::XServiceInfo > TextLayout_Base
Definition: textlayout.hxx:38
std::optional< vcl::Font > mpFont
::basegfx::B2DHomMatrix maMatrix
Definition: textlayout.cxx:387
unsigned char sal_Bool
signed char sal_Int8