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