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/verbosetrace.hxx>
24 :
25 : #include <rtl/logfile.hxx>
26 :
27 : #include <com/sun/star/rendering/PathCapType.hpp>
28 : #include <com/sun/star/rendering/PathJoinType.hpp>
29 : #include <com/sun/star/rendering/XCanvas.hpp>
30 : #include <com/sun/star/rendering/XCanvasFont.hpp>
31 :
32 : #include <basegfx/numeric/ftools.hxx>
33 : #include <basegfx/matrix/b2dhommatrix.hxx>
34 : #include <basegfx/range/b2drectangle.hxx>
35 : #include <basegfx/vector/b2dsize.hxx>
36 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 : #include <basegfx/polygon/b2dpolygontools.hxx>
38 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
39 :
40 : #include <tools/gen.hxx>
41 : #include <vcl/canvastools.hxx>
42 : #include <vcl/virdev.hxx>
43 :
44 : #include <basegfx/tools/canvastools.hxx>
45 : #include <canvas/canvastools.hxx>
46 :
47 : #include <boost/scoped_array.hpp>
48 : #include <boost/bind.hpp>
49 : #include <boost/utility.hpp>
50 :
51 : #include "textaction.hxx"
52 : #include "outdevstate.hxx"
53 : #include "mtftools.hxx"
54 :
55 :
56 : using namespace ::com::sun::star;
57 :
58 : namespace cppcanvas
59 : {
60 : namespace internal
61 : {
62 : namespace
63 : {
64 0 : void init( rendering::RenderState& o_rRenderState,
65 : const ::basegfx::B2DPoint& rStartPoint,
66 : const OutDevState& rState,
67 : const CanvasSharedPtr& rCanvas )
68 : {
69 0 : tools::initRenderState(o_rRenderState,rState);
70 :
71 : // #i36950# Offset clip back to origin (as it's also moved
72 : // by rStartPoint)
73 : // #i53964# Also take VCL font rotation into account,
74 : // since this, opposed to the FontMatrix rotation
75 : // elsewhere, _does_ get incorporated into the render
76 : // state transform.
77 : tools::modifyClip( o_rRenderState,
78 : rState,
79 : rCanvas,
80 : rStartPoint,
81 : NULL,
82 0 : &rState.fontRotation );
83 :
84 0 : basegfx::B2DHomMatrix aLocalTransformation(basegfx::tools::createRotateB2DHomMatrix(rState.fontRotation));
85 : aLocalTransformation.translate( rStartPoint.getX(),
86 0 : rStartPoint.getY() );
87 : ::canvas::tools::appendToRenderState( o_rRenderState,
88 0 : aLocalTransformation );
89 :
90 0 : o_rRenderState.DeviceColor = rState.textColor;
91 0 : }
92 :
93 0 : void init( rendering::RenderState& o_rRenderState,
94 : const ::basegfx::B2DPoint& rStartPoint,
95 : const OutDevState& rState,
96 : const CanvasSharedPtr& rCanvas,
97 : const ::basegfx::B2DHomMatrix& rTextTransform )
98 : {
99 0 : init( o_rRenderState, rStartPoint, rState, rCanvas );
100 :
101 : // TODO(F2): Also inversely-transform clip with
102 : // rTextTransform (which is actually rather hard, as the
103 : // text transform is _prepended_ to the render state)!
104 :
105 : // prepend extra font transform to render state
106 : // (prepend it, because it's interpreted in the unit
107 : // rect coordinate space)
108 : ::canvas::tools::prependToRenderState( o_rRenderState,
109 0 : rTextTransform );
110 0 : }
111 :
112 0 : void init( rendering::RenderState& o_rRenderState,
113 : uno::Reference< rendering::XCanvasFont >& o_rFont,
114 : const ::basegfx::B2DPoint& rStartPoint,
115 : const OutDevState& rState,
116 : const CanvasSharedPtr& rCanvas )
117 : {
118 : // ensure that o_rFont is valid. It is possible that
119 : // text actions are generated without previously
120 : // setting a font. Then, just take a default font
121 0 : if( !o_rFont.is() )
122 : {
123 : // Use completely default FontRequest
124 0 : const rendering::FontRequest aFontRequest;
125 :
126 0 : geometry::Matrix2D aFontMatrix;
127 0 : ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
128 :
129 0 : o_rFont = rCanvas->getUNOCanvas()->createFont(
130 : aFontRequest,
131 : uno::Sequence< beans::PropertyValue >(),
132 0 : aFontMatrix );
133 : }
134 :
135 : init( o_rRenderState,
136 : rStartPoint,
137 : rState,
138 0 : rCanvas );
139 0 : }
140 :
141 0 : void init( rendering::RenderState& o_rRenderState,
142 : uno::Reference< rendering::XCanvasFont >& o_rFont,
143 : const ::basegfx::B2DPoint& rStartPoint,
144 : const OutDevState& rState,
145 : const CanvasSharedPtr& rCanvas,
146 : const ::basegfx::B2DHomMatrix& rTextTransform )
147 : {
148 0 : init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );
149 :
150 : // TODO(F2): Also inversely-transform clip with
151 : // rTextTransform (which is actually rather hard, as the
152 : // text transform is _prepended_ to the render state)!
153 :
154 : // prepend extra font transform to render state
155 : // (prepend it, because it's interpreted in the unit
156 : // rect coordinate space)
157 : ::canvas::tools::prependToRenderState( o_rRenderState,
158 0 : rTextTransform );
159 0 : }
160 :
161 0 : ::basegfx::B2DPolyPolygon textLinesFromLogicalOffsets( const uno::Sequence< double >& rOffsets,
162 : const tools::TextLineInfo& rTextLineInfo )
163 : {
164 : return tools::createTextLinesPolyPolygon(
165 : 0.0,
166 : // extract character cell furthest to the right
167 : *(::std::max_element(
168 : rOffsets.getConstArray(),
169 0 : rOffsets.getConstArray() + rOffsets.getLength() )),
170 0 : rTextLineInfo );
171 : }
172 :
173 0 : uno::Sequence< double > setupDXArray( const sal_Int32* pCharWidths,
174 : sal_Int32 nLen,
175 : const OutDevState& rState )
176 : {
177 : // convert character widths from logical units
178 0 : uno::Sequence< double > aCharWidthSeq( nLen );
179 0 : double* pOutputWidths( aCharWidthSeq.getArray() );
180 :
181 : // #143885# maintain (nearly) full precision of DX
182 : // array, by circumventing integer-based
183 : // OutDev-mapping
184 0 : const double nScale( rState.mapModeTransform.get(0,0) );
185 0 : for( int i = 0; i < nLen; ++i )
186 : {
187 : // TODO(F2): use correct scale direction
188 0 : *pOutputWidths++ = *pCharWidths++ * nScale;
189 : }
190 :
191 0 : return aCharWidthSeq;
192 : }
193 :
194 0 : uno::Sequence< double > setupDXArray( const ::String& rText,
195 : sal_Int32 nStartPos,
196 : sal_Int32 nLen,
197 : VirtualDevice& rVDev,
198 : const OutDevState& rState )
199 : {
200 : // no external DX array given, create one from given
201 : // string
202 0 : ::boost::scoped_array< sal_Int32 > pCharWidths( new sal_Int32[nLen] );
203 :
204 : rVDev.GetTextArray( rText, pCharWidths.get(),
205 : static_cast<sal_uInt16>(nStartPos),
206 0 : static_cast<sal_uInt16>(nLen) );
207 :
208 0 : return setupDXArray( pCharWidths.get(), nLen, rState );
209 : }
210 :
211 0 : ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint& rStartPoint,
212 : const OutDevState& rState,
213 : const uno::Sequence< double >& rOffsets )
214 : {
215 0 : ::basegfx::B2DPoint aLocalPoint( rStartPoint );
216 :
217 0 : if( rState.textAlignment )
218 : {
219 : // text origin is right, not left. Modify start point
220 : // accordingly, because XCanvas::drawTextLayout()
221 : // always aligns left!
222 :
223 0 : const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );
224 :
225 : // correct start point for rotated text: rotate around
226 : // former start point
227 0 : aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
228 0 : aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
229 : }
230 :
231 0 : return aLocalPoint;
232 : }
233 :
234 : /** Perform common setup for array text actions
235 :
236 : This method creates the XTextLayout object and
237 : initializes it, e.g. with the logical advancements.
238 : */
239 0 : void initArrayAction( rendering::RenderState& o_rRenderState,
240 : uno::Reference< rendering::XTextLayout >& o_rTextLayout,
241 : const ::basegfx::B2DPoint& rStartPoint,
242 : const ::rtl::OUString& rText,
243 : sal_Int32 nStartPos,
244 : sal_Int32 nLen,
245 : const uno::Sequence< double >& rOffsets,
246 : const CanvasSharedPtr& rCanvas,
247 : const OutDevState& rState,
248 : const ::basegfx::B2DHomMatrix* pTextTransform )
249 : {
250 0 : ENSURE_OR_THROW( rOffsets.getLength(),
251 : "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
252 :
253 : const ::basegfx::B2DPoint aLocalStartPoint(
254 0 : adaptStartPoint( rStartPoint, rState, rOffsets ) );
255 :
256 0 : uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
257 :
258 0 : if( pTextTransform )
259 0 : init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
260 : else
261 0 : init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
262 :
263 0 : o_rTextLayout = xFont->createTextLayout(
264 : rendering::StringContext( rText, nStartPos, nLen ),
265 : rState.textDirection,
266 0 : 0 );
267 :
268 0 : ENSURE_OR_THROW( o_rTextLayout.is(),
269 : "::cppcanvas::internal::initArrayAction(): Invalid font" );
270 :
271 0 : o_rTextLayout->applyLogicalAdvancements( rOffsets );
272 0 : }
273 :
274 0 : double getLineWidth( ::VirtualDevice& rVDev,
275 : const OutDevState& rState,
276 : const rendering::StringContext& rStringContext )
277 : {
278 : // TODO(F2): use correct scale direction
279 : const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
280 : static_cast<sal_uInt16>(rStringContext.StartPosition),
281 0 : static_cast<sal_uInt16>(rStringContext.Length) ),
282 0 : 0 );
283 :
284 0 : return (rState.mapModeTransform * aSize).getX();
285 : }
286 :
287 : uno::Sequence< double >
288 0 : calcSubsetOffsets( rendering::RenderState& io_rRenderState,
289 : double& o_rMinPos,
290 : double& o_rMaxPos,
291 : const uno::Reference< rendering::XTextLayout >& rOrigTextLayout,
292 : const ::cppcanvas::internal::Action::Subset& rSubset )
293 : {
294 0 : ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
295 : "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
296 :
297 0 : uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
298 0 : const double* pOffsets( aOrigOffsets.getConstArray() );
299 :
300 0 : ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
301 : "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
302 :
303 : // TODO(F3): It currently seems that for RTL text, the
304 : // DX offsets are nevertheless increasing in logical
305 : // text order (I'd expect they are decreasing,
306 : // mimicking the fact that the text is output
307 : // right-to-left). This breaks text effects for ALL
308 : // RTL languages.
309 :
310 : // determine leftmost position in given subset range -
311 : // as the DX array contains the output positions
312 : // starting with the second character (the first is
313 : // assumed to have output position 0), correct begin
314 : // iterator.
315 : const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
316 : *(::std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
317 0 : pOffsets+rSubset.mnSubsetEnd )) );
318 :
319 : // determine rightmost position in given subset range
320 : // - as the DX array contains the output positions
321 : // starting with the second character (the first is
322 : // assumed to have output position 0), correct begin
323 : // iterator.
324 : const double nMaxPos(
325 : *(::std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
326 : 0 : rSubset.mnSubsetBegin-1),
327 0 : pOffsets + rSubset.mnSubsetEnd )) );
328 :
329 :
330 : // adapt render state, to move text output to given offset
331 : // -------------------------------------------------------
332 :
333 : // TODO(F1): Strictly speaking, we also have to adapt
334 : // the clip here, which normally should _not_ move
335 : // with the output offset. Neglected for now, as it
336 : // does not matter for drawing layer output
337 :
338 0 : if( rSubset.mnSubsetBegin > 0 )
339 : {
340 0 : ::basegfx::B2DHomMatrix aTranslation;
341 0 : if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical )
342 : {
343 : // vertical text -> offset in y direction
344 0 : aTranslation.translate( 0.0, nMinPos );
345 : }
346 : else
347 : {
348 : // horizontal text -> offset in x direction
349 0 : aTranslation.translate( nMinPos, 0.0 );
350 : }
351 :
352 : ::canvas::tools::appendToRenderState( io_rRenderState,
353 0 : aTranslation );
354 : }
355 :
356 :
357 : // reduce DX array to given substring
358 : // ----------------------------------
359 :
360 0 : const sal_Int32 nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
361 0 : uno::Sequence< double > aAdaptedOffsets( nNewElements );
362 0 : double* pAdaptedOffsets( aAdaptedOffsets.getArray() );
363 :
364 : // move to new output position (subtract nMinPos,
365 : // which is the new '0' position), copy only the range
366 : // as given by rSubset.
367 : ::std::transform( pOffsets + rSubset.mnSubsetBegin,
368 : pOffsets + rSubset.mnSubsetEnd,
369 : pAdaptedOffsets,
370 : ::boost::bind( ::std::minus<double>(),
371 : _1,
372 0 : nMinPos ) );
373 :
374 0 : o_rMinPos = nMinPos;
375 0 : o_rMaxPos = nMaxPos;
376 :
377 0 : return aAdaptedOffsets;
378 : }
379 :
380 : uno::Reference< rendering::XTextLayout >
381 0 : createSubsetLayout( const rendering::StringContext& rOrigContext,
382 : const ::cppcanvas::internal::Action::Subset& rSubset,
383 : const uno::Reference< rendering::XTextLayout >& rOrigTextLayout )
384 : {
385 : // create temporary new text layout with subset string
386 : // ---------------------------------------------------
387 :
388 : const sal_Int32 nNewStartPos( rOrigContext.StartPosition + ::std::min(
389 0 : rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
390 : const sal_Int32 nNewLength( ::std::max(
391 : ::std::min(
392 : rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
393 0 : rOrigContext.Length ),
394 0 : sal_Int32( 0 ) ) );
395 :
396 : const rendering::StringContext aContext( rOrigContext.Text,
397 : nNewStartPos,
398 0 : nNewLength );
399 :
400 : uno::Reference< rendering::XTextLayout > xTextLayout(
401 0 : rOrigTextLayout->getFont()->createTextLayout( aContext,
402 0 : rOrigTextLayout->getMainTextDirection(),
403 0 : 0 ),
404 0 : uno::UNO_QUERY_THROW );
405 :
406 0 : return xTextLayout;
407 : }
408 :
409 : /** Setup subset text layout
410 :
411 : @param io_rTextLayout
412 : Must contain original (full set) text layout on input,
413 : will contain subsetted text layout (or empty
414 : reference, for empty subsets) on output.
415 :
416 : @param io_rRenderState
417 : Must contain original render state on input, will
418 : contain shifted render state concatenated with
419 : rTransformation on output.
420 :
421 : @param rTransformation
422 : Additional transformation, to be prepended to render
423 : state
424 :
425 : @param rSubset
426 : Subset to prepare
427 : */
428 0 : void createSubsetLayout( uno::Reference< rendering::XTextLayout >& io_rTextLayout,
429 : rendering::RenderState& io_rRenderState,
430 : double& o_rMinPos,
431 : double& o_rMaxPos,
432 : const ::basegfx::B2DHomMatrix& rTransformation,
433 : const Action::Subset& rSubset )
434 : {
435 0 : ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
436 :
437 0 : if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
438 : {
439 : // empty range, empty layout
440 0 : io_rTextLayout.clear();
441 :
442 : return;
443 : }
444 :
445 0 : ENSURE_OR_THROW( io_rTextLayout.is(),
446 : "createSubsetLayout(): Invalid input layout" );
447 :
448 0 : const rendering::StringContext& rOrigContext( io_rTextLayout->getText() );
449 :
450 0 : if( rSubset.mnSubsetBegin == 0 &&
451 : rSubset.mnSubsetEnd == rOrigContext.Length )
452 : {
453 : // full range, no need for subsetting
454 : return;
455 : }
456 :
457 : uno::Reference< rendering::XTextLayout > xTextLayout(
458 0 : createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) );
459 :
460 0 : if( xTextLayout.is() )
461 : {
462 0 : xTextLayout->applyLogicalAdvancements(
463 : calcSubsetOffsets( io_rRenderState,
464 : o_rMinPos,
465 : o_rMaxPos,
466 : io_rTextLayout,
467 0 : rSubset ) );
468 : }
469 :
470 0 : io_rTextLayout = xTextLayout;
471 : }
472 :
473 :
474 : /** Interface for renderEffectText functor below.
475 :
476 : This is interface is used from the renderEffectText()
477 : method below, to call the client implementation.
478 : */
479 0 : class TextRenderer
480 : {
481 : public:
482 0 : virtual ~TextRenderer() {}
483 :
484 : /// Render text with given RenderState
485 : virtual bool operator()( const rendering::RenderState& rRenderState ) const = 0;
486 : };
487 :
488 : /** Render effect text.
489 :
490 : @param rRenderer
491 : Functor object, will be called to render the actual
492 : part of the text effect (the text itself and the means
493 : to render it are unknown to this method)
494 : */
495 0 : bool renderEffectText( const TextRenderer& rRenderer,
496 : const rendering::RenderState& rRenderState,
497 : const rendering::ViewState& /*rViewState*/,
498 : const uno::Reference< rendering::XCanvas >& xCanvas,
499 : const ::Color& rShadowColor,
500 : const ::basegfx::B2DSize& rShadowOffset,
501 : const ::Color& rReliefColor,
502 : const ::basegfx::B2DSize& rReliefOffset )
503 : {
504 0 : ::Color aEmptyColor( COL_AUTO );
505 : uno::Reference<rendering::XColorSpace> xColorSpace(
506 0 : xCanvas->getDevice()->getDeviceColorSpace() );
507 :
508 : // draw shadow text, if enabled
509 0 : if( rShadowColor != aEmptyColor )
510 : {
511 0 : rendering::RenderState aShadowState( rRenderState );
512 0 : ::basegfx::B2DHomMatrix aTranslate;
513 :
514 : aTranslate.translate( rShadowOffset.getX(),
515 0 : rShadowOffset.getY() );
516 :
517 0 : ::canvas::tools::appendToRenderState(aShadowState, aTranslate);
518 :
519 : aShadowState.DeviceColor =
520 : ::vcl::unotools::colorToDoubleSequence( rShadowColor,
521 0 : xColorSpace );
522 :
523 0 : rRenderer( aShadowState );
524 : }
525 :
526 : // draw relief text, if enabled
527 0 : if( rReliefColor != aEmptyColor )
528 : {
529 0 : rendering::RenderState aReliefState( rRenderState );
530 0 : ::basegfx::B2DHomMatrix aTranslate;
531 :
532 : aTranslate.translate( rReliefOffset.getX(),
533 0 : rReliefOffset.getY() );
534 :
535 0 : ::canvas::tools::appendToRenderState(aReliefState, aTranslate);
536 :
537 : aReliefState.DeviceColor =
538 : ::vcl::unotools::colorToDoubleSequence( rReliefColor,
539 0 : xColorSpace );
540 :
541 0 : rRenderer( aReliefState );
542 : }
543 :
544 : // draw normal text
545 0 : rRenderer( rRenderState );
546 :
547 0 : return true;
548 : }
549 :
550 :
551 0 : ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange& rTextBounds,
552 : const ::basegfx::B2DRange& rLineBounds,
553 : const ::basegfx::B2DSize& rReliefOffset,
554 : const ::basegfx::B2DSize& rShadowOffset,
555 : const rendering::RenderState& rRenderState,
556 : const rendering::ViewState& rViewState )
557 : {
558 0 : ::basegfx::B2DRange aBounds( rTextBounds );
559 :
560 : // add extends of text lines
561 0 : aBounds.expand( rLineBounds );
562 :
563 : // TODO(Q3): Provide this functionality at the B2DRange
564 0 : ::basegfx::B2DRange aTotalBounds( aBounds );
565 : aTotalBounds.expand(
566 0 : ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getX(),
567 0 : aBounds.getMinY() + rReliefOffset.getY(),
568 0 : aBounds.getMaxX() + rReliefOffset.getX(),
569 0 : aBounds.getMaxY() + rReliefOffset.getY() ) );
570 : aTotalBounds.expand(
571 0 : ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getX(),
572 0 : aBounds.getMinY() + rShadowOffset.getY(),
573 0 : aBounds.getMaxX() + rShadowOffset.getX(),
574 0 : aBounds.getMaxY() + rShadowOffset.getY() ) );
575 :
576 : return tools::calcDevicePixelBounds( aTotalBounds,
577 : rViewState,
578 0 : rRenderState );
579 : }
580 :
581 0 : void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
582 : uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
583 : const CanvasSharedPtr& rCanvas,
584 : const uno::Sequence< double >& rOffsets,
585 : const tools::TextLineInfo rLineInfo )
586 : {
587 : const ::basegfx::B2DPolyPolygon aPoly(
588 : textLinesFromLogicalOffsets(
589 : rOffsets,
590 0 : rLineInfo ) );
591 :
592 0 : o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
593 :
594 : o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
595 0 : rCanvas->getUNOCanvas()->getDevice(),
596 0 : aPoly );
597 0 : }
598 :
599 0 : void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
600 : uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
601 : const CanvasSharedPtr& rCanvas,
602 : double nLineWidth,
603 : const tools::TextLineInfo rLineInfo )
604 : {
605 : const ::basegfx::B2DPolyPolygon aPoly(
606 : tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
607 0 : rLineInfo ) );
608 :
609 0 : o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
610 :
611 : o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
612 0 : rCanvas->getUNOCanvas()->getDevice(),
613 0 : aPoly );
614 0 : }
615 :
616 :
617 : // -------------------------------------------------------------------------
618 :
619 0 : class TextAction : public Action, private ::boost::noncopyable
620 : {
621 : public:
622 : TextAction( const ::basegfx::B2DPoint& rStartPoint,
623 : const ::rtl::OUString& rString,
624 : sal_Int32 nStartPos,
625 : sal_Int32 nLen,
626 : const CanvasSharedPtr& rCanvas,
627 : const OutDevState& rState );
628 :
629 : TextAction( const ::basegfx::B2DPoint& rStartPoint,
630 : const ::rtl::OUString& rString,
631 : sal_Int32 nStartPos,
632 : sal_Int32 nLen,
633 : const CanvasSharedPtr& rCanvas,
634 : const OutDevState& rState,
635 : const ::basegfx::B2DHomMatrix& rTextTransform );
636 :
637 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
638 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
639 : const Subset& rSubset ) const;
640 :
641 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
642 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
643 : const Subset& rSubset ) const;
644 :
645 : virtual sal_Int32 getActionCount() const;
646 :
647 : private:
648 : // TODO(P2): This is potentially a real mass object
649 : // (every character might be a separate TextAction),
650 : // thus, make it as lightweight as possible. For
651 : // example, share common RenderState among several
652 : // TextActions, maybe using maOffsets for the
653 : // translation.
654 :
655 : uno::Reference< rendering::XCanvasFont > mxFont;
656 : const rendering::StringContext maStringContext;
657 : const CanvasSharedPtr mpCanvas;
658 : rendering::RenderState maState;
659 : const sal_Int8 maTextDirection;
660 : };
661 :
662 0 : TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
663 : const ::rtl::OUString& rString,
664 : sal_Int32 nStartPos,
665 : sal_Int32 nLen,
666 : const CanvasSharedPtr& rCanvas,
667 : const OutDevState& rState ) :
668 : mxFont( rState.xFont ),
669 : maStringContext( rString, nStartPos, nLen ),
670 : mpCanvas( rCanvas ),
671 : maState(),
672 0 : maTextDirection( rState.textDirection )
673 : {
674 : init( maState, mxFont,
675 : rStartPoint,
676 0 : rState, rCanvas );
677 :
678 0 : ENSURE_OR_THROW( mxFont.is(),
679 : "::cppcanvas::internal::TextAction(): Invalid font" );
680 0 : }
681 :
682 0 : TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
683 : const ::rtl::OUString& rString,
684 : sal_Int32 nStartPos,
685 : sal_Int32 nLen,
686 : const CanvasSharedPtr& rCanvas,
687 : const OutDevState& rState,
688 : const ::basegfx::B2DHomMatrix& rTextTransform ) :
689 : mxFont( rState.xFont ),
690 : maStringContext( rString, nStartPos, nLen ),
691 : mpCanvas( rCanvas ),
692 : maState(),
693 0 : maTextDirection( rState.textDirection )
694 : {
695 : init( maState, mxFont,
696 : rStartPoint,
697 0 : rState, rCanvas, rTextTransform );
698 :
699 0 : ENSURE_OR_THROW( mxFont.is(),
700 : "::cppcanvas::internal::TextAction(): Invalid font" );
701 0 : }
702 :
703 0 : bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
704 : {
705 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextAction::render()" );
706 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextAction: 0x%X", this );
707 :
708 0 : rendering::RenderState aLocalState( maState );
709 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
710 :
711 0 : mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
712 0 : mpCanvas->getViewState(), aLocalState, maTextDirection );
713 :
714 0 : return true;
715 : }
716 :
717 0 : bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
718 : const Subset& /*rSubset*/ ) const
719 : {
720 : OSL_FAIL( "TextAction::renderSubset(): Subset not supported by this object" );
721 :
722 : // TODO(P1): Retrieve necessary font metric info for
723 : // TextAction from XCanvas. Currently, the
724 : // TextActionFactory does not generate this object for
725 : // _subsettable_ text
726 0 : return render( rTransformation );
727 : }
728 :
729 0 : ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
730 : {
731 : // create XTextLayout, to have the
732 : // XTextLayout::queryTextBounds() method available
733 : uno::Reference< rendering::XTextLayout > xTextLayout(
734 0 : mxFont->createTextLayout(
735 : maStringContext,
736 : maTextDirection,
737 0 : 0 ) );
738 :
739 0 : rendering::RenderState aLocalState( maState );
740 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
741 :
742 : return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
743 0 : xTextLayout->queryTextBounds() ),
744 0 : mpCanvas->getViewState(),
745 0 : aLocalState );
746 : }
747 :
748 0 : ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
749 : const Subset& /*rSubset*/ ) const
750 : {
751 : OSL_FAIL( "TextAction::getBounds(): Subset not supported by this object" );
752 :
753 : // TODO(P1): Retrieve necessary font metric info for
754 : // TextAction from XCanvas. Currently, the
755 : // TextActionFactory does not generate this object for
756 : // _subsettable_ text
757 0 : return getBounds( rTransformation );
758 : }
759 :
760 0 : sal_Int32 TextAction::getActionCount() const
761 : {
762 : // TODO(P1): Retrieve necessary font metric info for
763 : // TextAction from XCanvas. Currently, the
764 : // TextActionFactory does not generate this object for
765 : // _subsettable_ text
766 0 : return 1;
767 : }
768 :
769 :
770 : // -------------------------------------------------------------------------
771 :
772 0 : class EffectTextAction :
773 : public Action,
774 : public TextRenderer,
775 : private ::boost::noncopyable
776 : {
777 : public:
778 : EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
779 : const ::basegfx::B2DSize& rReliefOffset,
780 : const ::Color& rReliefColor,
781 : const ::basegfx::B2DSize& rShadowOffset,
782 : const ::Color& rShadowColor,
783 : const ::rtl::OUString& rText,
784 : sal_Int32 nStartPos,
785 : sal_Int32 nLen,
786 : VirtualDevice& rVDev,
787 : const CanvasSharedPtr& rCanvas,
788 : const OutDevState& rState );
789 :
790 : EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
791 : const ::basegfx::B2DSize& rReliefOffset,
792 : const ::Color& rReliefColor,
793 : const ::basegfx::B2DSize& rShadowOffset,
794 : const ::Color& rShadowColor,
795 : const ::rtl::OUString& rText,
796 : sal_Int32 nStartPos,
797 : sal_Int32 nLen,
798 : VirtualDevice& rVDev,
799 : const CanvasSharedPtr& rCanvas,
800 : const OutDevState& rState,
801 : const ::basegfx::B2DHomMatrix& rTextTransform );
802 :
803 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
804 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
805 : const Subset& rSubset ) const;
806 :
807 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
808 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
809 : const Subset& rSubset ) const;
810 :
811 : virtual sal_Int32 getActionCount() const;
812 :
813 : private:
814 : /// Interface TextRenderer
815 : virtual bool operator()( const rendering::RenderState& rRenderState ) const;
816 :
817 : // TODO(P2): This is potentially a real mass object
818 : // (every character might be a separate TextAction),
819 : // thus, make it as lightweight as possible. For
820 : // example, share common RenderState among several
821 : // TextActions, maybe using maOffsets for the
822 : // translation.
823 :
824 : uno::Reference< rendering::XCanvasFont > mxFont;
825 : const rendering::StringContext maStringContext;
826 : const CanvasSharedPtr mpCanvas;
827 : rendering::RenderState maState;
828 : const tools::TextLineInfo maTextLineInfo;
829 : ::basegfx::B2DSize maLinesOverallSize;
830 : const double mnLineWidth;
831 : uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
832 : const ::basegfx::B2DSize maReliefOffset;
833 : const ::Color maReliefColor;
834 : const ::basegfx::B2DSize maShadowOffset;
835 : const ::Color maShadowColor;
836 : const sal_Int8 maTextDirection;
837 : };
838 :
839 0 : EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
840 : const ::basegfx::B2DSize& rReliefOffset,
841 : const ::Color& rReliefColor,
842 : const ::basegfx::B2DSize& rShadowOffset,
843 : const ::Color& rShadowColor,
844 : const ::rtl::OUString& rText,
845 : sal_Int32 nStartPos,
846 : sal_Int32 nLen,
847 : VirtualDevice& rVDev,
848 : const CanvasSharedPtr& rCanvas,
849 : const OutDevState& rState ) :
850 : mxFont( rState.xFont ),
851 : maStringContext( rText, nStartPos, nLen ),
852 : mpCanvas( rCanvas ),
853 : maState(),
854 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
855 : maLinesOverallSize(),
856 0 : mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
857 : mxTextLines(),
858 : maReliefOffset( rReliefOffset ),
859 : maReliefColor( rReliefColor ),
860 : maShadowOffset( rShadowOffset ),
861 : maShadowColor( rShadowColor ),
862 0 : maTextDirection( rState.textDirection )
863 : {
864 : initEffectLinePolyPolygon( maLinesOverallSize,
865 : mxTextLines,
866 : rCanvas,
867 : mnLineWidth,
868 0 : maTextLineInfo );
869 :
870 : init( maState, mxFont,
871 : rStartPoint,
872 0 : rState, rCanvas );
873 :
874 0 : ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
875 : "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
876 0 : }
877 :
878 0 : EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
879 : const ::basegfx::B2DSize& rReliefOffset,
880 : const ::Color& rReliefColor,
881 : const ::basegfx::B2DSize& rShadowOffset,
882 : const ::Color& rShadowColor,
883 : const ::rtl::OUString& rText,
884 : sal_Int32 nStartPos,
885 : sal_Int32 nLen,
886 : VirtualDevice& rVDev,
887 : const CanvasSharedPtr& rCanvas,
888 : const OutDevState& rState,
889 : const ::basegfx::B2DHomMatrix& rTextTransform ) :
890 : mxFont( rState.xFont ),
891 : maStringContext( rText, nStartPos, nLen ),
892 : mpCanvas( rCanvas ),
893 : maState(),
894 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
895 : maLinesOverallSize(),
896 0 : mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
897 : mxTextLines(),
898 : maReliefOffset( rReliefOffset ),
899 : maReliefColor( rReliefColor ),
900 : maShadowOffset( rShadowOffset ),
901 : maShadowColor( rShadowColor ),
902 0 : maTextDirection( rState.textDirection )
903 : {
904 : initEffectLinePolyPolygon( maLinesOverallSize,
905 : mxTextLines,
906 : rCanvas,
907 : mnLineWidth,
908 0 : maTextLineInfo );
909 :
910 : init( maState, mxFont,
911 : rStartPoint,
912 0 : rState, rCanvas, rTextTransform );
913 :
914 0 : ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
915 : "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
916 0 : }
917 :
918 0 : bool EffectTextAction::operator()( const rendering::RenderState& rRenderState ) const
919 : {
920 0 : const rendering::ViewState& rViewState( mpCanvas->getViewState() );
921 0 : const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
922 :
923 0 : rCanvas->fillPolyPolygon( mxTextLines,
924 : rViewState,
925 0 : rRenderState );
926 :
927 0 : rCanvas->drawText( maStringContext, mxFont,
928 : rViewState,
929 : rRenderState,
930 0 : maTextDirection );
931 :
932 0 : return true;
933 : }
934 :
935 0 : bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
936 : {
937 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextAction::render()" );
938 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextAction: 0x%X", this );
939 :
940 0 : rendering::RenderState aLocalState( maState );
941 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
942 :
943 : return renderEffectText( *this,
944 : aLocalState,
945 0 : mpCanvas->getViewState(),
946 0 : mpCanvas->getUNOCanvas(),
947 : maShadowColor,
948 : maShadowOffset,
949 : maReliefColor,
950 0 : maReliefOffset );
951 : }
952 :
953 0 : bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
954 : const Subset& /*rSubset*/ ) const
955 : {
956 : OSL_FAIL( "EffectTextAction::renderSubset(): Subset not supported by this object" );
957 :
958 : // TODO(P1): Retrieve necessary font metric info for
959 : // TextAction from XCanvas. Currently, the
960 : // TextActionFactory does not generate this object for
961 : // subsettable text
962 0 : return render( rTransformation );
963 : }
964 :
965 0 : ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
966 : {
967 : // create XTextLayout, to have the
968 : // XTextLayout::queryTextBounds() method available
969 : uno::Reference< rendering::XTextLayout > xTextLayout(
970 0 : mxFont->createTextLayout(
971 : maStringContext,
972 : maTextDirection,
973 0 : 0 ) );
974 :
975 0 : rendering::RenderState aLocalState( maState );
976 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
977 :
978 : return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
979 0 : xTextLayout->queryTextBounds() ),
980 : ::basegfx::B2DRange( 0,0,
981 : maLinesOverallSize.getX(),
982 : maLinesOverallSize.getY() ),
983 : maReliefOffset,
984 : maShadowOffset,
985 : aLocalState,
986 0 : mpCanvas->getViewState() );
987 : }
988 :
989 0 : ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
990 : const Subset& /*rSubset*/ ) const
991 : {
992 : OSL_FAIL( "EffectTextAction::getBounds(): Subset not supported by this object" );
993 :
994 : // TODO(P1): Retrieve necessary font metric info for
995 : // TextAction from XCanvas. Currently, the
996 : // TextActionFactory does not generate this object for
997 : // _subsettable_ text
998 0 : return getBounds( rTransformation );
999 : }
1000 :
1001 0 : sal_Int32 EffectTextAction::getActionCount() const
1002 : {
1003 : // TODO(P1): Retrieve necessary font metric info for
1004 : // TextAction from XCanvas. Currently, the
1005 : // TextActionFactory does not generate this object for
1006 : // subsettable text
1007 0 : return 1;
1008 : }
1009 :
1010 :
1011 : // -------------------------------------------------------------------------
1012 :
1013 0 : class TextArrayAction : public Action, private ::boost::noncopyable
1014 : {
1015 : public:
1016 : TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1017 : const ::rtl::OUString& rString,
1018 : sal_Int32 nStartPos,
1019 : sal_Int32 nLen,
1020 : const uno::Sequence< double >& rOffsets,
1021 : const CanvasSharedPtr& rCanvas,
1022 : const OutDevState& rState );
1023 :
1024 : TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1025 : const ::rtl::OUString& rString,
1026 : sal_Int32 nStartPos,
1027 : sal_Int32 nLen,
1028 : const uno::Sequence< double >& rOffsets,
1029 : const CanvasSharedPtr& rCanvas,
1030 : const OutDevState& rState,
1031 : const ::basegfx::B2DHomMatrix& rTextTransform );
1032 :
1033 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1034 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1035 : const Subset& rSubset ) const;
1036 :
1037 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1038 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1039 : const Subset& rSubset ) const;
1040 :
1041 : virtual sal_Int32 getActionCount() const;
1042 :
1043 : private:
1044 : // TODO(P2): This is potentially a real mass object
1045 : // (every character might be a separate TextAction),
1046 : // thus, make it as lightweight as possible. For
1047 : // example, share common RenderState among several
1048 : // TextActions, maybe using maOffsets for the
1049 : // translation.
1050 :
1051 : uno::Reference< rendering::XTextLayout > mxTextLayout;
1052 : const CanvasSharedPtr mpCanvas;
1053 : rendering::RenderState maState;
1054 : };
1055 :
1056 0 : TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1057 : const ::rtl::OUString& rString,
1058 : sal_Int32 nStartPos,
1059 : sal_Int32 nLen,
1060 : const uno::Sequence< double >& rOffsets,
1061 : const CanvasSharedPtr& rCanvas,
1062 : const OutDevState& rState ) :
1063 : mxTextLayout(),
1064 : mpCanvas( rCanvas ),
1065 0 : maState()
1066 : {
1067 : initArrayAction( maState,
1068 : mxTextLayout,
1069 : rStartPoint,
1070 : rString,
1071 : nStartPos,
1072 : nLen,
1073 : rOffsets,
1074 : rCanvas,
1075 0 : rState, NULL );
1076 0 : }
1077 :
1078 0 : TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1079 : const ::rtl::OUString& rString,
1080 : sal_Int32 nStartPos,
1081 : sal_Int32 nLen,
1082 : const uno::Sequence< double >& rOffsets,
1083 : const CanvasSharedPtr& rCanvas,
1084 : const OutDevState& rState,
1085 : const ::basegfx::B2DHomMatrix& rTextTransform ) :
1086 : mxTextLayout(),
1087 : mpCanvas( rCanvas ),
1088 0 : maState()
1089 : {
1090 : initArrayAction( maState,
1091 : mxTextLayout,
1092 : rStartPoint,
1093 : rString,
1094 : nStartPos,
1095 : nLen,
1096 : rOffsets,
1097 : rCanvas,
1098 : rState,
1099 0 : &rTextTransform );
1100 0 : }
1101 :
1102 0 : bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1103 : {
1104 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render()" );
1105 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1106 :
1107 0 : rendering::RenderState aLocalState( maState );
1108 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1109 :
1110 0 : mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
1111 0 : mpCanvas->getViewState(),
1112 0 : aLocalState );
1113 :
1114 0 : return true;
1115 : }
1116 :
1117 0 : bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1118 : const Subset& rSubset ) const
1119 : {
1120 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::renderSubset()" );
1121 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1122 :
1123 0 : rendering::RenderState aLocalState( maState );
1124 0 : uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1125 :
1126 : double nDummy0, nDummy1;
1127 : createSubsetLayout( xTextLayout,
1128 : aLocalState,
1129 : nDummy0,
1130 : nDummy1,
1131 : rTransformation,
1132 0 : rSubset );
1133 :
1134 0 : if( !xTextLayout.is() )
1135 0 : return true; // empty layout, render nothing
1136 :
1137 0 : mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
1138 0 : mpCanvas->getViewState(),
1139 0 : aLocalState );
1140 :
1141 0 : return true;
1142 : }
1143 :
1144 0 : ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1145 : {
1146 0 : rendering::RenderState aLocalState( maState );
1147 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1148 :
1149 : return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1150 0 : mxTextLayout->queryTextBounds() ),
1151 0 : mpCanvas->getViewState(),
1152 0 : aLocalState );
1153 : }
1154 :
1155 0 : ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1156 : const Subset& rSubset ) const
1157 : {
1158 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
1159 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1160 :
1161 0 : rendering::RenderState aLocalState( maState );
1162 0 : uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1163 :
1164 : double nDummy0, nDummy1;
1165 : createSubsetLayout( xTextLayout,
1166 : aLocalState,
1167 : nDummy0,
1168 : nDummy1,
1169 : rTransformation,
1170 0 : rSubset );
1171 :
1172 0 : if( !xTextLayout.is() )
1173 0 : return ::basegfx::B2DRange(); // empty layout, empty bounds
1174 :
1175 : return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1176 0 : xTextLayout->queryTextBounds() ),
1177 0 : mpCanvas->getViewState(),
1178 0 : aLocalState );
1179 : }
1180 :
1181 0 : sal_Int32 TextArrayAction::getActionCount() const
1182 : {
1183 0 : const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1184 :
1185 0 : return rOrigContext.Length;
1186 : }
1187 :
1188 :
1189 : // -------------------------------------------------------------------------
1190 :
1191 0 : class EffectTextArrayAction :
1192 : public Action,
1193 : public TextRenderer,
1194 : private ::boost::noncopyable
1195 : {
1196 : public:
1197 : EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1198 : const ::basegfx::B2DSize& rReliefOffset,
1199 : const ::Color& rReliefColor,
1200 : const ::basegfx::B2DSize& rShadowOffset,
1201 : const ::Color& rShadowColor,
1202 : const ::rtl::OUString& rText,
1203 : sal_Int32 nStartPos,
1204 : sal_Int32 nLen,
1205 : const uno::Sequence< double >& rOffsets,
1206 : VirtualDevice& rVDev,
1207 : const CanvasSharedPtr& rCanvas,
1208 : const OutDevState& rState );
1209 : EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1210 : const ::basegfx::B2DSize& rReliefOffset,
1211 : const ::Color& rReliefColor,
1212 : const ::basegfx::B2DSize& rShadowOffset,
1213 : const ::Color& rShadowColor,
1214 : const ::rtl::OUString& rText,
1215 : sal_Int32 nStartPos,
1216 : sal_Int32 nLen,
1217 : const uno::Sequence< double >& rOffsets,
1218 : VirtualDevice& rVDev,
1219 : const CanvasSharedPtr& rCanvas,
1220 : const OutDevState& rState,
1221 : const ::basegfx::B2DHomMatrix& rTextTransform );
1222 :
1223 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1224 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1225 : const Subset& rSubset ) const;
1226 :
1227 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1228 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1229 : const Subset& rSubset ) const;
1230 :
1231 : virtual sal_Int32 getActionCount() const;
1232 :
1233 : private:
1234 : // TextRenderer interface
1235 : virtual bool operator()( const rendering::RenderState& rRenderState ) const;
1236 :
1237 : // TODO(P2): This is potentially a real mass object
1238 : // (every character might be a separate TextAction),
1239 : // thus, make it as lightweight as possible. For
1240 : // example, share common RenderState among several
1241 : // TextActions, maybe using maOffsets for the
1242 : // translation.
1243 :
1244 : uno::Reference< rendering::XTextLayout > mxTextLayout;
1245 : const CanvasSharedPtr mpCanvas;
1246 : rendering::RenderState maState;
1247 : const tools::TextLineInfo maTextLineInfo;
1248 : ::basegfx::B2DSize maLinesOverallSize;
1249 : uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
1250 : const ::basegfx::B2DSize maReliefOffset;
1251 : const ::Color maReliefColor;
1252 : const ::basegfx::B2DSize maShadowOffset;
1253 : const ::Color maShadowColor;
1254 : };
1255 :
1256 0 : EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1257 : const ::basegfx::B2DSize& rReliefOffset,
1258 : const ::Color& rReliefColor,
1259 : const ::basegfx::B2DSize& rShadowOffset,
1260 : const ::Color& rShadowColor,
1261 : const ::rtl::OUString& rText,
1262 : sal_Int32 nStartPos,
1263 : sal_Int32 nLen,
1264 : const uno::Sequence< double >& rOffsets,
1265 : VirtualDevice& rVDev,
1266 : const CanvasSharedPtr& rCanvas,
1267 : const OutDevState& rState ) :
1268 : mxTextLayout(),
1269 : mpCanvas( rCanvas ),
1270 : maState(),
1271 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1272 : maLinesOverallSize(),
1273 : mxTextLines(),
1274 : maReliefOffset( rReliefOffset ),
1275 : maReliefColor( rReliefColor ),
1276 : maShadowOffset( rShadowOffset ),
1277 0 : maShadowColor( rShadowColor )
1278 : {
1279 : initEffectLinePolyPolygon( maLinesOverallSize,
1280 : mxTextLines,
1281 : rCanvas,
1282 : rOffsets,
1283 0 : maTextLineInfo );
1284 :
1285 : initArrayAction( maState,
1286 : mxTextLayout,
1287 : rStartPoint,
1288 : rText,
1289 : nStartPos,
1290 : nLen,
1291 : rOffsets,
1292 : rCanvas,
1293 0 : rState, NULL );
1294 0 : }
1295 :
1296 0 : EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1297 : const ::basegfx::B2DSize& rReliefOffset,
1298 : const ::Color& rReliefColor,
1299 : const ::basegfx::B2DSize& rShadowOffset,
1300 : const ::Color& rShadowColor,
1301 : const ::rtl::OUString& rText,
1302 : sal_Int32 nStartPos,
1303 : sal_Int32 nLen,
1304 : const uno::Sequence< double >& rOffsets,
1305 : VirtualDevice& rVDev,
1306 : const CanvasSharedPtr& rCanvas,
1307 : const OutDevState& rState,
1308 : const ::basegfx::B2DHomMatrix& rTextTransform ) :
1309 : mxTextLayout(),
1310 : mpCanvas( rCanvas ),
1311 : maState(),
1312 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1313 : maLinesOverallSize(),
1314 : mxTextLines(),
1315 : maReliefOffset( rReliefOffset ),
1316 : maReliefColor( rReliefColor ),
1317 : maShadowOffset( rShadowOffset ),
1318 0 : maShadowColor( rShadowColor )
1319 : {
1320 : initEffectLinePolyPolygon( maLinesOverallSize,
1321 : mxTextLines,
1322 : rCanvas,
1323 : rOffsets,
1324 0 : maTextLineInfo );
1325 :
1326 : initArrayAction( maState,
1327 : mxTextLayout,
1328 : rStartPoint,
1329 : rText,
1330 : nStartPos,
1331 : nLen,
1332 : rOffsets,
1333 : rCanvas,
1334 : rState,
1335 0 : &rTextTransform );
1336 0 : }
1337 :
1338 0 : bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState ) const
1339 : {
1340 0 : const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1341 0 : const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1342 :
1343 0 : rCanvas->fillPolyPolygon( mxTextLines,
1344 : rViewState,
1345 0 : rRenderState );
1346 :
1347 0 : rCanvas->drawTextLayout( mxTextLayout,
1348 : rViewState,
1349 0 : rRenderState );
1350 :
1351 0 : return true;
1352 : }
1353 :
1354 0 : bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1355 : {
1356 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
1357 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1358 :
1359 0 : rendering::RenderState aLocalState( maState );
1360 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1361 :
1362 : return renderEffectText( *this,
1363 : aLocalState,
1364 0 : mpCanvas->getViewState(),
1365 0 : mpCanvas->getUNOCanvas(),
1366 : maShadowColor,
1367 : maShadowOffset,
1368 : maReliefColor,
1369 0 : maReliefOffset );
1370 : }
1371 :
1372 0 : class EffectTextArrayRenderHelper : public TextRenderer
1373 : {
1374 : public:
1375 0 : EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1376 : const uno::Reference< rendering::XTextLayout >& rTextLayout,
1377 : const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1378 : const rendering::ViewState& rViewState ) :
1379 : mrCanvas( rCanvas ),
1380 : mrTextLayout( rTextLayout ),
1381 : mrLinePolygon( rLinePolygon ),
1382 0 : mrViewState( rViewState )
1383 : {
1384 0 : }
1385 :
1386 : // TextRenderer interface
1387 0 : virtual bool operator()( const rendering::RenderState& rRenderState ) const
1388 : {
1389 0 : mrCanvas->fillPolyPolygon( mrLinePolygon,
1390 : mrViewState,
1391 0 : rRenderState );
1392 :
1393 0 : mrCanvas->drawTextLayout( mrTextLayout,
1394 : mrViewState,
1395 0 : rRenderState );
1396 :
1397 0 : return true;
1398 : }
1399 :
1400 : private:
1401 : const uno::Reference< rendering::XCanvas >& mrCanvas;
1402 : const uno::Reference< rendering::XTextLayout >& mrTextLayout;
1403 : const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1404 : const rendering::ViewState& mrViewState;
1405 : };
1406 :
1407 0 : bool EffectTextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1408 : const Subset& rSubset ) const
1409 : {
1410 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
1411 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1412 :
1413 0 : rendering::RenderState aLocalState( maState );
1414 0 : uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1415 0 : const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1416 :
1417 0 : double nMinPos(0.0);
1418 0 : double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1419 :
1420 : createSubsetLayout( xTextLayout,
1421 : aLocalState,
1422 : nMinPos,
1423 : nMaxPos,
1424 : rTransformation,
1425 0 : rSubset );
1426 :
1427 0 : if( !xTextLayout.is() )
1428 0 : return true; // empty layout, render nothing
1429 :
1430 :
1431 : // create and setup local line polygon
1432 : // ===================================
1433 :
1434 0 : uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
1435 0 : const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1436 :
1437 : uno::Reference< rendering::XPolyPolygon2D > xTextLines(
1438 : ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1439 0 : xCanvas->getDevice(),
1440 : tools::createTextLinesPolyPolygon(
1441 : 0.0, nMaxPos - nMinPos,
1442 0 : maTextLineInfo ) ) );
1443 :
1444 :
1445 : // render everything
1446 : // =================
1447 :
1448 : return renderEffectText(
1449 : EffectTextArrayRenderHelper( xCanvas,
1450 : xTextLayout,
1451 : xTextLines,
1452 : rViewState ),
1453 : aLocalState,
1454 : rViewState,
1455 : xCanvas,
1456 : maShadowColor,
1457 : maShadowOffset,
1458 : maReliefColor,
1459 0 : maReliefOffset );
1460 : }
1461 :
1462 0 : ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1463 : {
1464 0 : rendering::RenderState aLocalState( maState );
1465 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1466 :
1467 : return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1468 0 : mxTextLayout->queryTextBounds() ),
1469 : ::basegfx::B2DRange( 0,0,
1470 : maLinesOverallSize.getX(),
1471 : maLinesOverallSize.getY() ),
1472 : maReliefOffset,
1473 : maShadowOffset,
1474 : aLocalState,
1475 0 : mpCanvas->getViewState() );
1476 : }
1477 :
1478 0 : ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1479 : const Subset& rSubset ) const
1480 : {
1481 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1482 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1483 :
1484 0 : rendering::RenderState aLocalState( maState );
1485 0 : uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1486 0 : const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1487 :
1488 0 : double nMinPos(0.0);
1489 0 : double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1490 :
1491 : createSubsetLayout( xTextLayout,
1492 : aLocalState,
1493 : nMinPos,
1494 : nMaxPos,
1495 : rTransformation,
1496 0 : rSubset );
1497 :
1498 0 : if( !xTextLayout.is() )
1499 0 : return ::basegfx::B2DRange(); // empty layout, empty bounds
1500 :
1501 :
1502 : // create and setup local line polygon
1503 : // ===================================
1504 :
1505 : const ::basegfx::B2DPolyPolygon aPoly(
1506 : tools::createTextLinesPolyPolygon(
1507 : 0.0, nMaxPos - nMinPos,
1508 0 : maTextLineInfo ) );
1509 :
1510 : return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1511 0 : xTextLayout->queryTextBounds() ),
1512 : ::basegfx::tools::getRange( aPoly ),
1513 : maReliefOffset,
1514 : maShadowOffset,
1515 : aLocalState,
1516 0 : mpCanvas->getViewState() );
1517 : }
1518 :
1519 0 : sal_Int32 EffectTextArrayAction::getActionCount() const
1520 : {
1521 0 : const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1522 :
1523 0 : return rOrigContext.Length;
1524 : }
1525 :
1526 :
1527 : // -------------------------------------------------------------------------
1528 :
1529 0 : class OutlineAction :
1530 : public Action,
1531 : public TextRenderer,
1532 : private ::boost::noncopyable
1533 : {
1534 : public:
1535 : OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1536 : const ::basegfx::B2DSize& rReliefOffset,
1537 : const ::Color& rReliefColor,
1538 : const ::basegfx::B2DSize& rShadowOffset,
1539 : const ::Color& rShadowColor,
1540 : const ::basegfx::B2DRectangle& rOutlineBounds,
1541 : const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1542 : const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
1543 : const uno::Sequence< double >& rOffsets,
1544 : VirtualDevice& rVDev,
1545 : const CanvasSharedPtr& rCanvas,
1546 : const OutDevState& rState );
1547 : OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1548 : const ::basegfx::B2DSize& rReliefOffset,
1549 : const ::Color& rReliefColor,
1550 : const ::basegfx::B2DSize& rShadowOffset,
1551 : const ::Color& rShadowColor,
1552 : const ::basegfx::B2DRectangle& rOutlineBounds,
1553 : const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1554 : const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
1555 : const uno::Sequence< double >& rOffsets,
1556 : VirtualDevice& rVDev,
1557 : const CanvasSharedPtr& rCanvas,
1558 : const OutDevState& rState,
1559 : const ::basegfx::B2DHomMatrix& rTextTransform );
1560 :
1561 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1562 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1563 : const Subset& rSubset ) const;
1564 :
1565 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1566 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1567 : const Subset& rSubset ) const;
1568 :
1569 : virtual sal_Int32 getActionCount() const;
1570 :
1571 : private:
1572 : // TextRenderer interface
1573 : virtual bool operator()( const rendering::RenderState& rRenderState ) const;
1574 :
1575 : // TODO(P2): This is potentially a real mass object
1576 : // (every character might be a separate TextAction),
1577 : // thus, make it as lightweight as possible. For
1578 : // example, share common RenderState among several
1579 : // TextActions, maybe using maOffsets for the
1580 : // translation.
1581 :
1582 : uno::Reference< rendering::XPolyPolygon2D > mxTextPoly;
1583 :
1584 : /** This vector denotes the index of the start polygon
1585 : for the respective glyph sequence.
1586 :
1587 : To get a polygon index range for a given character
1588 : index i, take [ maPolygonGlyphMap[i],
1589 : maPolygonGlyphMap[i+1] ). Note that this is wrong
1590 : for BiDi
1591 : */
1592 : const ::std::vector< sal_Int32 > maPolygonGlyphMap;
1593 : const uno::Sequence< double > maOffsets;
1594 : const CanvasSharedPtr mpCanvas;
1595 : rendering::RenderState maState;
1596 : double mnOutlineWidth;
1597 : const uno::Sequence< double > maFillColor;
1598 : const tools::TextLineInfo maTextLineInfo;
1599 : ::basegfx::B2DSize maLinesOverallSize;
1600 : const ::basegfx::B2DRectangle maOutlineBounds;
1601 : uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
1602 : const ::basegfx::B2DSize maReliefOffset;
1603 : const ::Color maReliefColor;
1604 : const ::basegfx::B2DSize maShadowOffset;
1605 : const ::Color maShadowColor;
1606 : };
1607 :
1608 0 : double calcOutlineWidth( const OutDevState& rState,
1609 : VirtualDevice& rVDev )
1610 : {
1611 : const ::basegfx::B2DSize aFontSize( 0,
1612 0 : rVDev.GetFont().GetHeight() / 64.0 );
1613 :
1614 : const double nOutlineWidth(
1615 0 : (rState.mapModeTransform * aFontSize).getY() );
1616 :
1617 0 : return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1618 : }
1619 :
1620 0 : OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1621 : const ::basegfx::B2DSize& rReliefOffset,
1622 : const ::Color& rReliefColor,
1623 : const ::basegfx::B2DSize& rShadowOffset,
1624 : const ::Color& rShadowColor,
1625 : const ::basegfx::B2DRectangle& rOutlineBounds,
1626 : const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1627 : const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
1628 : const uno::Sequence< double >& rOffsets,
1629 : VirtualDevice& rVDev,
1630 : const CanvasSharedPtr& rCanvas,
1631 : const OutDevState& rState ) :
1632 : mxTextPoly( rTextPoly ),
1633 : maPolygonGlyphMap( rPolygonGlyphMap ),
1634 : maOffsets( rOffsets ),
1635 : mpCanvas( rCanvas ),
1636 : maState(),
1637 0 : mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1638 : maFillColor(
1639 : ::vcl::unotools::colorToDoubleSequence(
1640 : ::Color( COL_WHITE ),
1641 0 : rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1642 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1643 : maLinesOverallSize(),
1644 : maOutlineBounds( rOutlineBounds ),
1645 : mxTextLines(),
1646 : maReliefOffset( rReliefOffset ),
1647 : maReliefColor( rReliefColor ),
1648 : maShadowOffset( rShadowOffset ),
1649 0 : maShadowColor( rShadowColor )
1650 : {
1651 : initEffectLinePolyPolygon( maLinesOverallSize,
1652 : mxTextLines,
1653 : rCanvas,
1654 : rOffsets,
1655 0 : maTextLineInfo );
1656 :
1657 : init( maState,
1658 : rStartPoint,
1659 : rState,
1660 0 : rCanvas );
1661 0 : }
1662 :
1663 0 : OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1664 : const ::basegfx::B2DSize& rReliefOffset,
1665 : const ::Color& rReliefColor,
1666 : const ::basegfx::B2DSize& rShadowOffset,
1667 : const ::Color& rShadowColor,
1668 : const ::basegfx::B2DRectangle& rOutlineBounds,
1669 : const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1670 : const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
1671 : const uno::Sequence< double >& rOffsets,
1672 : VirtualDevice& rVDev,
1673 : const CanvasSharedPtr& rCanvas,
1674 : const OutDevState& rState,
1675 : const ::basegfx::B2DHomMatrix& rTextTransform ) :
1676 : mxTextPoly( rTextPoly ),
1677 : maPolygonGlyphMap( rPolygonGlyphMap ),
1678 : maOffsets( rOffsets ),
1679 : mpCanvas( rCanvas ),
1680 : maState(),
1681 0 : mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1682 : maFillColor(
1683 : ::vcl::unotools::colorToDoubleSequence(
1684 : ::Color( COL_WHITE ),
1685 0 : rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1686 : maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1687 : maLinesOverallSize(),
1688 : maOutlineBounds( rOutlineBounds ),
1689 : mxTextLines(),
1690 : maReliefOffset( rReliefOffset ),
1691 : maReliefColor( rReliefColor ),
1692 : maShadowOffset( rShadowOffset ),
1693 0 : maShadowColor( rShadowColor )
1694 : {
1695 : initEffectLinePolyPolygon( maLinesOverallSize,
1696 : mxTextLines,
1697 : rCanvas,
1698 : rOffsets,
1699 0 : maTextLineInfo );
1700 :
1701 : init( maState,
1702 : rStartPoint,
1703 : rState,
1704 : rCanvas,
1705 0 : rTextTransform );
1706 0 : }
1707 :
1708 0 : bool OutlineAction::operator()( const rendering::RenderState& rRenderState ) const
1709 : {
1710 0 : const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1711 0 : const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1712 :
1713 0 : rendering::StrokeAttributes aStrokeAttributes;
1714 :
1715 0 : aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1716 0 : aStrokeAttributes.MiterLimit = 1.0;
1717 0 : aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1718 0 : aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1719 0 : aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1720 :
1721 0 : rendering::RenderState aLocalState( rRenderState );
1722 0 : aLocalState.DeviceColor = maFillColor;
1723 :
1724 : // TODO(P1): implement caching
1725 :
1726 : // background of text
1727 0 : rCanvas->fillPolyPolygon( mxTextPoly,
1728 : rViewState,
1729 0 : aLocalState );
1730 :
1731 : // border line of text
1732 0 : rCanvas->strokePolyPolygon( mxTextPoly,
1733 : rViewState,
1734 : rRenderState,
1735 0 : aStrokeAttributes );
1736 :
1737 : // underlines/strikethrough - background
1738 0 : rCanvas->fillPolyPolygon( mxTextLines,
1739 : rViewState,
1740 0 : aLocalState );
1741 : // underlines/strikethrough - border
1742 0 : rCanvas->strokePolyPolygon( mxTextLines,
1743 : rViewState,
1744 : rRenderState,
1745 0 : aStrokeAttributes );
1746 :
1747 0 : return true;
1748 : }
1749 :
1750 0 : bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1751 : {
1752 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
1753 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1754 :
1755 0 : rendering::RenderState aLocalState( maState );
1756 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1757 :
1758 : return renderEffectText( *this,
1759 : aLocalState,
1760 0 : mpCanvas->getViewState(),
1761 0 : mpCanvas->getUNOCanvas(),
1762 : maShadowColor,
1763 : maShadowOffset,
1764 : maReliefColor,
1765 0 : maReliefOffset );
1766 : }
1767 :
1768 0 : class OutlineTextArrayRenderHelper : public TextRenderer
1769 : {
1770 : public:
1771 : OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1772 : const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1773 : const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1774 : const rendering::ViewState& rViewState,
1775 : double nOutlineWidth ) :
1776 : maFillColor(
1777 : ::vcl::unotools::colorToDoubleSequence(
1778 : ::Color( COL_WHITE ),
1779 : rCanvas->getDevice()->getDeviceColorSpace() )),
1780 : mnOutlineWidth( nOutlineWidth ),
1781 : mrCanvas( rCanvas ),
1782 : mrTextPolygon( rTextPolygon ),
1783 : mrLinePolygon( rLinePolygon ),
1784 : mrViewState( rViewState )
1785 : {
1786 : }
1787 :
1788 : // TextRenderer interface
1789 0 : virtual bool operator()( const rendering::RenderState& rRenderState ) const
1790 : {
1791 0 : rendering::StrokeAttributes aStrokeAttributes;
1792 :
1793 0 : aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1794 0 : aStrokeAttributes.MiterLimit = 1.0;
1795 0 : aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1796 0 : aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1797 0 : aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1798 :
1799 0 : rendering::RenderState aLocalState( rRenderState );
1800 0 : aLocalState.DeviceColor = maFillColor;
1801 :
1802 : // TODO(P1): implement caching
1803 :
1804 : // background of text
1805 0 : mrCanvas->fillPolyPolygon( mrTextPolygon,
1806 : mrViewState,
1807 0 : aLocalState );
1808 :
1809 : // border line of text
1810 0 : mrCanvas->strokePolyPolygon( mrTextPolygon,
1811 : mrViewState,
1812 : rRenderState,
1813 0 : aStrokeAttributes );
1814 :
1815 : // underlines/strikethrough - background
1816 0 : mrCanvas->fillPolyPolygon( mrLinePolygon,
1817 : mrViewState,
1818 0 : aLocalState );
1819 : // underlines/strikethrough - border
1820 0 : mrCanvas->strokePolyPolygon( mrLinePolygon,
1821 : mrViewState,
1822 : rRenderState,
1823 0 : aStrokeAttributes );
1824 :
1825 0 : return true;
1826 : }
1827 :
1828 : private:
1829 : const uno::Sequence< double > maFillColor;
1830 : double mnOutlineWidth;
1831 : const uno::Reference< rendering::XCanvas >& mrCanvas;
1832 : const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
1833 : const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1834 : const rendering::ViewState& mrViewState;
1835 : };
1836 :
1837 0 : bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1838 : const Subset& rSubset ) const
1839 : {
1840 : RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::OutlineAction::renderSubset()" );
1841 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::OutlineAction: 0x%X", this );
1842 :
1843 0 : if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1844 0 : return true; // empty range, render nothing
1845 :
1846 : #if 1
1847 : // TODO(F3): Subsetting NYI for outline text!
1848 0 : return render( rTransformation );
1849 : #else
1850 : const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1851 :
1852 : if( rSubset.mnSubsetBegin == 0 &&
1853 : rSubset.mnSubsetEnd == rOrigContext.Length )
1854 : {
1855 : // full range, no need for subsetting
1856 : return render( rTransformation );
1857 : }
1858 :
1859 : rendering::RenderState aLocalState( maState );
1860 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1861 :
1862 :
1863 : // create and setup local Text polygon
1864 : // ===================================
1865 :
1866 : uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1867 :
1868 : // TODO(P3): Provide an API method for that!
1869 :
1870 : if( !xTextLayout.is() )
1871 : return false;
1872 :
1873 : // render everything
1874 : // =================
1875 :
1876 : return renderEffectText(
1877 : OutlineTextArrayRenderHelper(
1878 : xCanvas,
1879 : mnOutlineWidth,
1880 : xTextLayout,
1881 : xTextLines,
1882 : rViewState ),
1883 : aLocalState,
1884 : rViewState,
1885 : xCanvas,
1886 : maShadowColor,
1887 : maShadowOffset,
1888 : maReliefColor,
1889 : maReliefOffset );
1890 : #endif
1891 : }
1892 :
1893 0 : ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1894 : {
1895 0 : rendering::RenderState aLocalState( maState );
1896 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1897 :
1898 : return calcEffectTextBounds( maOutlineBounds,
1899 : ::basegfx::B2DRange( 0,0,
1900 : maLinesOverallSize.getX(),
1901 : maLinesOverallSize.getY() ),
1902 : maReliefOffset,
1903 : maShadowOffset,
1904 : aLocalState,
1905 0 : mpCanvas->getViewState() );
1906 : }
1907 :
1908 0 : ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1909 : const Subset& /*rSubset*/ ) const
1910 : {
1911 : OSL_FAIL( "OutlineAction::getBounds(): Subset not yet supported by this object" );
1912 :
1913 0 : return getBounds( rTransformation );
1914 : }
1915 :
1916 0 : sal_Int32 OutlineAction::getActionCount() const
1917 : {
1918 : // TODO(F3): Subsetting NYI for outline text!
1919 0 : return maOffsets.getLength();
1920 : }
1921 :
1922 :
1923 : // ======================================================================
1924 : //
1925 : // Action factory methods
1926 : //
1927 : // ======================================================================
1928 :
1929 : /** Create an outline action
1930 :
1931 : This method extracts the polygonal outline from the
1932 : text, and creates a properly setup OutlineAction from
1933 : it.
1934 : */
1935 0 : ActionSharedPtr createOutline( const ::basegfx::B2DPoint& rStartPoint,
1936 : const ::basegfx::B2DSize& rReliefOffset,
1937 : const ::Color& rReliefColor,
1938 : const ::basegfx::B2DSize& rShadowOffset,
1939 : const ::Color& rShadowColor,
1940 : const String& rText,
1941 : sal_Int32 nStartPos,
1942 : sal_Int32 nLen,
1943 : const sal_Int32* pDXArray,
1944 : VirtualDevice& rVDev,
1945 : const CanvasSharedPtr& rCanvas,
1946 : const OutDevState& rState,
1947 : const Renderer::Parameters& rParms )
1948 : {
1949 : // operate on raw DX array here (in logical coordinate
1950 : // system), to have a higher resolution
1951 : // PolyPolygon. That polygon is then converted to
1952 : // device coordinate system.
1953 :
1954 : // #i68512# Temporarily switch off font rotation
1955 : // (which is already contained in the render state
1956 : // transformation matrix - otherwise, glyph polygons
1957 : // will be rotated twice)
1958 0 : const ::Font aOrigFont( rVDev.GetFont() );
1959 0 : ::Font aUnrotatedFont( aOrigFont );
1960 0 : aUnrotatedFont.SetOrientation(0);
1961 0 : rVDev.SetFont( aUnrotatedFont );
1962 :
1963 : // TODO(F3): Don't understand parameter semantics of
1964 : // GetTextOutlines()
1965 0 : ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
1966 0 : PolyPolyVector aVCLPolyPolyVector;
1967 : const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
1968 : static_cast<sal_uInt16>(nStartPos),
1969 : static_cast<sal_uInt16>(nStartPos),
1970 : static_cast<sal_uInt16>(nLen),
1971 0 : sal_True, 0, pDXArray ) );
1972 0 : rVDev.SetFont(aOrigFont);
1973 :
1974 0 : if( !bHaveOutlines )
1975 0 : return ActionSharedPtr();
1976 :
1977 0 : ::std::vector< sal_Int32 > aPolygonGlyphMap;
1978 :
1979 : // first glyph starts at polygon index 0
1980 0 : aPolygonGlyphMap.push_back( 0 );
1981 :
1982 : // remove offsetting from mapmode transformation
1983 : // (outline polygons must stay at origin, only need to
1984 : // be scaled)
1985 : ::basegfx::B2DHomMatrix aMapModeTransform(
1986 0 : rState.mapModeTransform );
1987 0 : aMapModeTransform.set(0,2, 0.0);
1988 0 : aMapModeTransform.set(1,2, 0.0);
1989 :
1990 0 : PolyPolyVector::const_iterator aIter( aVCLPolyPolyVector.begin() );
1991 0 : const PolyPolyVector::const_iterator aEnd( aVCLPolyPolyVector.end() );
1992 0 : for( ; aIter!= aEnd; ++aIter )
1993 : {
1994 0 : ::basegfx::B2DPolyPolygon aPolyPolygon;
1995 :
1996 0 : aPolyPolygon = aIter->getB2DPolyPolygon();
1997 0 : aPolyPolygon.transform( aMapModeTransform );
1998 :
1999 : // append result to collecting polypoly
2000 0 : for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2001 : {
2002 : // #i47795# Ensure closed polygons (since
2003 : // FreeType returns the glyph outlines
2004 : // open)
2005 0 : const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2006 0 : const sal_uInt32 nCount( rPoly.count() );
2007 0 : if( nCount<3 ||
2008 0 : rPoly.isClosed() )
2009 : {
2010 : // polygon either degenerate, or
2011 : // already closed.
2012 0 : aResultingPolyPolygon.append( rPoly );
2013 : }
2014 : else
2015 : {
2016 0 : ::basegfx::B2DPolygon aPoly(rPoly);
2017 0 : aPoly.setClosed(true);
2018 :
2019 0 : aResultingPolyPolygon.append( aPoly );
2020 : }
2021 0 : }
2022 :
2023 : // TODO(F3): Depending on the semantics of
2024 : // GetTextOutlines(), this here is wrong!
2025 :
2026 : // calc next glyph index
2027 0 : aPolygonGlyphMap.push_back( aResultingPolyPolygon.count() );
2028 0 : }
2029 :
2030 : const uno::Sequence< double > aCharWidthSeq(
2031 : pDXArray ?
2032 : setupDXArray( pDXArray, nLen, rState ) :
2033 : setupDXArray( rText,
2034 : nStartPos,
2035 : nLen,
2036 : rVDev,
2037 0 : rState ));
2038 : const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2039 : ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2040 0 : rCanvas->getUNOCanvas()->getDevice(),
2041 0 : aResultingPolyPolygon ) );
2042 :
2043 0 : if( rParms.maTextTransformation.is_initialized() )
2044 : {
2045 : return ActionSharedPtr(
2046 : new OutlineAction(
2047 : rStartPoint,
2048 : rReliefOffset,
2049 : rReliefColor,
2050 : rShadowOffset,
2051 : rShadowColor,
2052 : ::basegfx::tools::getRange(aResultingPolyPolygon),
2053 : xTextPoly,
2054 : aPolygonGlyphMap,
2055 : aCharWidthSeq,
2056 : rVDev,
2057 : rCanvas,
2058 : rState,
2059 0 : *rParms.maTextTransformation ) );
2060 : }
2061 : else
2062 : {
2063 : return ActionSharedPtr(
2064 : new OutlineAction(
2065 : rStartPoint,
2066 : rReliefOffset,
2067 : rReliefColor,
2068 : rShadowOffset,
2069 : rShadowColor,
2070 : ::basegfx::tools::getRange(aResultingPolyPolygon),
2071 : xTextPoly,
2072 : aPolygonGlyphMap,
2073 : aCharWidthSeq,
2074 : rVDev,
2075 : rCanvas,
2076 0 : rState ) );
2077 0 : }
2078 : }
2079 :
2080 : } // namespace
2081 :
2082 :
2083 : // ---------------------------------------------------------------------------------
2084 :
2085 0 : ActionSharedPtr TextActionFactory::createTextAction( const ::Point& rStartPoint,
2086 : const ::Size& rReliefOffset,
2087 : const ::Color& rReliefColor,
2088 : const ::Size& rShadowOffset,
2089 : const ::Color& rShadowColor,
2090 : const String& rText,
2091 : sal_Int32 nStartPos,
2092 : sal_Int32 nLen,
2093 : const sal_Int32* pDXArray,
2094 : VirtualDevice& rVDev,
2095 : const CanvasSharedPtr& rCanvas,
2096 : const OutDevState& rState,
2097 : const Renderer::Parameters& rParms,
2098 : bool bSubsettable )
2099 : {
2100 : const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2101 0 : rVDev ) );
2102 : // #143885# maintain (nearly) full precision positioning,
2103 : // by circumventing integer-based OutDev-mapping
2104 : const ::basegfx::B2DPoint aStartPoint(
2105 : rState.mapModeTransform *
2106 0 : ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2107 0 : rStartPoint.Y() + aBaselineOffset.Height()) );
2108 :
2109 : const ::basegfx::B2DSize aReliefOffset(
2110 0 : rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2111 : const ::basegfx::B2DSize aShadowOffset(
2112 0 : rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2113 :
2114 0 : if( rState.isTextOutlineModeSet )
2115 : {
2116 : return createOutline(
2117 : aStartPoint,
2118 : aReliefOffset,
2119 : rReliefColor,
2120 : aShadowOffset,
2121 : rShadowColor,
2122 : rText,
2123 : nStartPos,
2124 : nLen,
2125 : pDXArray,
2126 : rVDev,
2127 : rCanvas,
2128 : rState,
2129 0 : rParms );
2130 : }
2131 :
2132 : // convert DX array to device coordinate system (and
2133 : // create it in the first place, if pDXArray is NULL)
2134 : const uno::Sequence< double > aCharWidths(
2135 : pDXArray ?
2136 : setupDXArray( pDXArray, nLen, rState ) :
2137 : setupDXArray( rText,
2138 : nStartPos,
2139 : nLen,
2140 : rVDev,
2141 0 : rState ));
2142 :
2143 : // determine type of text action to create
2144 : // =======================================
2145 :
2146 0 : const ::Color aEmptyColor( COL_AUTO );
2147 :
2148 : // no DX array, and no need to subset - no need to store
2149 : // DX array, then.
2150 0 : if( !pDXArray && !bSubsettable )
2151 : {
2152 : // effects, or not?
2153 0 : if( !rState.textOverlineStyle &&
2154 0 : !rState.textUnderlineStyle &&
2155 0 : !rState.textStrikeoutStyle &&
2156 0 : rReliefColor == aEmptyColor &&
2157 0 : rShadowColor == aEmptyColor )
2158 : {
2159 : // nope
2160 0 : if( rParms.maTextTransformation.is_initialized() )
2161 : {
2162 : return ActionSharedPtr( new TextAction(
2163 : aStartPoint,
2164 : rText,
2165 : nStartPos,
2166 : nLen,
2167 : rCanvas,
2168 : rState,
2169 0 : *rParms.maTextTransformation ) );
2170 : }
2171 : else
2172 : {
2173 : return ActionSharedPtr( new TextAction(
2174 : aStartPoint,
2175 : rText,
2176 : nStartPos,
2177 : nLen,
2178 : rCanvas,
2179 0 : rState ) );
2180 : }
2181 : }
2182 : else
2183 : {
2184 : // at least one of the effects requested
2185 0 : if( rParms.maTextTransformation.is_initialized() )
2186 : return ActionSharedPtr( new EffectTextAction(
2187 : aStartPoint,
2188 : aReliefOffset,
2189 : rReliefColor,
2190 : aShadowOffset,
2191 : rShadowColor,
2192 : rText,
2193 : nStartPos,
2194 : nLen,
2195 : rVDev,
2196 : rCanvas,
2197 : rState,
2198 0 : *rParms.maTextTransformation ) );
2199 : else
2200 : return ActionSharedPtr( new EffectTextAction(
2201 : aStartPoint,
2202 : aReliefOffset,
2203 : rReliefColor,
2204 : aShadowOffset,
2205 : rShadowColor,
2206 : rText,
2207 : nStartPos,
2208 : nLen,
2209 : rVDev,
2210 : rCanvas,
2211 0 : rState ) );
2212 : }
2213 : }
2214 : else
2215 : {
2216 : // DX array necessary - any effects?
2217 0 : if( !rState.textOverlineStyle &&
2218 0 : !rState.textUnderlineStyle &&
2219 0 : !rState.textStrikeoutStyle &&
2220 0 : rReliefColor == aEmptyColor &&
2221 0 : rShadowColor == aEmptyColor )
2222 : {
2223 : // nope
2224 0 : if( rParms.maTextTransformation.is_initialized() )
2225 : return ActionSharedPtr( new TextArrayAction(
2226 : aStartPoint,
2227 : rText,
2228 : nStartPos,
2229 : nLen,
2230 : aCharWidths,
2231 : rCanvas,
2232 : rState,
2233 0 : *rParms.maTextTransformation ) );
2234 : else
2235 : return ActionSharedPtr( new TextArrayAction(
2236 : aStartPoint,
2237 : rText,
2238 : nStartPos,
2239 : nLen,
2240 : aCharWidths,
2241 : rCanvas,
2242 0 : rState ) );
2243 : }
2244 : else
2245 : {
2246 : // at least one of the effects requested
2247 0 : if( rParms.maTextTransformation.is_initialized() )
2248 : return ActionSharedPtr( new EffectTextArrayAction(
2249 : aStartPoint,
2250 : aReliefOffset,
2251 : rReliefColor,
2252 : aShadowOffset,
2253 : rShadowColor,
2254 : rText,
2255 : nStartPos,
2256 : nLen,
2257 : aCharWidths,
2258 : rVDev,
2259 : rCanvas,
2260 : rState,
2261 0 : *rParms.maTextTransformation ) );
2262 : else
2263 : return ActionSharedPtr( new EffectTextArrayAction(
2264 : aStartPoint,
2265 : aReliefOffset,
2266 : rReliefColor,
2267 : aShadowOffset,
2268 : rShadowColor,
2269 : rText,
2270 : nStartPos,
2271 : nLen,
2272 : aCharWidths,
2273 : rVDev,
2274 : rCanvas,
2275 0 : rState ) );
2276 : }
2277 : }
2278 : #if defined(__GNUC__)
2279 0 : return ActionSharedPtr();
2280 : #endif
2281 : }
2282 : }
2283 66 : }
2284 :
2285 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|