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