Line data Source code
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 <canvas/debug.hxx>
22 : #include <tools/diagnose_ex.h>
23 : #include <canvas/canvastools.hxx>
24 :
25 : #include <com/sun/star/rendering/CompositeOperation.hpp>
26 : #include <com/sun/star/rendering/TextDirection.hpp>
27 : #include <cppuhelper/supportsservice.hxx>
28 :
29 : #include <vcl/metric.hxx>
30 : #include <vcl/virdev.hxx>
31 :
32 : #include <basegfx/matrix/b2dhommatrix.hxx>
33 : #include <basegfx/numeric/ftools.hxx>
34 : #include <basegfx/tools/canvastools.hxx>
35 :
36 : #include "impltools.hxx"
37 : #include "textlayout.hxx"
38 :
39 : #include <boost/scoped_array.hpp>
40 :
41 : using namespace ::com::sun::star;
42 :
43 : namespace vclcanvas
44 : {
45 : namespace
46 : {
47 0 : void setupLayoutMode( OutputDevice& rOutDev,
48 : sal_Int8 nTextDirection )
49 : {
50 : // TODO(P3): avoid if already correctly set
51 0 : ComplexTextLayoutMode nLayoutMode = TEXT_LAYOUT_DEFAULT;
52 0 : switch( nTextDirection )
53 : {
54 : case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
55 0 : break;
56 : case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
57 0 : nLayoutMode = TEXT_LAYOUT_BIDI_STRONG;
58 0 : break;
59 : case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
60 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL;
61 0 : break;
62 : case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
63 0 : nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
64 0 : break;
65 : default:
66 0 : break;
67 : }
68 :
69 : // set calculated layout mode. Origin is always the left edge,
70 : // as required at the API spec
71 0 : rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT );
72 0 : }
73 : }
74 :
75 0 : TextLayout::TextLayout( const rendering::StringContext& aText,
76 : sal_Int8 nDirection,
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 ),
82 : maText( aText ),
83 : maLogicalAdvancements(),
84 : mpFont( rFont ),
85 : mxDevice( xDevice ),
86 : mpOutDevProvider( rOutDev ),
87 0 : mnTextDirection( nDirection )
88 : {
89 : (void)nRandomSeed;
90 0 : }
91 :
92 0 : void SAL_CALL TextLayout::disposing()
93 : {
94 0 : SolarMutexGuard aGuard;
95 :
96 0 : mpOutDevProvider.reset();
97 0 : mxDevice.clear();
98 0 : mpFont.clear();
99 0 : }
100 :
101 : // XTextLayout
102 0 : uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException, std::exception)
103 : {
104 0 : SolarMutexGuard aGuard;
105 :
106 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
107 0 : VirtualDevice aVDev( rOutDev );
108 0 : aVDev.SetFont( mpFont->getVCLFont() );
109 :
110 0 : setupLayoutMode( aVDev, mnTextDirection );
111 :
112 : const rendering::ViewState aViewState(
113 : geometry::AffineMatrix2D(1,0,0, 0,1,0),
114 0 : NULL);
115 :
116 : rendering::RenderState aRenderState (
117 : geometry::AffineMatrix2D(1,0,0,0,1,0),
118 : NULL,
119 : uno::Sequence<double>(4),
120 0 : rendering::CompositeOperation::SOURCE);
121 :
122 0 : ::boost::scoped_array< long > aOffsets(new long[maLogicalAdvancements.getLength()]);
123 0 : setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
124 :
125 0 : uno::Sequence< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
126 0 : ::basegfx::B2DPolyPolygonVector aOutlines;
127 0 : if (aVDev.GetTextOutlines(
128 : aOutlines,
129 : maText.Text,
130 : maText.StartPosition,
131 : maText.StartPosition,
132 : maText.Length,
133 : false,
134 : 0,
135 0 : aOffsets.get()))
136 : {
137 0 : aOutlineSequence.realloc(aOutlines.size());
138 0 : sal_Int32 nIndex (0);
139 0 : for (::basegfx::B2DPolyPolygonVector::const_iterator
140 0 : iOutline(aOutlines.begin()),
141 0 : iEnd(aOutlines.end());
142 : iOutline!=iEnd;
143 : ++iOutline)
144 : {
145 0 : aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
146 : mxDevice,
147 0 : *iOutline);
148 : }
149 : }
150 :
151 0 : return aOutlineSequence;
152 : }
153 :
154 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException, std::exception)
155 : {
156 0 : SolarMutexGuard aGuard;
157 :
158 :
159 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
160 0 : VirtualDevice aVDev( rOutDev );
161 0 : aVDev.SetFont( mpFont->getVCLFont() );
162 :
163 0 : setupLayoutMode( aVDev, mnTextDirection );
164 :
165 : const rendering::ViewState aViewState(
166 : geometry::AffineMatrix2D(1,0,0, 0,1,0),
167 0 : NULL);
168 :
169 : rendering::RenderState aRenderState (
170 : geometry::AffineMatrix2D(1,0,0,0,1,0),
171 : NULL,
172 : uno::Sequence<double>(4),
173 0 : rendering::CompositeOperation::SOURCE);
174 :
175 0 : ::boost::scoped_array< long > aOffsets(new long[maLogicalAdvancements.getLength()]);
176 0 : setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
177 :
178 0 : MetricVector aMetricVector;
179 0 : uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes;
180 0 : if (aVDev.GetGlyphBoundRects(
181 : Point(0,0),
182 : maText.Text,
183 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
184 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
185 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
186 0 : aMetricVector))
187 : {
188 0 : aBoundingBoxes.realloc(aMetricVector.size());
189 0 : sal_Int32 nIndex (0);
190 0 : for (MetricVector::const_iterator
191 0 : iMetric(aMetricVector.begin()),
192 0 : iEnd(aMetricVector.end());
193 : iMetric!=iEnd;
194 : ++iMetric)
195 : {
196 0 : aBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
197 0 : iMetric->getX(),
198 0 : iMetric->getY(),
199 0 : iMetric->getX() + iMetric->getWidth(),
200 0 : iMetric->getY() + iMetric->getHeight());
201 : }
202 : }
203 0 : return aBoundingBoxes;
204 : }
205 :
206 0 : uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException, std::exception)
207 : {
208 0 : SolarMutexGuard aGuard;
209 :
210 : // TODO(F1)
211 0 : return uno::Sequence< geometry::RealRectangle2D >();
212 : }
213 :
214 0 : uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException, std::exception)
215 : {
216 0 : SolarMutexGuard aGuard;
217 :
218 0 : return maLogicalAdvancements;
219 : }
220 :
221 0 : void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
222 : {
223 0 : SolarMutexGuard aGuard;
224 :
225 0 : ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length,
226 : "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
227 :
228 0 : maLogicalAdvancements = aAdvancements;
229 0 : }
230 :
231 0 : geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException, std::exception)
232 : {
233 0 : SolarMutexGuard aGuard;
234 :
235 0 : if( !mpOutDevProvider )
236 0 : return geometry::RealRectangle2D();
237 :
238 0 : OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
239 :
240 0 : VirtualDevice aVDev( rOutDev );
241 0 : aVDev.SetFont( mpFont->getVCLFont() );
242 :
243 : // need metrics for Y offset, the XCanvas always renders
244 : // relative to baseline
245 0 : const ::FontMetric& aMetric( aVDev.GetFontMetric() );
246 :
247 0 : setupLayoutMode( aVDev, mnTextDirection );
248 :
249 0 : const sal_Int32 nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric.GetAscent() );
250 0 : const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
251 :
252 0 : if( maLogicalAdvancements.getLength() )
253 : {
254 : return geometry::RealRectangle2D( 0, nAboveBaseline,
255 0 : maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
256 0 : nBelowBaseline );
257 : }
258 : else
259 : {
260 : return geometry::RealRectangle2D( 0, nAboveBaseline,
261 : aVDev.GetTextWidth(
262 : maText.Text,
263 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
264 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
265 0 : nBelowBaseline );
266 0 : }
267 : }
268 :
269 0 : double SAL_CALL TextLayout::justify( double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
270 : {
271 0 : SolarMutexGuard aGuard;
272 :
273 : (void)nSize;
274 :
275 : // TODO(F1)
276 0 : return 0.0;
277 : }
278 :
279 0 : double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& aNextLayouts,
280 : double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
281 : {
282 0 : SolarMutexGuard aGuard;
283 :
284 : (void)aNextLayouts;
285 : (void)nSize;
286 :
287 : // TODO(F1)
288 0 : return 0.0;
289 : }
290 :
291 0 : rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& aHitPoint ) throw (uno::RuntimeException, std::exception)
292 : {
293 0 : SolarMutexGuard aGuard;
294 :
295 : (void)aHitPoint;
296 :
297 : // TODO(F1)
298 0 : return rendering::TextHit();
299 : }
300 :
301 0 : rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
302 : {
303 0 : SolarMutexGuard aGuard;
304 :
305 : (void)nInsertionIndex;
306 : (void)bExcludeLigatures;
307 :
308 : // TODO(F1)
309 0 : return rendering::Caret();
310 : }
311 :
312 0 : sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
313 : {
314 0 : SolarMutexGuard aGuard;
315 :
316 : (void)nStartIndex;
317 : (void)nCaretAdvancement;
318 : (void)bExcludeLigatures;
319 :
320 : // TODO(F1)
321 0 : return 0;
322 : }
323 :
324 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
325 : {
326 0 : SolarMutexGuard aGuard;
327 :
328 : (void)nStartIndex;
329 : (void)nEndIndex;
330 :
331 : // TODO(F1)
332 0 : return uno::Reference< rendering::XPolyPolygon2D >();
333 : }
334 :
335 0 : uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception)
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 : double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException, std::exception)
347 : {
348 0 : SolarMutexGuard aGuard;
349 :
350 : // TODO(F1)
351 0 : return 0.0;
352 : }
353 :
354 0 : sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException, std::exception)
355 : {
356 0 : SolarMutexGuard aGuard;
357 :
358 0 : return mnTextDirection;
359 : }
360 :
361 0 : uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException, std::exception)
362 : {
363 0 : SolarMutexGuard aGuard;
364 :
365 0 : return mpFont.get();
366 : }
367 :
368 0 : rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException, std::exception)
369 : {
370 0 : SolarMutexGuard aGuard;
371 :
372 0 : return maText;
373 : }
374 :
375 0 : bool TextLayout::draw( OutputDevice& rOutDev,
376 : const Point& rOutpos,
377 : const rendering::ViewState& viewState,
378 : const rendering::RenderState& renderState ) const
379 : {
380 0 : SolarMutexGuard aGuard;
381 :
382 0 : setupLayoutMode( rOutDev, mnTextDirection );
383 :
384 0 : if( maLogicalAdvancements.getLength() )
385 : {
386 : // TODO(P2): cache that
387 0 : ::boost::scoped_array< long > aOffsets(new long[maLogicalAdvancements.getLength()]);
388 0 : setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState );
389 :
390 : // TODO(F3): ensure correct length and termination for DX
391 : // array (last entry _must_ contain the overall width)
392 :
393 : rOutDev.DrawTextArray( rOutpos,
394 : maText.Text,
395 0 : aOffsets.get(),
396 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
397 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
398 : }
399 : else
400 : {
401 : rOutDev.DrawText( rOutpos,
402 : maText.Text,
403 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
404 0 : ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
405 : }
406 :
407 0 : return true;
408 : }
409 :
410 : namespace
411 : {
412 0 : class OffsetTransformer
413 : {
414 : public:
415 0 : OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) :
416 0 : maMatrix( rMat )
417 : {
418 0 : }
419 :
420 0 : sal_Int32 operator()( const double& rOffset )
421 : {
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
430 : // component.
431 :
432 : // TODO(F2): Handle non-horizontal advancements!
433 0 : return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
434 0 : maMatrix.get(1,0)*rOffset) );
435 : }
436 :
437 : private:
438 : ::basegfx::B2DHomMatrix maMatrix;
439 : };
440 : }
441 :
442 0 : void TextLayout::setupTextOffsets( long* outputOffsets,
443 : const uno::Sequence< double >& inputOffsets,
444 : const rendering::ViewState& viewState,
445 : const rendering::RenderState& renderState ) const
446 : {
447 0 : ENSURE_OR_THROW( outputOffsets!=NULL,
448 : "TextLayout::setupTextOffsets offsets NULL" );
449 :
450 0 : ::basegfx::B2DHomMatrix aMatrix;
451 :
452 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
453 : viewState,
454 0 : renderState);
455 :
456 : // fill integer offsets
457 : ::std::transform( inputOffsets.getConstArray(),
458 0 : inputOffsets.getConstArray()+inputOffsets.getLength(),
459 : outputOffsets,
460 0 : OffsetTransformer( aMatrix ) );
461 0 : }
462 :
463 0 : OUString SAL_CALL TextLayout::getImplementationName() throw( uno::RuntimeException, std::exception )
464 : {
465 0 : return OUString( "VCLCanvas::TextLayout" );
466 : }
467 :
468 0 : sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName ) throw( uno::RuntimeException, std::exception )
469 : {
470 0 : return cppu::supportsService( this, ServiceName );
471 : }
472 :
473 0 : uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames() throw( uno::RuntimeException, std::exception )
474 : {
475 0 : uno::Sequence< OUString > aRet(1);
476 0 : aRet[0] = "com.sun.star.rendering.TextLayout";
477 :
478 0 : return aRet;
479 : }
480 0 : }
481 :
482 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|