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>
28 #include <vcl/metric.hxx>
29 #include <vcl/virdev.hxx>
31 #include <basegfx/matrix/b2dhommatrix.hxx>
32 #include <basegfx/numeric/ftools.hxx>
33 #include <basegfx/tools/canvastools.hxx>
35 #include "impltools.hxx"
36 #include "textlayout.hxx"
38 #include <boost/scoped_array.hpp>
40 using namespace ::com::sun::star
;
46 void setupLayoutMode( OutputDevice
& rOutDev
,
47 sal_Int8 nTextDirection
)
49 // TODO(P3): avoid if already correctly set
50 sal_uIntPtr nLayoutMode
;
51 switch( nTextDirection
)
56 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
57 nLayoutMode
= TEXT_LAYOUT_BIDI_LTR
;
59 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
60 nLayoutMode
= TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
;
62 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
63 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
;
65 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
66 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
;
70 // set calculated layout mode. Origin is always the left edge,
71 // as required at the API spec
72 rOutDev
.SetLayoutMode( nLayoutMode
| TEXT_LAYOUT_TEXTORIGIN_LEFT
);
76 TextLayout::TextLayout( const rendering::StringContext
& aText
,
78 sal_Int64 nRandomSeed
,
79 const CanvasFont::Reference
& rFont
,
80 const uno::Reference
<rendering::XGraphicDevice
>& xDevice
,
81 const OutDevProviderSharedPtr
& rOutDev
) :
82 TextLayout_Base( m_aMutex
),
84 maLogicalAdvancements(),
87 mpOutDevProvider( rOutDev
),
88 mnTextDirection( nDirection
)
93 void SAL_CALL
TextLayout::disposing()
95 SolarMutexGuard aGuard
;
97 mpOutDevProvider
.reset();
103 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > SAL_CALL
TextLayout::queryTextShapes( ) throw (uno::RuntimeException
)
105 SolarMutexGuard aGuard
;
107 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
108 VirtualDevice
aVDev( rOutDev
);
109 aVDev
.SetFont( mpFont
->getVCLFont() );
111 setupLayoutMode( aVDev
, mnTextDirection
);
113 const rendering::ViewState
aViewState(
114 geometry::AffineMatrix2D(1,0,0, 0,1,0),
117 rendering::RenderState
aRenderState (
118 geometry::AffineMatrix2D(1,0,0,0,1,0),
120 uno::Sequence
<double>(4),
121 rendering::CompositeOperation::SOURCE
);
123 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
124 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
126 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > aOutlineSequence
;
127 ::basegfx::B2DPolyPolygonVector aOutlines
;
128 if (aVDev
.GetTextOutlines(
131 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
132 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
133 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
),
138 aOutlineSequence
.realloc(aOutlines
.size());
139 sal_Int32
nIndex (0);
140 for (::basegfx::B2DPolyPolygonVector::const_iterator
141 iOutline(aOutlines
.begin()),
142 iEnd(aOutlines
.end());
146 aOutlineSequence
[nIndex
++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
152 return aOutlineSequence
;
155 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryInkMeasures( ) throw (uno::RuntimeException
)
157 SolarMutexGuard aGuard
;
160 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
161 VirtualDevice
aVDev( rOutDev
);
162 aVDev
.SetFont( mpFont
->getVCLFont() );
164 setupLayoutMode( aVDev
, mnTextDirection
);
166 const rendering::ViewState
aViewState(
167 geometry::AffineMatrix2D(1,0,0, 0,1,0),
170 rendering::RenderState
aRenderState (
171 geometry::AffineMatrix2D(1,0,0,0,1,0),
173 uno::Sequence
<double>(4),
174 rendering::CompositeOperation::SOURCE
);
176 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
177 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
179 MetricVector aMetricVector
;
180 uno::Sequence
<geometry::RealRectangle2D
> aBoundingBoxes
;
181 if (aVDev
.GetGlyphBoundRects(
184 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
185 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
),
186 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
189 aBoundingBoxes
.realloc(aMetricVector
.size());
190 sal_Int32
nIndex (0);
191 for (MetricVector::const_iterator
192 iMetric(aMetricVector
.begin()),
193 iEnd(aMetricVector
.end());
197 aBoundingBoxes
[nIndex
++] = geometry::RealRectangle2D(
200 iMetric
->getX() + iMetric
->getWidth(),
201 iMetric
->getY() + iMetric
->getHeight());
204 return aBoundingBoxes
;
207 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryMeasures( ) throw (uno::RuntimeException
)
209 SolarMutexGuard aGuard
;
212 return uno::Sequence
< geometry::RealRectangle2D
>();
215 uno::Sequence
< double > SAL_CALL
TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException
)
217 SolarMutexGuard aGuard
;
219 return maLogicalAdvancements
;
222 void SAL_CALL
TextLayout::applyLogicalAdvancements( const uno::Sequence
< double >& aAdvancements
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
224 SolarMutexGuard aGuard
;
226 ENSURE_ARG_OR_THROW( aAdvancements
.getLength() == maText
.Length
,
227 "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
229 maLogicalAdvancements
= aAdvancements
;
232 geometry::RealRectangle2D SAL_CALL
TextLayout::queryTextBounds( ) throw (uno::RuntimeException
)
234 SolarMutexGuard aGuard
;
236 if( !mpOutDevProvider
)
237 return geometry::RealRectangle2D();
239 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
241 VirtualDevice
aVDev( rOutDev
);
242 aVDev
.SetFont( mpFont
->getVCLFont() );
244 // need metrics for Y offset, the XCanvas always renders
245 // relative to baseline
246 const ::FontMetric
& aMetric( aVDev
.GetFontMetric() );
248 setupLayoutMode( aVDev
, mnTextDirection
);
250 const sal_Int32
nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric
.GetAscent() );
251 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
253 if( maLogicalAdvancements
.getLength() )
255 return geometry::RealRectangle2D( 0, nAboveBaseline
,
256 maLogicalAdvancements
[ maLogicalAdvancements
.getLength()-1 ],
261 return geometry::RealRectangle2D( 0, nAboveBaseline
,
264 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
265 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) ),
270 double SAL_CALL
TextLayout::justify( double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
272 SolarMutexGuard aGuard
;
280 double SAL_CALL
TextLayout::combinedJustify( const uno::Sequence
< uno::Reference
< rendering::XTextLayout
> >& aNextLayouts
,
281 double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
283 SolarMutexGuard aGuard
;
292 rendering::TextHit SAL_CALL
TextLayout::getTextHit( const geometry::RealPoint2D
& aHitPoint
) throw (uno::RuntimeException
)
294 SolarMutexGuard aGuard
;
299 return rendering::TextHit();
302 rendering::Caret SAL_CALL
TextLayout::getCaret( sal_Int32 nInsertionIndex
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
304 SolarMutexGuard aGuard
;
306 (void)nInsertionIndex
;
307 (void)bExcludeLigatures
;
310 return rendering::Caret();
313 sal_Int32 SAL_CALL
TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex
, sal_Int32 nCaretAdvancement
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
315 SolarMutexGuard aGuard
;
318 (void)nCaretAdvancement
;
319 (void)bExcludeLigatures
;
325 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
327 SolarMutexGuard aGuard
;
333 return uno::Reference
< rendering::XPolyPolygon2D
>();
336 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
338 SolarMutexGuard aGuard
;
344 return uno::Reference
< rendering::XPolyPolygon2D
>();
347 double SAL_CALL
TextLayout::getBaselineOffset( ) throw (uno::RuntimeException
)
349 SolarMutexGuard aGuard
;
355 sal_Int8 SAL_CALL
TextLayout::getMainTextDirection( ) throw (uno::RuntimeException
)
357 SolarMutexGuard aGuard
;
359 return mnTextDirection
;
362 uno::Reference
< rendering::XCanvasFont
> SAL_CALL
TextLayout::getFont( ) throw (uno::RuntimeException
)
364 SolarMutexGuard aGuard
;
366 return mpFont
.getRef();
369 rendering::StringContext SAL_CALL
TextLayout::getText( ) throw (uno::RuntimeException
)
371 SolarMutexGuard aGuard
;
376 bool TextLayout::draw( OutputDevice
& rOutDev
,
377 const Point
& rOutpos
,
378 const rendering::ViewState
& viewState
,
379 const rendering::RenderState
& renderState
) const
381 SolarMutexGuard aGuard
;
383 setupLayoutMode( rOutDev
, mnTextDirection
);
385 if( maLogicalAdvancements
.getLength() )
387 // TODO(P2): cache that
388 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
389 setupTextOffsets( aOffsets
.get(), maLogicalAdvancements
, viewState
, renderState
);
391 // TODO(F3): ensure correct length and termination for DX
392 // array (last entry _must_ contain the overall width)
394 rOutDev
.DrawTextArray( rOutpos
,
397 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
398 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
402 rOutDev
.DrawText( rOutpos
,
404 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.StartPosition
),
405 ::canvas::tools::numeric_cast
<sal_uInt16
>(maText
.Length
) );
413 class OffsetTransformer
416 OffsetTransformer( const ::basegfx::B2DHomMatrix
& rMat
) :
421 sal_Int32
operator()( const double& rOffset
)
423 // This is an optimization of the normal rMat*[x,0]
424 // transformation of the advancement vector (in x
425 // direction), followed by a length calculation of the
426 // resulting vector: advancement' =
427 // ||rMat*[x,0]||. Since advancements are vectors, we
428 // can ignore translational components, thus if [x,0],
429 // it follows that rMat*[x,0]=[x',0] holds. Thus, we
430 // just have to calc the transformation of the x
433 // TODO(F2): Handle non-horizontal advancements!
434 return ::basegfx::fround( hypot(maMatrix
.get(0,0)*rOffset
,
435 maMatrix
.get(1,0)*rOffset
) );
439 ::basegfx::B2DHomMatrix maMatrix
;
443 void TextLayout::setupTextOffsets( sal_Int32
* outputOffsets
,
444 const uno::Sequence
< double >& inputOffsets
,
445 const rendering::ViewState
& viewState
,
446 const rendering::RenderState
& renderState
) const
448 ENSURE_OR_THROW( outputOffsets
!=NULL
,
449 "TextLayout::setupTextOffsets offsets NULL" );
451 ::basegfx::B2DHomMatrix aMatrix
;
453 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
457 // fill integer offsets
458 ::std::transform( inputOffsets
.getConstArray(),
459 inputOffsets
.getConstArray()+inputOffsets
.getLength(),
461 OffsetTransformer( aMatrix
) );
465 #define IMPLEMENTATION_NAME "VCLCanvas::TextLayout"
466 #define SERVICE_NAME "com.sun.star.rendering.TextLayout"
468 OUString SAL_CALL
TextLayout::getImplementationName() throw( uno::RuntimeException
)
470 return OUString( IMPLEMENTATION_NAME
);
473 sal_Bool SAL_CALL
TextLayout::supportsService( const OUString
& ServiceName
) throw( uno::RuntimeException
)
475 return ServiceName
== SERVICE_NAME
;
478 uno::Sequence
< OUString
> SAL_CALL
TextLayout::getSupportedServiceNames() throw( uno::RuntimeException
)
480 uno::Sequence
< OUString
> aRet(1);
481 aRet
[0] = OUString( SERVICE_NAME
);
487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */