1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <editeng/txtrange.hxx>
23 #include <tools/poly.hxx>
24 #include <tools/debug.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/polygon/b2dpolypolygon.hxx>
30 TextRanger::TextRanger( const basegfx::B2DPolyPolygon
& rPolyPolygon
,
31 const basegfx::B2DPolyPolygon
* pLinePolyPolygon
,
32 sal_uInt16 nCacheSz
, sal_uInt16 nLft
, sal_uInt16 nRght
,
33 bool bSimpl
, bool bInnr
, bool bVert
) :
34 maPolyPolygon( rPolyPolygon
.count() ),
35 nCacheSize( nCacheSz
),
45 sal_uInt32
nCount(rPolyPolygon
.count());
47 for(sal_uInt32
i(0); i
< nCount
; i
++)
49 const basegfx::B2DPolygon
aCandidate(rPolyPolygon
.getB2DPolygon(i
).getDefaultAdaptiveSubdivision());
50 nPointCount
+= aCandidate
.count();
51 maPolyPolygon
.Insert( tools::Polygon(aCandidate
), static_cast<sal_uInt16
>(i
) );
54 if( pLinePolyPolygon
)
56 nCount
= pLinePolyPolygon
->count();
57 mpLinePolyPolygon
.reset( new tools::PolyPolygon() );
59 for(sal_uInt32
i(0); i
< nCount
; i
++)
61 const basegfx::B2DPolygon
aCandidate(pLinePolyPolygon
->getB2DPolygon(i
).getDefaultAdaptiveSubdivision());
62 nPointCount
+= aCandidate
.count();
63 mpLinePolyPolygon
->Insert( tools::Polygon(aCandidate
), static_cast<sal_uInt16
>(i
) );
67 mpLinePolyPolygon
= nullptr;
71 TextRanger::~TextRanger()
76 /* TextRanger::SetVertical(..)
77 If there's is a change in the writing direction,
78 the cache has to be cleared.
80 void TextRanger::SetVertical( bool bNew
)
82 if( IsVertical() != bNew
)
91 //! SvxBoundArgs is used to perform temporary calculations on a range array.
92 //! Temporary instances are created in TextRanger::GetTextRanges()
95 std::vector
<bool> aBoolArr
;
96 std::deque
<tools::Long
>* pLongArr
;
97 TextRanger
*pTextRanger
;
103 tools::Long nLowDiff
;
118 void NoteRange( bool bToggle
);
119 tools::Long
Cut( tools::Long nY
, const Point
& rPt1
, const Point
& rPt2
);
121 void NoteFarPoint_( tools::Long nPx
, tools::Long nPyDiff
, tools::Long nDiff
);
122 void NoteFarPoint( tools::Long nPx
, tools::Long nPyDiff
, tools::Long nDiff
)
123 { if( nDiff
) NoteFarPoint_( nPx
, nPyDiff
, nDiff
); }
124 tools::Long
CalcMax( const Point
& rPt1
, const Point
& rPt2
, tools::Long nRange
, tools::Long nFar
);
125 void CheckCut( const Point
& rLst
, const Point
& rNxt
);
126 tools::Long
A( const Point
& rP
) const { return bRotate
? rP
.Y() : rP
.X(); }
127 tools::Long
B( const Point
& rP
) const { return bRotate
? rP
.X() : rP
.Y(); }
129 SvxBoundArgs( TextRanger
* pRanger
, std::deque
<tools::Long
>* pLong
, const Range
& rRange
);
130 void NotePoint( const tools::Long nA
) { NoteMargin( nA
- nStart
, nA
+ nEnd
); }
131 void NoteMargin( const tools::Long nL
, const tools::Long nR
)
132 { if( nMin
> nL
) nMin
= nL
; if( nMax
< nR
) nMax
= nR
; }
133 sal_uInt16
Area( const Point
& rPt
);
134 void NoteUpLow( tools::Long nA
, const sal_uInt8 nArea
);
135 void Calc( const tools::PolyPolygon
& rPoly
);
136 void Concat( const tools::PolyPolygon
* pPoly
);
138 void NoteLast() { if( bMultiple
) NoteRange( nAct
== nFirst
); }
139 void SetConcat( const bool bNew
){ bConcat
= bNew
; }
140 bool IsConcat() const { return bConcat
; }
145 SvxBoundArgs::SvxBoundArgs( TextRanger
* pRanger
, std::deque
<tools::Long
>* pLong
,
146 const Range
& rRange
)
148 , pTextRanger(pRanger
)
152 , nBottom(rRange
.Max())
159 , bInner(pRanger
->IsInner())
160 , bMultiple(bInner
|| !pRanger
->IsSimple())
162 , bRotate(pRanger
->IsVertical())
166 nStart
= pRanger
->GetUpper();
167 nEnd
= pRanger
->GetLower();
168 nLowDiff
= pRanger
->GetLeft();
169 nUpDiff
= pRanger
->GetRight();
173 nStart
= pRanger
->GetLeft();
174 nEnd
= pRanger
->GetRight();
175 nLowDiff
= pRanger
->GetUpper();
176 nUpDiff
= pRanger
->GetLower();
178 nUpper
= nTop
- nUpDiff
;
179 nLower
= nBottom
+ nLowDiff
;
183 tools::Long
SvxBoundArgs::CalcMax( const Point
& rPt1
, const Point
& rPt2
,
184 tools::Long nRange
, tools::Long nFarRange
)
186 double nDa
= Cut( nRange
, rPt1
, rPt2
) - Cut( nFarRange
, rPt1
, rPt2
);
197 nB
= nRange
+ nDa
* ( nFarRange
- nRange
) / sqrt( nB
);
201 bNote
= nB
> B(rPt1
);
203 bNote
= nB
< B(rPt1
);
205 return( tools::Long( nB
) );
209 void SvxBoundArgs::CheckCut( const Point
& rLst
, const Point
& rNxt
)
212 NotePoint( Cut( nBottom
, rLst
, rNxt
) );
214 NotePoint( Cut( nTop
, rLst
, rNxt
) );
215 if( rLst
.X() == rNxt
.X() || rLst
.Y() == rNxt
.Y() )
219 if( nLowDiff
&& ( ( nCut
& 1 ) || nLast
== 1 || nNext
== 1 ) )
221 nYps
= CalcMax( rLst
, rNxt
, nBottom
, nLower
);
223 NoteFarPoint_( Cut( nYps
, rLst
, rNxt
), nLower
-nYps
, nLowDiff
);
225 if( nUpDiff
&& ( ( nCut
& 2 ) || nLast
== 2 || nNext
== 2 ) )
227 nYps
= CalcMax( rLst
, rNxt
, nTop
, nUpper
);
229 NoteFarPoint_( Cut( nYps
, rLst
, rNxt
), nYps
-nUpper
, nUpDiff
);
233 void SvxBoundArgs::NoteFarPoint_( tools::Long nPa
, tools::Long nPbDiff
, tools::Long nDiff
)
236 double nQuot
= 2 * nDiff
- nPbDiff
;
238 nQuot
= sqrt( nQuot
);
240 nTmpA
= nPa
- tools::Long( nStart
* nQuot
);
241 nPbDiff
= nPa
+ tools::Long( nEnd
* nQuot
);
242 NoteMargin( nTmpA
, nPbDiff
);
245 void SvxBoundArgs::NoteRange( bool bToggle
)
247 DBG_ASSERT( nMax
>= nMin
|| bInner
, "NoteRange: Min > Max?");
253 sal_uInt16 nCount
= pLongArr
->size();
254 DBG_ASSERT( nCount
== 2 * aBoolArr
.size(), "NoteRange: Incompatible Sizes" );
255 while( nIdx
< nCount
&& (*pLongArr
)[ nIdx
] < nMin
)
257 bool bOdd
= (nIdx
% 2) != 0;
258 // No overlap with existing intervals?
259 if( nIdx
== nCount
|| ( !bOdd
&& nMax
< (*pLongArr
)[ nIdx
] ) )
260 { // Then a new one is inserted ...
261 pLongArr
->insert( pLongArr
->begin() + nIdx
, nMin
);
262 pLongArr
->insert( pLongArr
->begin() + nIdx
+ 1, nMax
);
263 aBoolArr
.insert( aBoolArr
.begin() + (nIdx
/2), bToggle
);
266 { // expand an existing interval ...
267 sal_uInt16 nMaxIdx
= nIdx
;
268 // If we end up on a left interval boundary, it must be reduced to nMin.
272 (*pLongArr
)[ nIdx
] = nMin
;
273 while( nMaxIdx
< nCount
&& (*pLongArr
)[ nMaxIdx
] < nMax
)
275 DBG_ASSERT( nMaxIdx
> nIdx
|| nMin
== nMax
, "NoteRange: Funny Situation." );
280 // If we end up on a right interval boundary, it must be raised to nMax.
282 (*pLongArr
)[ nMaxIdx
-- ] = nMax
;
283 // Possible merge of intervals.
284 sal_uInt16 nDiff
= nMaxIdx
- nIdx
;
285 nMaxIdx
= nIdx
/ 2; // From here on is nMaxIdx the Index in BoolArray.
288 pLongArr
->erase( pLongArr
->begin() + nIdx
+ 1, pLongArr
->begin() + nIdx
+ 1 + nDiff
);
290 sal_uInt16 nStop
= nMaxIdx
+ nDiff
;
291 for( sal_uInt16 i
= nMaxIdx
; i
< nStop
; ++i
)
292 bToggle
^= aBoolArr
[ i
];
293 aBoolArr
.erase( aBoolArr
.begin() + nMaxIdx
, aBoolArr
.begin() + (nMaxIdx
+ nDiff
) );
295 DBG_ASSERT( nMaxIdx
< aBoolArr
.size(), "NoteRange: Too much deleted" );
296 aBoolArr
[ nMaxIdx
] = aBoolArr
[ nMaxIdx
] != bToggle
;
300 void SvxBoundArgs::Calc( const tools::PolyPolygon
& rPoly
)
304 for( sal_uInt16 i
= 0; i
< rPoly
.Count(); ++i
)
306 const tools::Polygon
& rPol
= rPoly
[ i
];
307 nCount
= rPol
.GetSize();
310 const Point
& rNull
= rPol
[ 0 ];
311 bClosed
= IsConcat() || ( rNull
== rPol
[ nCount
- 1 ] );
312 nLast
= Area( rNull
);
321 // The first point of the polygon is within the line.
324 if( bMultiple
|| !nAct
)
330 NoteFarPoint( A(rNull
), nLower
- B(rNull
), nLowDiff
);
332 NoteFarPoint( A(rNull
), B(rNull
) - nUpper
, nUpDiff
);
336 if( bMultiple
|| !nAct
)
343 NotePoint( A(rNull
) );
345 nFirst
= 0; // leaving the line in which direction?
346 nAct
= 3; // we are within the line at the moment.
353 const Point
& rLast
= rPol
[ nIdx
- 1 ];
356 const Point
& rNext
= rPol
[ nIdx
];
357 nNext
= Area( rNext
);
358 nCut
= nNext
^ nLast
;
359 sal_uInt16 nOldAct
= nAct
;
361 CheckCut( rLast
, rNext
);
364 NoteUpLow( Cut( nLower
, rLast
, rNext
), 2 );
365 if( nAct
&& nAct
!= nOldAct
)
368 CheckCut( rLast
, rNext
);
373 NoteUpLow( Cut( nUpper
, rLast
, rNext
), 1 );
374 if( nAct
&& nAct
!= nOldAct
)
375 CheckCut( rLast
, rNext
);
379 if( !( nNext
& 12 ) )
383 if( !( nNext
& 12 ) )
386 NotePoint( A(rNext
) );
388 NoteFarPoint( A(rNext
), nLower
-B(rNext
), nLowDiff
);
390 NoteFarPoint( A(rNext
), B(rNext
)-nUpper
, nUpDiff
);
393 if( ++nIdx
== nCount
&& !bClosed
)
395 if( !( nNext
& 12 ) )
401 if( bMultiple
&& IsConcat() )
410 DBG_ASSERT( pLongArr
->empty(), "I said: Simple!" );
415 tools::Long nTmpMin
= nMin
+ 2 * nStart
;
416 tools::Long nTmpMax
= nMax
- 2 * nEnd
;
417 if( nTmpMin
<= nTmpMax
)
419 pLongArr
->push_front(nTmpMax
);
420 pLongArr
->push_front(nTmpMin
);
425 pLongArr
->push_front(nMax
);
426 pLongArr
->push_front(nMin
);
430 else if( !IsConcat() )
434 void SvxBoundArgs::Add()
436 size_t nCount
= aBoolArr
.size();
437 if( nCount
&& ( !bInner
|| !pTextRanger
->IsSimple() ) )
439 bool bDelete
= aBoolArr
.front();
442 sal_uInt16 nLongIdx
= 1;
443 for( size_t nBoolIdx
= 1; nBoolIdx
< nCount
; ++nBoolIdx
)
448 while( nBoolIdx
< nCount
&& !aBoolArr
[ nBoolIdx
++ ] &&
449 (!bInner
|| nBoolIdx
< nCount
) )
451 pLongArr
->erase( pLongArr
->begin() + nLongIdx
, pLongArr
->begin() + nLongIdx
+ next
);
453 nBoolIdx
= nBoolIdx
- next
;
454 nCount
= nCount
- next
;
455 aBoolArr
.erase( aBoolArr
.begin() + nBoolIdx
, aBoolArr
.begin() + (nBoolIdx
+ next
) );
457 aBoolArr
[ nBoolIdx
- 1 ] = false;
458 #if OSL_DEBUG_LEVEL > 1
463 bDelete
= nBoolIdx
< nCount
&& aBoolArr
[ nBoolIdx
];
465 DBG_ASSERT( nLongIdx
== 2*nBoolIdx
+1, "BoundArgs: Array-Idx Confusion" );
466 DBG_ASSERT( aBoolArr
.size()*2 == pLongArr
->size(),
467 "BoundArgs: Array-Count: Confusion" );
470 if( pLongArr
->empty() )
476 pLongArr
->pop_front();
477 pLongArr
->pop_back();
479 // Here the line is held inside a large rectangle for "simple"
480 // contour wrap. Currently (April 1999) the EditEngine evaluates
481 // only the first rectangle. If it one day is able to output a line
482 // in several parts, it may be advisable to delete the following lines.
483 if( pTextRanger
->IsSimple() && pLongArr
->size() > 2 )
484 pLongArr
->erase( pLongArr
->begin() + 1, pLongArr
->end() - 1 );
487 void SvxBoundArgs::Concat( const tools::PolyPolygon
* pPoly
)
490 DBG_ASSERT( pPoly
, "Nothing to do?" );
491 std::deque
<tools::Long
>* pOld
= pLongArr
;
492 pLongArr
= new std::deque
<tools::Long
>;
495 Calc( *pPoly
); // Note that this updates pLongArr, which is why we swapped it out earlier.
496 std::deque
<tools::Long
>::size_type nCount
= pLongArr
->size();
497 std::deque
<tools::Long
>::size_type nIdx
= 0;
498 std::deque
<tools::Long
>::size_type i
= 0;
499 bool bSubtract
= pTextRanger
->IsInner();
502 std::deque
<tools::Long
>::size_type nOldCount
= pOld
->size();
503 if( nIdx
== nOldCount
)
504 { // Reached the end of the old Array...
506 pOld
->insert( pOld
->begin() + nIdx
, pLongArr
->begin() + i
, pLongArr
->end() );
509 tools::Long nLeft
= (*pLongArr
)[ i
++ ];
510 tools::Long nRight
= (*pLongArr
)[ i
++ ];
511 std::deque
<tools::Long
>::size_type nLeftPos
= nIdx
+ 1;
512 while( nLeftPos
< nOldCount
&& nLeft
> (*pOld
)[ nLeftPos
] )
514 if( nLeftPos
>= nOldCount
)
515 { // The current interval belongs to the end of the old array ...
517 pOld
->insert( pOld
->begin() + nOldCount
, pLongArr
->begin() + i
- 2, pLongArr
->end() );
520 std::deque
<tools::Long
>::size_type nRightPos
= nLeftPos
- 1;
521 while( nRightPos
< nOldCount
&& nRight
>= (*pOld
)[ nRightPos
] )
523 if( nRightPos
< nLeftPos
)
524 { // The current interval belongs between two old intervals
526 pOld
->insert( pOld
->begin() + nRightPos
, pLongArr
->begin() + i
- 2, pLongArr
->begin() + i
);
528 else if( bSubtract
) // Subtract, if necessary separate
530 const tools::Long nOld
= (*pOld
)[nLeftPos
- 1];
532 { // Now we split the left part...
533 if( nLeft
- 1 > nOld
)
535 pOld
->insert( pOld
->begin() + nLeftPos
- 1, nOld
);
536 pOld
->insert( pOld
->begin() + nLeftPos
, nLeft
- 1 );
541 if( nRightPos
- nLeftPos
> 1 )
542 pOld
->erase( pOld
->begin() + nLeftPos
, pOld
->begin() + nRightPos
- 1 );
543 if (++nRight
>= (*pOld
)[nLeftPos
])
544 pOld
->erase( pOld
->begin() + nLeftPos
- 1, pOld
->begin() + nLeftPos
+ 1 );
546 (*pOld
)[ nLeftPos
- 1 ] = nRight
;
550 if( nLeft
< (*pOld
)[ nLeftPos
- 1 ] )
551 (*pOld
)[ nLeftPos
- 1 ] = nLeft
;
552 if( nRight
> (*pOld
)[ nRightPos
- 1 ] )
553 (*pOld
)[ nRightPos
- 1 ] = nRight
;
554 if( nRightPos
- nLeftPos
> 1 )
555 pOld
->erase( pOld
->begin() + nLeftPos
, pOld
->begin() + nRightPos
- 1 );
563 /*************************************************************************
564 * SvxBoundArgs::Area returns the area in which the point is located.
565 * 0 = within the line
566 * 1 = below, but within the upper edge
567 * 2 = above, but within the lower edge
568 * 5 = below the upper edge
569 *10 = above the lower edge
570 *************************************************************************/
572 sal_uInt16
SvxBoundArgs::Area( const Point
& rPt
)
574 tools::Long nB
= B( rPt
);
590 /*************************************************************************
591 * lcl_Cut calculates the X-Coordinate of the distance (Pt1-Pt2) at the
593 * It is assumed that the one of the points are located above and the other
594 * one below the Y-Coordinate.
595 *************************************************************************/
597 tools::Long
SvxBoundArgs::Cut( tools::Long nB
, const Point
& rPt1
, const Point
& rPt2
)
599 if( pTextRanger
->IsVertical() )
601 double nQuot
= nB
- rPt1
.X();
602 nQuot
/= ( rPt2
.X() - rPt1
.X() );
603 nQuot
*= ( rPt2
.Y() - rPt1
.Y() );
604 return tools::Long( rPt1
.Y() + nQuot
);
606 double nQuot
= nB
- rPt1
.Y();
607 nQuot
/= ( rPt2
.Y() - rPt1
.Y() );
608 nQuot
*= ( rPt2
.X() - rPt1
.X() );
609 return tools::Long( rPt1
.X() + nQuot
);
612 void SvxBoundArgs::NoteUpLow( tools::Long nA
, const sal_uInt8 nArea
)
616 NoteMargin( nA
, nA
);
619 NoteRange( nArea
!= nAct
);
633 std::deque
<tools::Long
>* TextRanger::GetTextRanges( const Range
& rRange
)
635 DBG_ASSERT( rRange
.Min() || rRange
.Max(), "Zero-Range not allowed, Bye Bye" );
636 //Can we find the result we need in the cache?
637 for (auto & elem
: mRangeCache
)
639 if (elem
.range
== rRange
)
640 return &(elem
.results
);
642 //Calculate a new result
643 RangeCacheItem
rngCache(rRange
);
644 SvxBoundArgs
aArg( this, &(rngCache
.results
), rRange
);
645 aArg
.Calc( maPolyPolygon
);
646 if( mpLinePolyPolygon
)
647 aArg
.Concat( mpLinePolyPolygon
.get() );
648 //Add new result to the cache
649 mRangeCache
.push_back(std::move(rngCache
));
650 if (mRangeCache
.size() > nCacheSize
)
651 mRangeCache
.pop_front();
652 return &(mRangeCache
.back().results
);
655 const tools::Rectangle
& TextRanger::GetBoundRect_() const
657 DBG_ASSERT( !mxBound
, "Don't call twice." );
658 mxBound
= maPolyPolygon
.GetBoundRect();
663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */