Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <canvas/debug.hxx>
31 : : #include <tools/diagnose_ex.h>
32 : : #include <canvas/canvastools.hxx>
33 : :
34 : : #include <com/sun/star/rendering/CompositeOperation.hpp>
35 : : #include <com/sun/star/rendering/TextDirection.hpp>
36 : :
37 : : #include <vcl/metric.hxx>
38 : : #include <vcl/virdev.hxx>
39 : :
40 : : #include <basegfx/matrix/b2dhommatrix.hxx>
41 : : #include <basegfx/numeric/ftools.hxx>
42 : : #include <basegfx/tools/canvastools.hxx>
43 : :
44 : : #include "impltools.hxx"
45 : : #include "textlayout.hxx"
46 : :
47 : : #include <boost/scoped_array.hpp>
48 : :
49 : :
50 : : using namespace ::com::sun::star;
51 : :
52 : : namespace vclcanvas
53 : : {
54 : : namespace
55 : : {
56 : 0 : void setupLayoutMode( OutputDevice& rOutDev,
57 : : sal_Int8 nTextDirection )
58 : : {
59 : : // TODO(P3): avoid if already correctly set
60 : : sal_uIntPtr nLayoutMode;
61 : 0 : switch( nTextDirection )
62 : : {
63 : : default:
64 : 0 : nLayoutMode = 0;
65 : 0 : break;
66 : : case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
67 : 0 : nLayoutMode = TEXT_LAYOUT_BIDI_LTR;
68 : 0 : break;
69 : : case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
70 : 0 : nLayoutMode = TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG;
71 : 0 : break;
72 : : case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
73 : 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL;
74 : 0 : break;
75 : : case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
76 : 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
77 : 0 : break;
78 : : }
79 : :
80 : : // set calculated layout mode. Origin is always the left edge,
81 : : // as required at the API spec
82 : 0 : rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT );
83 : 0 : }
84 : : }
85 : :
86 : 0 : TextLayout::TextLayout( const rendering::StringContext& aText,
87 : : sal_Int8 nDirection,
88 : : sal_Int64 nRandomSeed,
89 : : const CanvasFont::Reference& rFont,
90 : : const uno::Reference<rendering::XGraphicDevice>& xDevice,
91 : : const OutDevProviderSharedPtr& rOutDev ) :
92 : : TextLayout_Base( m_aMutex ),
93 : : maText( aText ),
94 : : maLogicalAdvancements(),
95 : : mpFont( rFont ),
96 : : mxDevice( xDevice ),
97 : : mpOutDevProvider( rOutDev ),
98 : 0 : mnTextDirection( nDirection )
99 : : {
100 : : (void)nRandomSeed;
101 : 0 : }
102 : :
103 : 0 : void SAL_CALL TextLayout::disposing()
104 : : {
105 : 0 : SolarMutexGuard aGuard;
106 : :
107 : 0 : mpOutDevProvider.reset();
108 : 0 : mxDevice.clear();
109 : 0 : mpFont.reset();
110 : 0 : }
111 : :
112 : : // XTextLayout
113 : 0 : uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException)
114 : : {
115 : 0 : SolarMutexGuard aGuard;
116 : :
117 : 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
118 : 0 : VirtualDevice aVDev( rOutDev );
119 : 0 : aVDev.SetFont( mpFont->getVCLFont() );
120 : :
121 : 0 : setupLayoutMode( aVDev, mnTextDirection );
122 : :
123 : : const rendering::ViewState aViewState(
124 : : geometry::AffineMatrix2D(1,0,0, 0,1,0),
125 : 0 : NULL);
126 : :
127 : : rendering::RenderState aRenderState (
128 : : geometry::AffineMatrix2D(1,0,0,0,1,0),
129 : : NULL,
130 : : uno::Sequence<double>(4),
131 : 0 : rendering::CompositeOperation::SOURCE);
132 : :
133 : 0 : ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]);
134 : 0 : setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
135 : :
136 : 0 : uno::Sequence< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
137 : 0 : ::basegfx::B2DPolyPolygonVector aOutlines;
138 : 0 : if (aVDev.GetTextOutlines(
139 : : aOutlines,
140 : : maText.Text,
141 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
142 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
143 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
144 : : sal_False,
145 : : 0,
146 : 0 : aOffsets.get()))
147 : : {
148 : 0 : aOutlineSequence.realloc(aOutlines.size());
149 : 0 : sal_Int32 nIndex (0);
150 : 0 : for (::basegfx::B2DPolyPolygonVector::const_iterator
151 : 0 : iOutline(aOutlines.begin()),
152 : 0 : iEnd(aOutlines.end());
153 : : iOutline!=iEnd;
154 : : ++iOutline)
155 : : {
156 : 0 : aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
157 : : mxDevice,
158 : 0 : *iOutline);
159 : : }
160 : : }
161 : :
162 : 0 : return aOutlineSequence;
163 : : }
164 : :
165 : 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException)
166 : : {
167 : 0 : SolarMutexGuard aGuard;
168 : :
169 : :
170 : 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
171 : 0 : VirtualDevice aVDev( rOutDev );
172 : 0 : aVDev.SetFont( mpFont->getVCLFont() );
173 : :
174 : 0 : setupLayoutMode( aVDev, mnTextDirection );
175 : :
176 : : const rendering::ViewState aViewState(
177 : : geometry::AffineMatrix2D(1,0,0, 0,1,0),
178 : 0 : NULL);
179 : :
180 : : rendering::RenderState aRenderState (
181 : : geometry::AffineMatrix2D(1,0,0,0,1,0),
182 : : NULL,
183 : : uno::Sequence<double>(4),
184 : 0 : rendering::CompositeOperation::SOURCE);
185 : :
186 : 0 : ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]);
187 : 0 : setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
188 : :
189 : 0 : MetricVector aMetricVector;
190 : 0 : uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes;
191 : 0 : if (aVDev.GetGlyphBoundRects(
192 : : Point(0,0),
193 : : maText.Text,
194 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
195 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
196 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
197 : 0 : aMetricVector))
198 : : {
199 : 0 : aBoundingBoxes.realloc(aMetricVector.size());
200 : 0 : sal_Int32 nIndex (0);
201 : 0 : for (MetricVector::const_iterator
202 : 0 : iMetric(aMetricVector.begin()),
203 : 0 : iEnd(aMetricVector.end());
204 : : iMetric!=iEnd;
205 : : ++iMetric)
206 : : {
207 : 0 : aBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
208 : 0 : iMetric->getX(),
209 : 0 : iMetric->getY(),
210 : 0 : iMetric->getX() + iMetric->getWidth(),
211 : 0 : iMetric->getY() + iMetric->getHeight());
212 : : }
213 : : }
214 : 0 : return aBoundingBoxes;
215 : : }
216 : :
217 : 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException)
218 : : {
219 : 0 : SolarMutexGuard aGuard;
220 : :
221 : : // TODO(F1)
222 : 0 : return uno::Sequence< geometry::RealRectangle2D >();
223 : : }
224 : :
225 : 0 : uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException)
226 : : {
227 : 0 : SolarMutexGuard aGuard;
228 : :
229 : 0 : return maLogicalAdvancements;
230 : : }
231 : :
232 : 0 : void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException)
233 : : {
234 : 0 : SolarMutexGuard aGuard;
235 : :
236 : 0 : ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length,
237 : : "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
238 : :
239 : 0 : maLogicalAdvancements = aAdvancements;
240 : 0 : }
241 : :
242 : 0 : geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException)
243 : : {
244 : 0 : SolarMutexGuard aGuard;
245 : :
246 : 0 : if( !mpOutDevProvider )
247 : 0 : return geometry::RealRectangle2D();
248 : :
249 : 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
250 : :
251 : 0 : VirtualDevice aVDev( rOutDev );
252 : 0 : aVDev.SetFont( mpFont->getVCLFont() );
253 : :
254 : : // need metrics for Y offset, the XCanvas always renders
255 : : // relative to baseline
256 : 0 : const ::FontMetric& aMetric( aVDev.GetFontMetric() );
257 : :
258 : 0 : setupLayoutMode( aVDev, mnTextDirection );
259 : :
260 : 0 : const sal_Int32 nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric.GetAscent() );
261 : 0 : const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
262 : :
263 : 0 : if( maLogicalAdvancements.getLength() )
264 : : {
265 : : return geometry::RealRectangle2D( 0, nAboveBaseline,
266 : 0 : maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
267 : 0 : nBelowBaseline );
268 : : }
269 : : else
270 : : {
271 : : return geometry::RealRectangle2D( 0, nAboveBaseline,
272 : : aVDev.GetTextWidth(
273 : : maText.Text,
274 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
275 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
276 : 0 : nBelowBaseline );
277 : 0 : }
278 : : }
279 : :
280 : 0 : double SAL_CALL TextLayout::justify( double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException)
281 : : {
282 : 0 : SolarMutexGuard aGuard;
283 : :
284 : : (void)nSize;
285 : :
286 : : // TODO(F1)
287 : 0 : return 0.0;
288 : : }
289 : :
290 : 0 : double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& aNextLayouts,
291 : : double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException)
292 : : {
293 : 0 : SolarMutexGuard aGuard;
294 : :
295 : : (void)aNextLayouts;
296 : : (void)nSize;
297 : :
298 : : // TODO(F1)
299 : 0 : return 0.0;
300 : : }
301 : :
302 : 0 : rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& aHitPoint ) throw (uno::RuntimeException)
303 : : {
304 : 0 : SolarMutexGuard aGuard;
305 : :
306 : : (void)aHitPoint;
307 : :
308 : : // TODO(F1)
309 : 0 : return rendering::TextHit();
310 : : }
311 : :
312 : 0 : rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
313 : : {
314 : 0 : SolarMutexGuard aGuard;
315 : :
316 : : (void)nInsertionIndex;
317 : : (void)bExcludeLigatures;
318 : :
319 : : // TODO(F1)
320 : 0 : return rendering::Caret();
321 : : }
322 : :
323 : 0 : sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
324 : : {
325 : 0 : SolarMutexGuard aGuard;
326 : :
327 : : (void)nStartIndex;
328 : : (void)nCaretAdvancement;
329 : : (void)bExcludeLigatures;
330 : :
331 : : // TODO(F1)
332 : 0 : return 0;
333 : : }
334 : :
335 : 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
336 : : {
337 : 0 : SolarMutexGuard aGuard;
338 : :
339 : : (void)nStartIndex;
340 : : (void)nEndIndex;
341 : :
342 : : // TODO(F1)
343 : 0 : return uno::Reference< rendering::XPolyPolygon2D >();
344 : : }
345 : :
346 : 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
347 : : {
348 : 0 : SolarMutexGuard aGuard;
349 : :
350 : : (void)nStartIndex;
351 : : (void)nEndIndex;
352 : :
353 : : // TODO(F1)
354 : 0 : return uno::Reference< rendering::XPolyPolygon2D >();
355 : : }
356 : :
357 : 0 : double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException)
358 : : {
359 : 0 : SolarMutexGuard aGuard;
360 : :
361 : : // TODO(F1)
362 : 0 : return 0.0;
363 : : }
364 : :
365 : 0 : sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException)
366 : : {
367 : 0 : SolarMutexGuard aGuard;
368 : :
369 : 0 : return mnTextDirection;
370 : : }
371 : :
372 : 0 : uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException)
373 : : {
374 : 0 : SolarMutexGuard aGuard;
375 : :
376 : 0 : return mpFont.getRef();
377 : : }
378 : :
379 : 0 : rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException)
380 : : {
381 : 0 : SolarMutexGuard aGuard;
382 : :
383 : 0 : return maText;
384 : : }
385 : :
386 : 0 : bool TextLayout::draw( OutputDevice& rOutDev,
387 : : const Point& rOutpos,
388 : : const rendering::ViewState& viewState,
389 : : const rendering::RenderState& renderState ) const
390 : : {
391 : 0 : SolarMutexGuard aGuard;
392 : :
393 : 0 : setupLayoutMode( rOutDev, mnTextDirection );
394 : :
395 : 0 : if( maLogicalAdvancements.getLength() )
396 : : {
397 : : // TODO(P2): cache that
398 : 0 : ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]);
399 : 0 : setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState );
400 : :
401 : : // TODO(F3): ensure correct length and termination for DX
402 : : // array (last entry _must_ contain the overall width)
403 : :
404 : : rOutDev.DrawTextArray( rOutpos,
405 : : maText.Text,
406 : 0 : aOffsets.get(),
407 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
408 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
409 : : }
410 : : else
411 : : {
412 : : rOutDev.DrawText( rOutpos,
413 : : maText.Text,
414 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
415 : 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
416 : : }
417 : :
418 : 0 : return true;
419 : : }
420 : :
421 : : namespace
422 : : {
423 : 0 : class OffsetTransformer
424 : : {
425 : : public:
426 : 0 : OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) :
427 : 0 : maMatrix( rMat )
428 : : {
429 : 0 : }
430 : :
431 : 0 : sal_Int32 operator()( const double& rOffset )
432 : : {
433 : : // This is an optimization of the normal rMat*[x,0]
434 : : // transformation of the advancement vector (in x
435 : : // direction), followed by a length calculation of the
436 : : // resulting vector: advancement' =
437 : : // ||rMat*[x,0]||. Since advancements are vectors, we
438 : : // can ignore translational components, thus if [x,0],
439 : : // it follows that rMat*[x,0]=[x',0] holds. Thus, we
440 : : // just have to calc the transformation of the x
441 : : // component.
442 : :
443 : : // TODO(F2): Handle non-horizontal advancements!
444 : 0 : return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
445 : 0 : maMatrix.get(1,0)*rOffset) );
446 : : }
447 : :
448 : : private:
449 : : ::basegfx::B2DHomMatrix maMatrix;
450 : : };
451 : : }
452 : :
453 : 0 : void TextLayout::setupTextOffsets( sal_Int32* outputOffsets,
454 : : const uno::Sequence< double >& inputOffsets,
455 : : const rendering::ViewState& viewState,
456 : : const rendering::RenderState& renderState ) const
457 : : {
458 : 0 : ENSURE_OR_THROW( outputOffsets!=NULL,
459 : : "TextLayout::setupTextOffsets offsets NULL" );
460 : :
461 : 0 : ::basegfx::B2DHomMatrix aMatrix;
462 : :
463 : : ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
464 : : viewState,
465 : 0 : renderState);
466 : :
467 : : // fill integer offsets
468 : : ::std::transform( inputOffsets.getConstArray(),
469 : 0 : inputOffsets.getConstArray()+inputOffsets.getLength(),
470 : : outputOffsets,
471 : 0 : OffsetTransformer( aMatrix ) );
472 : 0 : }
473 : :
474 : :
475 : : #define IMPLEMENTATION_NAME "VCLCanvas::TextLayout"
476 : : #define SERVICE_NAME "com.sun.star.rendering.TextLayout"
477 : :
478 : 0 : ::rtl::OUString SAL_CALL TextLayout::getImplementationName() throw( uno::RuntimeException )
479 : : {
480 : 0 : return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) );
481 : : }
482 : :
483 : 0 : sal_Bool SAL_CALL TextLayout::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException )
484 : : {
485 : 0 : return ServiceName == SERVICE_NAME;
486 : : }
487 : :
488 : 0 : uno::Sequence< ::rtl::OUString > SAL_CALL TextLayout::getSupportedServiceNames() throw( uno::RuntimeException )
489 : : {
490 : 0 : uno::Sequence< ::rtl::OUString > aRet(1);
491 : 0 : aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) );
492 : :
493 : 0 : return aRet;
494 : : }
495 : 0 : }
496 : :
497 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|