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