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
= tools::PolyPolygon(nCount
);
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
.reset();
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
);
196 nB
= std::hypot(nB
, nDa
);
198 if (nB
== 0) // avoid div / 0
201 nB
= nRange
+ nDa
* ( nFarRange
- nRange
) / nB
;
205 bNote
= nB
> B(rPt1
);
207 bNote
= nB
< B(rPt1
);
209 return( tools::Long( nB
) );
213 void SvxBoundArgs::CheckCut( const Point
& rLst
, const Point
& rNxt
)
216 NotePoint( Cut( nBottom
, rLst
, rNxt
) );
218 NotePoint( Cut( nTop
, rLst
, rNxt
) );
219 if( rLst
.X() == rNxt
.X() || rLst
.Y() == rNxt
.Y() )
223 if( nLowDiff
&& ( ( nCut
& 1 ) || nLast
== 1 || nNext
== 1 ) )
225 nYps
= CalcMax( rLst
, rNxt
, nBottom
, nLower
);
227 NoteFarPoint_( Cut( nYps
, rLst
, rNxt
), nLower
-nYps
, nLowDiff
);
229 if( nUpDiff
&& ( ( nCut
& 2 ) || nLast
== 2 || nNext
== 2 ) )
231 nYps
= CalcMax( rLst
, rNxt
, nTop
, nUpper
);
233 NoteFarPoint_( Cut( nYps
, rLst
, rNxt
), nYps
-nUpper
, nUpDiff
);
237 void SvxBoundArgs::NoteFarPoint_( tools::Long nPa
, tools::Long nPbDiff
, tools::Long nDiff
)
240 double nQuot
= 2 * nDiff
- nPbDiff
;
242 nQuot
= sqrt( nQuot
);
244 nTmpA
= nPa
- tools::Long( nStart
* nQuot
);
245 nPbDiff
= nPa
+ tools::Long( nEnd
* nQuot
);
246 NoteMargin( nTmpA
, nPbDiff
);
249 void SvxBoundArgs::NoteRange( bool bToggle
)
251 DBG_ASSERT( nMax
>= nMin
|| bInner
, "NoteRange: Min > Max?");
257 sal_uInt16 nCount
= pLongArr
->size();
258 DBG_ASSERT( nCount
== 2 * aBoolArr
.size(), "NoteRange: Incompatible Sizes" );
259 while( nIdx
< nCount
&& (*pLongArr
)[ nIdx
] < nMin
)
261 bool bOdd
= (nIdx
% 2) != 0;
262 // No overlap with existing intervals?
263 if( nIdx
== nCount
|| ( !bOdd
&& nMax
< (*pLongArr
)[ nIdx
] ) )
264 { // Then a new one is inserted ...
265 pLongArr
->insert( pLongArr
->begin() + nIdx
, nMin
);
266 pLongArr
->insert( pLongArr
->begin() + nIdx
+ 1, nMax
);
267 aBoolArr
.insert( aBoolArr
.begin() + (nIdx
/2), bToggle
);
270 { // expand an existing interval ...
271 sal_uInt16 nMaxIdx
= nIdx
;
272 // If we end up on a left interval boundary, it must be reduced to nMin.
276 (*pLongArr
)[ nIdx
] = nMin
;
277 while( nMaxIdx
< nCount
&& (*pLongArr
)[ nMaxIdx
] < nMax
)
279 DBG_ASSERT( nMaxIdx
> nIdx
|| nMin
== nMax
, "NoteRange: Funny Situation." );
284 // If we end up on a right interval boundary, it must be raised to nMax.
286 (*pLongArr
)[ nMaxIdx
-- ] = nMax
;
287 // Possible merge of intervals.
288 sal_uInt16 nDiff
= nMaxIdx
- nIdx
;
289 nMaxIdx
= nIdx
/ 2; // From here on is nMaxIdx the Index in BoolArray.
292 pLongArr
->erase( pLongArr
->begin() + nIdx
+ 1, pLongArr
->begin() + nIdx
+ 1 + nDiff
);
294 sal_uInt16 nStop
= nMaxIdx
+ nDiff
;
295 for( sal_uInt16 i
= nMaxIdx
; i
< nStop
; ++i
)
296 bToggle
^= aBoolArr
[ i
];
297 aBoolArr
.erase( aBoolArr
.begin() + nMaxIdx
, aBoolArr
.begin() + (nMaxIdx
+ nDiff
) );
299 DBG_ASSERT( nMaxIdx
< aBoolArr
.size(), "NoteRange: Too much deleted" );
300 aBoolArr
[ nMaxIdx
] = aBoolArr
[ nMaxIdx
] != bToggle
;
304 void SvxBoundArgs::Calc( const tools::PolyPolygon
& rPoly
)
308 for( sal_uInt16 i
= 0; i
< rPoly
.Count(); ++i
)
310 const tools::Polygon
& rPol
= rPoly
[ i
];
311 nCount
= rPol
.GetSize();
314 const Point
& rNull
= rPol
[ 0 ];
315 bClosed
= IsConcat() || ( rNull
== rPol
[ nCount
- 1 ] );
316 nLast
= Area( rNull
);
325 // The first point of the polygon is within the line.
328 if( bMultiple
|| !nAct
)
334 NoteFarPoint( A(rNull
), nLower
- B(rNull
), nLowDiff
);
336 NoteFarPoint( A(rNull
), B(rNull
) - nUpper
, nUpDiff
);
340 if( bMultiple
|| !nAct
)
347 NotePoint( A(rNull
) );
349 nFirst
= 0; // leaving the line in which direction?
350 nAct
= 3; // we are within the line at the moment.
357 const Point
& rLast
= rPol
[ nIdx
- 1 ];
360 const Point
& rNext
= rPol
[ nIdx
];
361 nNext
= Area( rNext
);
362 nCut
= nNext
^ nLast
;
363 sal_uInt16 nOldAct
= nAct
;
365 CheckCut( rLast
, rNext
);
368 NoteUpLow( Cut( nLower
, rLast
, rNext
), 2 );
369 if( nAct
&& nAct
!= nOldAct
)
372 CheckCut( rLast
, rNext
);
377 NoteUpLow( Cut( nUpper
, rLast
, rNext
), 1 );
378 if( nAct
&& nAct
!= nOldAct
)
379 CheckCut( rLast
, rNext
);
383 if( !( nNext
& 12 ) )
387 if( !( nNext
& 12 ) )
390 NotePoint( A(rNext
) );
392 NoteFarPoint( A(rNext
), nLower
-B(rNext
), nLowDiff
);
394 NoteFarPoint( A(rNext
), B(rNext
)-nUpper
, nUpDiff
);
397 if( ++nIdx
== nCount
&& !bClosed
)
399 if( !( nNext
& 12 ) )
405 if( bMultiple
&& IsConcat() )
414 DBG_ASSERT( pLongArr
->empty(), "I said: Simple!" );
419 tools::Long nTmpMin
= nMin
+ 2 * nStart
;
420 tools::Long nTmpMax
= nMax
- 2 * nEnd
;
421 if( nTmpMin
<= nTmpMax
)
423 pLongArr
->push_front(nTmpMax
);
424 pLongArr
->push_front(nTmpMin
);
429 pLongArr
->push_front(nMax
);
430 pLongArr
->push_front(nMin
);
434 else if( !IsConcat() )
438 void SvxBoundArgs::Add()
440 size_t nCount
= aBoolArr
.size();
441 if( nCount
&& ( !bInner
|| !pTextRanger
->IsSimple() ) )
443 bool bDelete
= aBoolArr
.front();
446 sal_uInt16 nLongIdx
= 1;
447 for( size_t nBoolIdx
= 1; nBoolIdx
< nCount
; ++nBoolIdx
)
452 while( nBoolIdx
< nCount
&& !aBoolArr
[ nBoolIdx
++ ] &&
453 (!bInner
|| nBoolIdx
< nCount
) )
455 pLongArr
->erase( pLongArr
->begin() + nLongIdx
, pLongArr
->begin() + nLongIdx
+ next
);
457 nBoolIdx
= nBoolIdx
- next
;
458 nCount
= nCount
- next
;
459 aBoolArr
.erase( aBoolArr
.begin() + nBoolIdx
, aBoolArr
.begin() + (nBoolIdx
+ next
) );
461 aBoolArr
[ nBoolIdx
- 1 ] = false;
462 #if OSL_DEBUG_LEVEL > 1
467 bDelete
= nBoolIdx
< nCount
&& aBoolArr
[ nBoolIdx
];
469 DBG_ASSERT( nLongIdx
== 2*nBoolIdx
+1, "BoundArgs: Array-Idx Confusion" );
470 DBG_ASSERT( aBoolArr
.size()*2 == pLongArr
->size(),
471 "BoundArgs: Array-Count: Confusion" );
474 if( pLongArr
->empty() )
480 pLongArr
->pop_front();
481 pLongArr
->pop_back();
483 // Here the line is held inside a large rectangle for "simple"
484 // contour wrap. Currently (April 1999) the EditEngine evaluates
485 // only the first rectangle. If it one day is able to output a line
486 // in several parts, it may be advisable to delete the following lines.
487 if( pTextRanger
->IsSimple() && pLongArr
->size() > 2 )
488 pLongArr
->erase( pLongArr
->begin() + 1, pLongArr
->end() - 1 );
491 void SvxBoundArgs::Concat( const tools::PolyPolygon
* pPoly
)
494 DBG_ASSERT( pPoly
, "Nothing to do?" );
495 std::deque
<tools::Long
>* pOld
= pLongArr
;
496 pLongArr
= new std::deque
<tools::Long
>;
499 Calc( *pPoly
); // Note that this updates pLongArr, which is why we swapped it out earlier.
500 std::deque
<tools::Long
>::size_type nCount
= pLongArr
->size();
501 std::deque
<tools::Long
>::size_type nIdx
= 0;
502 std::deque
<tools::Long
>::size_type i
= 0;
503 bool bSubtract
= pTextRanger
->IsInner();
506 std::deque
<tools::Long
>::size_type nOldCount
= pOld
->size();
507 if( nIdx
== nOldCount
)
508 { // Reached the end of the old Array...
510 pOld
->insert( pOld
->begin() + nIdx
, pLongArr
->begin() + i
, pLongArr
->end() );
513 tools::Long nLeft
= (*pLongArr
)[ i
++ ];
514 tools::Long nRight
= (*pLongArr
)[ i
++ ];
515 std::deque
<tools::Long
>::size_type nLeftPos
= nIdx
+ 1;
516 while( nLeftPos
< nOldCount
&& nLeft
> (*pOld
)[ nLeftPos
] )
518 if( nLeftPos
>= nOldCount
)
519 { // The current interval belongs to the end of the old array ...
521 pOld
->insert( pOld
->begin() + nOldCount
, pLongArr
->begin() + i
- 2, pLongArr
->end() );
524 std::deque
<tools::Long
>::size_type nRightPos
= nLeftPos
- 1;
525 while( nRightPos
< nOldCount
&& nRight
>= (*pOld
)[ nRightPos
] )
527 if( nRightPos
< nLeftPos
)
528 { // The current interval belongs between two old intervals
530 pOld
->insert( pOld
->begin() + nRightPos
, pLongArr
->begin() + i
- 2, pLongArr
->begin() + i
);
532 else if( bSubtract
) // Subtract, if necessary separate
534 const tools::Long nOld
= (*pOld
)[nLeftPos
- 1];
536 { // Now we split the left part...
537 if( nLeft
- 1 > nOld
)
539 pOld
->insert( pOld
->begin() + nLeftPos
- 1, nOld
);
540 pOld
->insert( pOld
->begin() + nLeftPos
, nLeft
- 1 );
545 if( nRightPos
- nLeftPos
> 1 )
546 pOld
->erase( pOld
->begin() + nLeftPos
, pOld
->begin() + nRightPos
- 1 );
547 if (++nRight
>= (*pOld
)[nLeftPos
])
548 pOld
->erase( pOld
->begin() + nLeftPos
- 1, pOld
->begin() + nLeftPos
+ 1 );
550 (*pOld
)[ nLeftPos
- 1 ] = nRight
;
554 if( nLeft
< (*pOld
)[ nLeftPos
- 1 ] )
555 (*pOld
)[ nLeftPos
- 1 ] = nLeft
;
556 if( nRight
> (*pOld
)[ nRightPos
- 1 ] )
557 (*pOld
)[ nRightPos
- 1 ] = nRight
;
558 if( nRightPos
- nLeftPos
> 1 )
559 pOld
->erase( pOld
->begin() + nLeftPos
, pOld
->begin() + nRightPos
- 1 );
567 /*************************************************************************
568 * SvxBoundArgs::Area returns the area in which the point is located.
569 * 0 = within the line
570 * 1 = below, but within the upper edge
571 * 2 = above, but within the lower edge
572 * 5 = below the upper edge
573 *10 = above the lower edge
574 *************************************************************************/
576 sal_uInt16
SvxBoundArgs::Area( const Point
& rPt
)
578 tools::Long nB
= B( rPt
);
594 /*************************************************************************
595 * lcl_Cut calculates the X-Coordinate of the distance (Pt1-Pt2) at the
597 * It is assumed that the one of the points are located above and the other
598 * one below the Y-Coordinate.
599 *************************************************************************/
601 tools::Long
SvxBoundArgs::Cut( tools::Long nB
, const Point
& rPt1
, const Point
& rPt2
)
603 if( pTextRanger
->IsVertical() )
605 double nQuot
= nB
- rPt1
.X();
606 nQuot
/= ( rPt2
.X() - rPt1
.X() );
607 nQuot
*= ( rPt2
.Y() - rPt1
.Y() );
608 return tools::Long( rPt1
.Y() + nQuot
);
610 double nQuot
= nB
- rPt1
.Y();
611 nQuot
/= ( rPt2
.Y() - rPt1
.Y() );
612 nQuot
*= ( rPt2
.X() - rPt1
.X() );
613 return tools::Long( rPt1
.X() + nQuot
);
616 void SvxBoundArgs::NoteUpLow( tools::Long nA
, const sal_uInt8 nArea
)
620 NoteMargin( nA
, nA
);
623 NoteRange( nArea
!= nAct
);
637 std::deque
<tools::Long
>* TextRanger::GetTextRanges( const Range
& rRange
)
639 DBG_ASSERT( rRange
.Min() || rRange
.Max(), "Zero-Range not allowed, Bye Bye" );
640 //Can we find the result we need in the cache?
641 for (auto & elem
: mRangeCache
)
643 if (elem
.range
== rRange
)
644 return &(elem
.results
);
646 //Calculate a new result
647 RangeCacheItem
rngCache(rRange
);
648 SvxBoundArgs
aArg( this, &(rngCache
.results
), rRange
);
649 aArg
.Calc( maPolyPolygon
);
650 if( mpLinePolyPolygon
)
651 aArg
.Concat( &*mpLinePolyPolygon
);
652 //Add new result to the cache
653 mRangeCache
.push_back(std::move(rngCache
));
654 if (mRangeCache
.size() > nCacheSize
)
655 mRangeCache
.pop_front();
656 return &(mRangeCache
.back().results
);
659 const tools::Rectangle
& TextRanger::GetBoundRect_() const
661 DBG_ASSERT( !mxBound
, "Don't call twice." );
662 mxBound
= maPolyPolygon
.GetBoundRect();
667 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */