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 .
21 #include <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <canvas/canvastools.hxx>
25 #include <com/sun/star/rendering/CompositeOperation.hpp>
26 #include <com/sun/star/rendering/TextDirection.hpp>
27 #include <cppuhelper/supportsservice.hxx>
29 #include <vcl/metric.hxx>
30 #include <vcl/virdev.hxx>
32 #include <basegfx/matrix/b2dhommatrix.hxx>
33 #include <basegfx/numeric/ftools.hxx>
34 #include <basegfx/tools/canvastools.hxx>
36 #include "impltools.hxx"
37 #include "textlayout.hxx"
39 #include <boost/scoped_array.hpp>
41 using namespace ::com::sun::star
;
47 void setupLayoutMode( OutputDevice
& rOutDev
,
48 sal_Int8 nTextDirection
)
50 // TODO(P3): avoid if already correctly set
51 ComplexTextLayoutMode nLayoutMode
= TEXT_LAYOUT_DEFAULT
;
52 switch( nTextDirection
)
54 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
56 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
57 nLayoutMode
= TEXT_LAYOUT_BIDI_STRONG
;
59 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
60 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
;
62 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
63 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
;
69 // set calculated layout mode. Origin is always the left edge,
70 // as required at the API spec
71 rOutDev
.SetLayoutMode( nLayoutMode
| TEXT_LAYOUT_TEXTORIGIN_LEFT
);
75 TextLayout::TextLayout( const rendering::StringContext
& aText
,
77 sal_Int64 nRandomSeed
,
78 const CanvasFont::Reference
& rFont
,
79 const uno::Reference
<rendering::XGraphicDevice
>& xDevice
,
80 const OutDevProviderSharedPtr
& rOutDev
) :
81 TextLayout_Base( m_aMutex
),
83 maLogicalAdvancements(),
86 mpOutDevProvider( rOutDev
),
87 mnTextDirection( nDirection
)
92 void SAL_CALL
TextLayout::disposing()
94 SolarMutexGuard aGuard
;
96 mpOutDevProvider
.reset();
102 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > SAL_CALL
TextLayout::queryTextShapes( ) throw (uno::RuntimeException
, std::exception
)
104 SolarMutexGuard aGuard
;
106 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
107 ScopedVclPtrInstance
< VirtualDevice
> pVDev( rOutDev
);
108 pVDev
->SetFont( mpFont
->getVCLFont() );
110 setupLayoutMode( *pVDev
.get(), mnTextDirection
);
112 const rendering::ViewState
aViewState(
113 geometry::AffineMatrix2D(1,0,0, 0,1,0),
116 rendering::RenderState
aRenderState (
117 geometry::AffineMatrix2D(1,0,0,0,1,0),
119 uno::Sequence
<double>(4),
120 rendering::CompositeOperation::SOURCE
);
122 ::boost::scoped_array
< long > aOffsets(new long[maLogicalAdvancements
.getLength()]);
123 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
125 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > aOutlineSequence
;
126 ::basegfx::B2DPolyPolygonVector aOutlines
;
127 if (pVDev
->GetTextOutlines(
130 maText
.StartPosition
,
131 maText
.StartPosition
,
137 aOutlineSequence
.realloc(aOutlines
.size());
138 sal_Int32
nIndex (0);
139 for (::basegfx::B2DPolyPolygonVector::const_iterator
140 iOutline(aOutlines
.begin()),
141 iEnd(aOutlines
.end());
145 aOutlineSequence
[nIndex
++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
151 return aOutlineSequence
;
154 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryInkMeasures( ) throw (uno::RuntimeException
, std::exception
)
156 SolarMutexGuard aGuard
;
159 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
160 ScopedVclPtrInstance
< VirtualDevice
> pVDev( rOutDev
);
161 pVDev
->SetFont( mpFont
->getVCLFont() );
163 setupLayoutMode( *pVDev
.get(), mnTextDirection
);
165 const rendering::ViewState
aViewState(
166 geometry::AffineMatrix2D(1,0,0, 0,1,0),
169 rendering::RenderState
aRenderState (
170 geometry::AffineMatrix2D(1,0,0,0,1,0),
172 uno::Sequence
<double>(4),
173 rendering::CompositeOperation::SOURCE
);
175 ::boost::scoped_array
< long > aOffsets(new long[maLogicalAdvancements
.getLength()]);
176 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
178 MetricVector aMetricVector
;
179 uno::Sequence
<geometry::RealRectangle2D
> aBoundingBoxes
;
180 if (pVDev
->GetGlyphBoundRects(
183 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
184 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
),
185 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
188 aBoundingBoxes
.realloc(aMetricVector
.size());
189 sal_Int32
nIndex (0);
190 for (MetricVector::const_iterator
191 iMetric(aMetricVector
.begin()),
192 iEnd(aMetricVector
.end());
196 aBoundingBoxes
[nIndex
++] = geometry::RealRectangle2D(
199 iMetric
->getX() + iMetric
->getWidth(),
200 iMetric
->getY() + iMetric
->getHeight());
203 return aBoundingBoxes
;
206 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryMeasures( ) throw (uno::RuntimeException
, std::exception
)
208 SolarMutexGuard aGuard
;
211 return uno::Sequence
< geometry::RealRectangle2D
>();
214 uno::Sequence
< double > SAL_CALL
TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException
, std::exception
)
216 SolarMutexGuard aGuard
;
218 return maLogicalAdvancements
;
221 void SAL_CALL
TextLayout::applyLogicalAdvancements( const uno::Sequence
< double >& aAdvancements
) throw (lang::IllegalArgumentException
, uno::RuntimeException
, std::exception
)
223 SolarMutexGuard aGuard
;
225 ENSURE_ARG_OR_THROW( aAdvancements
.getLength() == maText
.Length
,
226 "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
228 maLogicalAdvancements
= aAdvancements
;
231 geometry::RealRectangle2D SAL_CALL
TextLayout::queryTextBounds( ) throw (uno::RuntimeException
, std::exception
)
233 SolarMutexGuard aGuard
;
235 if( !mpOutDevProvider
)
236 return geometry::RealRectangle2D();
238 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
240 ScopedVclPtrInstance
< VirtualDevice
> pVDev( rOutDev
);
241 pVDev
->SetFont( mpFont
->getVCLFont() );
243 // need metrics for Y offset, the XCanvas always renders
244 // relative to baseline
245 const ::FontMetric
& aMetric( pVDev
->GetFontMetric() );
247 setupLayoutMode( *pVDev
.get(), mnTextDirection
);
249 const sal_Int32
nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric
.GetAscent() );
250 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
252 if( maLogicalAdvancements
.getLength() )
254 return geometry::RealRectangle2D( 0, nAboveBaseline
,
255 maLogicalAdvancements
[ maLogicalAdvancements
.getLength()-1 ],
260 return geometry::RealRectangle2D( 0, nAboveBaseline
,
263 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
264 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) ),
269 double SAL_CALL
TextLayout::justify( double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
, std::exception
)
271 SolarMutexGuard aGuard
;
279 double SAL_CALL
TextLayout::combinedJustify( const uno::Sequence
< uno::Reference
< rendering::XTextLayout
> >& aNextLayouts
,
280 double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
, std::exception
)
282 SolarMutexGuard aGuard
;
291 rendering::TextHit SAL_CALL
TextLayout::getTextHit( const geometry::RealPoint2D
& aHitPoint
) throw (uno::RuntimeException
, std::exception
)
293 SolarMutexGuard aGuard
;
298 return rendering::TextHit();
301 rendering::Caret SAL_CALL
TextLayout::getCaret( sal_Int32 nInsertionIndex
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
, std::exception
)
303 SolarMutexGuard aGuard
;
305 (void)nInsertionIndex
;
306 (void)bExcludeLigatures
;
309 return rendering::Caret();
312 sal_Int32 SAL_CALL
TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex
, sal_Int32 nCaretAdvancement
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
, std::exception
)
314 SolarMutexGuard aGuard
;
317 (void)nCaretAdvancement
;
318 (void)bExcludeLigatures
;
324 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
, std::exception
)
326 SolarMutexGuard aGuard
;
332 return uno::Reference
< rendering::XPolyPolygon2D
>();
335 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
, std::exception
)
337 SolarMutexGuard aGuard
;
343 return uno::Reference
< rendering::XPolyPolygon2D
>();
346 double SAL_CALL
TextLayout::getBaselineOffset( ) throw (uno::RuntimeException
, std::exception
)
348 SolarMutexGuard aGuard
;
354 sal_Int8 SAL_CALL
TextLayout::getMainTextDirection( ) throw (uno::RuntimeException
, std::exception
)
356 SolarMutexGuard aGuard
;
358 return mnTextDirection
;
361 uno::Reference
< rendering::XCanvasFont
> SAL_CALL
TextLayout::getFont( ) throw (uno::RuntimeException
, std::exception
)
363 SolarMutexGuard aGuard
;
368 rendering::StringContext SAL_CALL
TextLayout::getText( ) throw (uno::RuntimeException
, std::exception
)
370 SolarMutexGuard aGuard
;
375 bool TextLayout::draw( OutputDevice
& rOutDev
,
376 const Point
& rOutpos
,
377 const rendering::ViewState
& viewState
,
378 const rendering::RenderState
& renderState
) const
380 SolarMutexGuard aGuard
;
382 setupLayoutMode( rOutDev
, mnTextDirection
);
384 if( maLogicalAdvancements
.getLength() )
386 // TODO(P2): cache that
387 ::boost::scoped_array
< long > aOffsets(new long[maLogicalAdvancements
.getLength()]);
388 setupTextOffsets( aOffsets
.get(), maLogicalAdvancements
, viewState
, renderState
);
390 // TODO(F3): ensure correct length and termination for DX
391 // array (last entry _must_ contain the overall width)
393 rOutDev
.DrawTextArray( rOutpos
,
396 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
397 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
401 rOutDev
.DrawText( rOutpos
,
403 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
404 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
412 class OffsetTransformer
415 OffsetTransformer( const ::basegfx::B2DHomMatrix
& rMat
) :
420 sal_Int32
operator()( const double& rOffset
)
422 // This is an optimization of the normal rMat*[x,0]
423 // transformation of the advancement vector (in x
424 // direction), followed by a length calculation of the
425 // resulting vector: advancement' =
426 // ||rMat*[x,0]||. Since advancements are vectors, we
427 // can ignore translational components, thus if [x,0],
428 // it follows that rMat*[x,0]=[x',0] holds. Thus, we
429 // just have to calc the transformation of the x
432 // TODO(F2): Handle non-horizontal advancements!
433 return ::basegfx::fround( hypot(maMatrix
.get(0,0)*rOffset
,
434 maMatrix
.get(1,0)*rOffset
) );
438 ::basegfx::B2DHomMatrix maMatrix
;
442 void TextLayout::setupTextOffsets( long* outputOffsets
,
443 const uno::Sequence
< double >& inputOffsets
,
444 const rendering::ViewState
& viewState
,
445 const rendering::RenderState
& renderState
) const
447 ENSURE_OR_THROW( outputOffsets
!=NULL
,
448 "TextLayout::setupTextOffsets offsets NULL" );
450 ::basegfx::B2DHomMatrix aMatrix
;
452 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
456 // fill integer offsets
457 ::std::transform( inputOffsets
.getConstArray(),
458 inputOffsets
.getConstArray()+inputOffsets
.getLength(),
460 OffsetTransformer( aMatrix
) );
463 OUString SAL_CALL
TextLayout::getImplementationName() throw( uno::RuntimeException
, std::exception
)
465 return OUString( "VCLCanvas::TextLayout" );
468 sal_Bool SAL_CALL
TextLayout::supportsService( const OUString
& ServiceName
) throw( uno::RuntimeException
, std::exception
)
470 return cppu::supportsService( this, ServiceName
);
473 uno::Sequence
< OUString
> SAL_CALL
TextLayout::getSupportedServiceNames() throw( uno::RuntimeException
, std::exception
)
475 uno::Sequence
< OUString
> aRet(1);
476 aRet
[0] = "com.sun.star.rendering.TextLayout";
482 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */