1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: textlayout.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <canvas/canvastools.hxx>
38 #include <com/sun/star/rendering/CompositeOperation.hpp>
39 #include <com/sun/star/rendering/TextDirection.hpp>
41 #include <vcl/metric.hxx>
42 #include <vcl/virdev.hxx>
44 #include <basegfx/matrix/b2dhommatrix.hxx>
45 #include <basegfx/numeric/ftools.hxx>
46 #include <basegfx/tools/canvastools.hxx>
48 #include "impltools.hxx"
49 #include "textlayout.hxx"
51 #include <boost/scoped_array.hpp>
54 using namespace ::com::sun::star
;
60 void setupLayoutMode( OutputDevice
& rOutDev
,
61 sal_Int8 nTextDirection
)
63 // TODO(P3): avoid if already correctly set
65 switch( nTextDirection
)
70 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
71 nLayoutMode
= TEXT_LAYOUT_BIDI_LTR
;
73 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
74 nLayoutMode
= TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
;
76 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
77 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
;
79 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
80 nLayoutMode
= TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
;
84 // set calculated layout mode. Origin is always the left edge,
85 // as required at the API spec
86 rOutDev
.SetLayoutMode( nLayoutMode
| TEXT_LAYOUT_TEXTORIGIN_LEFT
);
90 TextLayout::TextLayout( const rendering::StringContext
& aText
,
92 sal_Int64 nRandomSeed
,
93 const CanvasFont::Reference
& rFont
,
94 const uno::Reference
<rendering::XGraphicDevice
>& xDevice
,
95 const OutDevProviderSharedPtr
& rOutDev
) :
96 TextLayout_Base( m_aMutex
),
98 maLogicalAdvancements(),
101 mpOutDevProvider( rOutDev
),
102 mnTextDirection( nDirection
)
107 void SAL_CALL
TextLayout::disposing()
109 tools::LocalGuard aGuard
;
111 mpOutDevProvider
.reset();
117 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > SAL_CALL
TextLayout::queryTextShapes( ) throw (uno::RuntimeException
)
119 tools::LocalGuard aGuard
;
121 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
122 VirtualDevice
aVDev( rOutDev
);
123 aVDev
.SetFont( mpFont
->getVCLFont() );
125 setupLayoutMode( aVDev
, mnTextDirection
);
127 const rendering::ViewState
aViewState(
128 geometry::AffineMatrix2D(1,0,0, 0,1,0),
131 rendering::RenderState
aRenderState (
132 geometry::AffineMatrix2D(1,0,0,0,1,0),
134 uno::Sequence
<double>(4),
135 rendering::CompositeOperation::SOURCE
);
137 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
138 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
140 uno::Sequence
< uno::Reference
< rendering::XPolyPolygon2D
> > aOutlineSequence
;
141 ::basegfx::B2DPolyPolygonVector aOutlines
;
142 if (aVDev
.GetTextOutlines(
145 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
146 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
147 ::canvas::tools::numeric_cast
<USHORT
>(maText
.Length
),
152 aOutlineSequence
.realloc(aOutlines
.size());
153 sal_Int32
nIndex (0);
154 for (::basegfx::B2DPolyPolygonVector::const_iterator
155 iOutline(aOutlines
.begin()),
156 iEnd(aOutlines
.end());
160 aOutlineSequence
[nIndex
++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
166 return aOutlineSequence
;
169 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryInkMeasures( ) throw (uno::RuntimeException
)
171 tools::LocalGuard aGuard
;
174 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
175 VirtualDevice
aVDev( rOutDev
);
176 aVDev
.SetFont( mpFont
->getVCLFont() );
178 setupLayoutMode( aVDev
, mnTextDirection
);
180 const rendering::ViewState
aViewState(
181 geometry::AffineMatrix2D(1,0,0, 0,1,0),
184 rendering::RenderState
aRenderState (
185 geometry::AffineMatrix2D(1,0,0,0,1,0),
187 uno::Sequence
<double>(4),
188 rendering::CompositeOperation::SOURCE
);
190 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
191 setupTextOffsets(aOffsets
.get(), maLogicalAdvancements
, aViewState
, aRenderState
);
193 MetricVector aMetricVector
;
194 uno::Sequence
<geometry::RealRectangle2D
> aBoundingBoxes
;
195 if (aVDev
.GetGlyphBoundRects(
198 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
199 ::canvas::tools::numeric_cast
<USHORT
>(maText
.Length
),
200 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
203 aBoundingBoxes
.realloc(aMetricVector
.size());
204 sal_Int32
nIndex (0);
205 for (MetricVector::const_iterator
206 iMetric(aMetricVector
.begin()),
207 iEnd(aMetricVector
.end());
211 aBoundingBoxes
[nIndex
++] = geometry::RealRectangle2D(
214 iMetric
->getX() + iMetric
->getWidth(),
215 iMetric
->getY() + iMetric
->getHeight());
218 return aBoundingBoxes
;
221 uno::Sequence
< geometry::RealRectangle2D
> SAL_CALL
TextLayout::queryMeasures( ) throw (uno::RuntimeException
)
223 tools::LocalGuard aGuard
;
226 return uno::Sequence
< geometry::RealRectangle2D
>();
229 uno::Sequence
< double > SAL_CALL
TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException
)
231 tools::LocalGuard aGuard
;
233 return maLogicalAdvancements
;
236 void SAL_CALL
TextLayout::applyLogicalAdvancements( const uno::Sequence
< double >& aAdvancements
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
238 tools::LocalGuard aGuard
;
240 ENSURE_ARG_OR_THROW( aAdvancements
.getLength() == maText
.Length
,
241 "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
243 maLogicalAdvancements
= aAdvancements
;
246 geometry::RealRectangle2D SAL_CALL
TextLayout::queryTextBounds( ) throw (uno::RuntimeException
)
248 tools::LocalGuard aGuard
;
250 if( !mpOutDevProvider
)
251 return geometry::RealRectangle2D();
253 OutputDevice
& rOutDev
= mpOutDevProvider
->getOutDev();
255 VirtualDevice
aVDev( rOutDev
);
256 aVDev
.SetFont( mpFont
->getVCLFont() );
258 // need metrics for Y offset, the XCanvas always renders
259 // relative to baseline
260 const ::FontMetric
& aMetric( aVDev
.GetFontMetric() );
262 setupLayoutMode( aVDev
, mnTextDirection
);
264 const sal_Int32
nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric
.GetAscent() );
265 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
267 if( maLogicalAdvancements
.getLength() )
269 return geometry::RealRectangle2D( 0, nAboveBaseline
,
270 maLogicalAdvancements
[ maLogicalAdvancements
.getLength()-1 ],
275 return geometry::RealRectangle2D( 0, nAboveBaseline
,
278 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
279 ::canvas::tools::numeric_cast
<USHORT
>(maText
.Length
) ),
284 double SAL_CALL
TextLayout::justify( double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
286 tools::LocalGuard aGuard
;
294 double SAL_CALL
TextLayout::combinedJustify( const uno::Sequence
< uno::Reference
< rendering::XTextLayout
> >& aNextLayouts
,
295 double nSize
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
297 tools::LocalGuard aGuard
;
306 rendering::TextHit SAL_CALL
TextLayout::getTextHit( const geometry::RealPoint2D
& aHitPoint
) throw (uno::RuntimeException
)
308 tools::LocalGuard aGuard
;
313 return rendering::TextHit();
316 rendering::Caret SAL_CALL
TextLayout::getCaret( sal_Int32 nInsertionIndex
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
318 tools::LocalGuard aGuard
;
320 (void)nInsertionIndex
;
321 (void)bExcludeLigatures
;
324 return rendering::Caret();
327 sal_Int32 SAL_CALL
TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex
, sal_Int32 nCaretAdvancement
, sal_Bool bExcludeLigatures
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
329 tools::LocalGuard aGuard
;
332 (void)nCaretAdvancement
;
333 (void)bExcludeLigatures
;
339 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
341 tools::LocalGuard aGuard
;
347 return uno::Reference
< rendering::XPolyPolygon2D
>();
350 uno::Reference
< rendering::XPolyPolygon2D
> SAL_CALL
TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
)
352 tools::LocalGuard aGuard
;
358 return uno::Reference
< rendering::XPolyPolygon2D
>();
361 double SAL_CALL
TextLayout::getBaselineOffset( ) throw (uno::RuntimeException
)
363 tools::LocalGuard aGuard
;
369 sal_Int8 SAL_CALL
TextLayout::getMainTextDirection( ) throw (uno::RuntimeException
)
371 tools::LocalGuard aGuard
;
373 return mnTextDirection
;
376 uno::Reference
< rendering::XCanvasFont
> SAL_CALL
TextLayout::getFont( ) throw (uno::RuntimeException
)
378 tools::LocalGuard aGuard
;
380 return mpFont
.getRef();
383 rendering::StringContext SAL_CALL
TextLayout::getText( ) throw (uno::RuntimeException
)
385 tools::LocalGuard aGuard
;
390 bool TextLayout::draw( OutputDevice
& rOutDev
,
391 const Point
& rOutpos
,
392 const rendering::ViewState
& viewState
,
393 const rendering::RenderState
& renderState
) const
395 tools::LocalGuard aGuard
;
397 setupLayoutMode( rOutDev
, mnTextDirection
);
399 if( maLogicalAdvancements
.getLength() )
401 // TODO(P2): cache that
402 ::boost::scoped_array
< sal_Int32
> aOffsets(new sal_Int32
[maLogicalAdvancements
.getLength()]);
403 setupTextOffsets( aOffsets
.get(), maLogicalAdvancements
, viewState
, renderState
);
405 // TODO(F3): ensure correct length and termination for DX
406 // array (last entry _must_ contain the overall width)
408 rOutDev
.DrawTextArray( rOutpos
,
411 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
412 ::canvas::tools::numeric_cast
<USHORT
>(maText
.Length
) );
416 rOutDev
.DrawText( rOutpos
,
418 ::canvas::tools::numeric_cast
<USHORT
>(maText
.StartPosition
),
419 ::canvas::tools::numeric_cast
<USHORT
>(maText
.Length
) );
427 class OffsetTransformer
430 OffsetTransformer( const ::basegfx::B2DHomMatrix
& rMat
) :
435 sal_Int32
operator()( const double& rOffset
)
437 // This is an optimization of the normal rMat*[x,0]
438 // transformation of the advancement vector (in x
439 // direction), followed by a length calculation of the
440 // resulting vector: advancement' =
441 // ||rMat*[x,0]||. Since advancements are vectors, we
442 // can ignore translational components, thus if [x,0],
443 // it follows that rMat*[x,0]=[x',0] holds. Thus, we
444 // just have to calc the transformation of the x
447 // TODO(F2): Handle non-horizontal advancements!
448 return ::basegfx::fround( hypot(maMatrix
.get(0,0)*rOffset
,
449 maMatrix
.get(1,0)*rOffset
) );
453 ::basegfx::B2DHomMatrix maMatrix
;
457 void TextLayout::setupTextOffsets( sal_Int32
* outputOffsets
,
458 const uno::Sequence
< double >& inputOffsets
,
459 const rendering::ViewState
& viewState
,
460 const rendering::RenderState
& renderState
) const
462 ENSURE_OR_THROW( outputOffsets
!=NULL
,
463 "TextLayout::setupTextOffsets offsets NULL" );
465 ::basegfx::B2DHomMatrix aMatrix
;
467 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
471 // fill integer offsets
472 ::std::transform( const_cast< uno::Sequence
< double >& >(inputOffsets
).getConstArray(),
473 const_cast< uno::Sequence
< double >& >(inputOffsets
).getConstArray()+inputOffsets
.getLength(),
475 OffsetTransformer( aMatrix
) );
479 #define IMPLEMENTATION_NAME "VCLCanvas::TextLayout"
480 #define SERVICE_NAME "com.sun.star.rendering.TextLayout"
482 ::rtl::OUString SAL_CALL
TextLayout::getImplementationName() throw( uno::RuntimeException
)
484 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME
) );
487 sal_Bool SAL_CALL
TextLayout::supportsService( const ::rtl::OUString
& ServiceName
) throw( uno::RuntimeException
)
489 return ServiceName
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME
) );
492 uno::Sequence
< ::rtl::OUString
> SAL_CALL
TextLayout::getSupportedServiceNames() throw( uno::RuntimeException
)
494 uno::Sequence
< ::rtl::OUString
> aRet(1);
495 aRet
[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME
) );