xref: /AOO41X/main/drawinglayer/source/primitive2d/textbreakuphelper.cxx (revision e2bf1e9da236996a1b93912513a09414a2616846)
1ddde725dSArmin Le Grand /**************************************************************
2ddde725dSArmin Le Grand  *
3ddde725dSArmin Le Grand  * Licensed to the Apache Software Foundation (ASF) under one
4ddde725dSArmin Le Grand  * or more contributor license agreements.  See the NOTICE file
5ddde725dSArmin Le Grand  * distributed with this work for additional information
6ddde725dSArmin Le Grand  * regarding copyright ownership.  The ASF licenses this file
7ddde725dSArmin Le Grand  * to you under the Apache License, Version 2.0 (the
8ddde725dSArmin Le Grand  * "License"); you may not use this file except in compliance
9ddde725dSArmin Le Grand  * with the License.  You may obtain a copy of the License at
10ddde725dSArmin Le Grand  *
11ddde725dSArmin Le Grand  *   http:\\www.apache.org\licenses\LICENSE-2.0
12ddde725dSArmin Le Grand  *
13ddde725dSArmin Le Grand  * Unless required by applicable law or agreed to in writing,
14ddde725dSArmin Le Grand  * software distributed under the License is distributed on an
15ddde725dSArmin Le Grand  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16ddde725dSArmin Le Grand  * KIND, either express or implied.  See the License for the
17ddde725dSArmin Le Grand  * specific language governing permissions and limitations
18ddde725dSArmin Le Grand  * under the License.
19ddde725dSArmin Le Grand  *
20ddde725dSArmin Le Grand  *************************************************************/
21ddde725dSArmin Le Grand 
22ddde725dSArmin Le Grand // MARKER(update_precomp.py): autogen include statement, do not remove
23ddde725dSArmin Le Grand #include "precompiled_drawinglayer.hxx"
24ddde725dSArmin Le Grand 
25ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
26ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
27ddde725dSArmin Le Grand #include <com/sun/star/i18n/XBreakIterator.hpp>
28ddde725dSArmin Le Grand #include <comphelper/processfactory.hxx>
29ddde725dSArmin Le Grand #include <com/sun/star/i18n/CharacterIteratorMode.hdl>
30ddde725dSArmin Le Grand #include <com/sun/star/i18n/WordType.hpp>
31ddde725dSArmin Le Grand 
32ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
33ddde725dSArmin Le Grand 
34ddde725dSArmin Le Grand namespace drawinglayer
35ddde725dSArmin Le Grand {
36ddde725dSArmin Le Grand 	namespace primitive2d
37ddde725dSArmin Le Grand 	{
38ddde725dSArmin Le Grand         TextBreakupHelper::TextBreakupHelper(const Primitive2DReference& rxSource)
39ddde725dSArmin Le Grand         :   mxSource(rxSource),
40ddde725dSArmin Le Grand             mxResult(),
41ddde725dSArmin Le Grand             mpSource(dynamic_cast< const TextSimplePortionPrimitive2D* >(rxSource.get())),
42ddde725dSArmin Le Grand             maTextLayouter(),
43ddde725dSArmin Le Grand             maDecTrans(),
44ddde725dSArmin Le Grand             mbNoDXArray()
45ddde725dSArmin Le Grand         {
46ddde725dSArmin Le Grand             if(mpSource)
47ddde725dSArmin Le Grand             {
48ddde725dSArmin Le Grand                 maDecTrans = mpSource->getTextTransform();
49ddde725dSArmin Le Grand                 mbNoDXArray = mpSource->getDXArray().empty();
50ddde725dSArmin Le Grand 
51ddde725dSArmin Le Grand                 if(mbNoDXArray)
52ddde725dSArmin Le Grand                 {
53ddde725dSArmin Le Grand                     // init TextLayouter when no dxarray
54ddde725dSArmin Le Grand 				    maTextLayouter.setFontAttribute(
55ddde725dSArmin Le Grand                         mpSource->getFontAttribute(),
56ddde725dSArmin Le Grand                         maDecTrans.getScale().getX(),
57ddde725dSArmin Le Grand                         maDecTrans.getScale().getY(),
58ddde725dSArmin Le Grand                         mpSource->getLocale());
59ddde725dSArmin Le Grand                 }
60ddde725dSArmin Le Grand             }
61ddde725dSArmin Le Grand         }
62ddde725dSArmin Le Grand 
63ddde725dSArmin Le Grand         TextBreakupHelper::~TextBreakupHelper()
64ddde725dSArmin Le Grand         {
65ddde725dSArmin Le Grand         }
66ddde725dSArmin Le Grand 
67ddde725dSArmin Le Grand         void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength)
68ddde725dSArmin Le Grand         {
69ddde725dSArmin Le Grand             if(mpSource && nLength && !(nIndex == mpSource->getTextPosition() && nLength == mpSource->getTextLength()))
70ddde725dSArmin Le Grand             {
71ddde725dSArmin Le Grand  				// prepare values for new portion
72ddde725dSArmin Le Grand 				basegfx::B2DHomMatrix aNewTransform;
73ddde725dSArmin Le Grand 				::std::vector< double > aNewDXArray;
74ddde725dSArmin Le Grand 				const bool bNewStartIsNotOldStart(nIndex > mpSource->getTextPosition());
75ddde725dSArmin Le Grand 
76ddde725dSArmin Le Grand 				if(!mbNoDXArray)
77ddde725dSArmin Le Grand 				{
78ddde725dSArmin Le Grand 					// prepare new DXArray for the single word
79ddde725dSArmin Le Grand 					aNewDXArray = ::std::vector< double >(
80ddde725dSArmin Le Grand 						mpSource->getDXArray().begin() + (nIndex - mpSource->getTextPosition()),
81ddde725dSArmin Le Grand 						mpSource->getDXArray().begin() + ((nIndex + nLength) - mpSource->getTextPosition()));
82ddde725dSArmin Le Grand 				}
83ddde725dSArmin Le Grand 
84ddde725dSArmin Le Grand 				if(bNewStartIsNotOldStart)
85ddde725dSArmin Le Grand 				{
86ddde725dSArmin Le Grand 					// needs to be moved to a new start position
87ddde725dSArmin Le Grand 					double fOffset(0.0);
88ddde725dSArmin Le Grand 
89ddde725dSArmin Le Grand 					if(mbNoDXArray)
90ddde725dSArmin Le Grand 					{
91ddde725dSArmin Le Grand 						// evaluate using TextLayouter
92ddde725dSArmin Le Grand 						fOffset = maTextLayouter.getTextWidth(mpSource->getText(), mpSource->getTextPosition(), nIndex);
93ddde725dSArmin Le Grand 					}
94ddde725dSArmin Le Grand 					else
95ddde725dSArmin Le Grand 					{
96ddde725dSArmin Le Grand 						// get from DXArray
97*e2bf1e9dSArmin Le Grand 						const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mpSource->getTextPosition()));
98*e2bf1e9dSArmin Le Grand 						fOffset = mpSource->getDXArray()[nIndex2 - 1];
99ddde725dSArmin Le Grand 					}
100ddde725dSArmin Le Grand 
101ddde725dSArmin Le Grand                     // need offset without FontScale for building the new transformation. The
102ddde725dSArmin Le Grand                     // new transformation will be multiplied with the current text transformation
103ddde725dSArmin Le Grand                     // so FontScale would be double
104ddde725dSArmin Le Grand 					double fOffsetNoScale(fOffset);
105ddde725dSArmin Le Grand                     const double fFontScaleX(maDecTrans.getScale().getX());
106ddde725dSArmin Le Grand 
107ddde725dSArmin Le Grand                     if(!basegfx::fTools::equal(fFontScaleX, 1.0)
108ddde725dSArmin Le Grand                         && !basegfx::fTools::equalZero(fFontScaleX))
109ddde725dSArmin Le Grand                     {
110ddde725dSArmin Le Grand                         fOffsetNoScale /= fFontScaleX;
111ddde725dSArmin Le Grand                     }
112ddde725dSArmin Le Grand 
113ddde725dSArmin Le Grand 					// apply needed offset to transformation
114ddde725dSArmin Le Grand                     aNewTransform.translate(fOffsetNoScale, 0.0);
115ddde725dSArmin Le Grand 
116ddde725dSArmin Le Grand 					if(!mbNoDXArray)
117ddde725dSArmin Le Grand 					{
118ddde725dSArmin Le Grand 						// DXArray values need to be corrected with the offset, too. Here,
119ddde725dSArmin Le Grand                         // take the scaled offset since the DXArray is scaled
120ddde725dSArmin Le Grand 						const sal_uInt32 nArraySize(aNewDXArray.size());
121ddde725dSArmin Le Grand 
122ddde725dSArmin Le Grand 						for(sal_uInt32 a(0); a < nArraySize; a++)
123ddde725dSArmin Le Grand 						{
124ddde725dSArmin Le Grand 							aNewDXArray[a] -= fOffset;
125ddde725dSArmin Le Grand 						}
126ddde725dSArmin Le Grand 					}
127ddde725dSArmin Le Grand 				}
128ddde725dSArmin Le Grand 
129ddde725dSArmin Le Grand 				// add text transformation to new transformation
130ddde725dSArmin Le Grand 				aNewTransform = maDecTrans.getB2DHomMatrix() * aNewTransform;
131ddde725dSArmin Le Grand 
132ddde725dSArmin Le Grand                 // callback to allow evtl. changes
133ddde725dSArmin Le Grand                 const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength));
134ddde725dSArmin Le Grand 
135ddde725dSArmin Le Grand                 if(bCreate)
136ddde725dSArmin Le Grand                 {
137ddde725dSArmin Le Grand                     // check if we have a decorated primitive as source
138ddde725dSArmin Le Grand                     const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D =
139ddde725dSArmin Le Grand                         dynamic_cast< const TextDecoratedPortionPrimitive2D* >(mpSource);
140ddde725dSArmin Le Grand 
141ddde725dSArmin Le Grand                     if(pTextDecoratedPortionPrimitive2D)
142ddde725dSArmin Le Grand                     {
143ddde725dSArmin Le Grand                         // create a TextDecoratedPortionPrimitive2D
144ddde725dSArmin Le Grand 	                    rTempResult.push_back(
145ddde725dSArmin Le Grand                             new TextDecoratedPortionPrimitive2D(
146ddde725dSArmin Le Grand                                 aNewTransform,
147ddde725dSArmin Le Grand                                 mpSource->getText(),
148ddde725dSArmin Le Grand 				                nIndex,
149ddde725dSArmin Le Grand 				                nLength,
150ddde725dSArmin Le Grand                                 aNewDXArray,
151ddde725dSArmin Le Grand                                 mpSource->getFontAttribute(),
152ddde725dSArmin Le Grand                                 mpSource->getLocale(),
153ddde725dSArmin Le Grand                                 mpSource->getFontColor(),
154ddde725dSArmin Le Grand 
155ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getOverlineColor(),
156ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getTextlineColor(),
157ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getFontOverline(),
158ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getFontUnderline(),
159ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getUnderlineAbove(),
160ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getTextStrikeout(),
161ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getWordLineMode(),
162ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(),
163ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(),
164ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(),
165ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getTextRelief(),
166ddde725dSArmin Le Grand                                 pTextDecoratedPortionPrimitive2D->getShadow()));
167ddde725dSArmin Le Grand                     }
168ddde725dSArmin Le Grand                     else
169ddde725dSArmin Le Grand                     {
170ddde725dSArmin Le Grand                         // create a SimpleTextPrimitive
171ddde725dSArmin Le Grand 	                    rTempResult.push_back(
172ddde725dSArmin Le Grand                             new TextSimplePortionPrimitive2D(
173ddde725dSArmin Le Grand                                 aNewTransform,
174ddde725dSArmin Le Grand                                 mpSource->getText(),
175ddde725dSArmin Le Grand 				                nIndex,
176ddde725dSArmin Le Grand 				                nLength,
177ddde725dSArmin Le Grand                                 aNewDXArray,
178ddde725dSArmin Le Grand                                 mpSource->getFontAttribute(),
179ddde725dSArmin Le Grand                                 mpSource->getLocale(),
180ddde725dSArmin Le Grand                                 mpSource->getFontColor()));
181ddde725dSArmin Le Grand                     }
182ddde725dSArmin Le Grand                 }
183ddde725dSArmin Le Grand             }
184ddde725dSArmin Le Grand         }
185ddde725dSArmin Le Grand 
186*e2bf1e9dSArmin Le Grand         bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/)
187ddde725dSArmin Le Grand         {
188ddde725dSArmin Le Grand             return true;
189ddde725dSArmin Le Grand         }
190ddde725dSArmin Le Grand 
191ddde725dSArmin Le Grand         void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit)
192ddde725dSArmin Le Grand         {
193ddde725dSArmin Le Grand             if(mpSource && mpSource->getTextLength())
194ddde725dSArmin Le Grand             {
195ddde725dSArmin Le Grand                 Primitive2DVector aTempResult;
196ddde725dSArmin Le Grand                 static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBreakIterator;
197ddde725dSArmin Le Grand 
198ddde725dSArmin Le Grand                 if(!xBreakIterator.is())
199ddde725dSArmin Le Grand                 {
200ddde725dSArmin Le Grand                     ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory());
201ddde725dSArmin Le Grand                     xBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY);
202ddde725dSArmin Le Grand                 }
203ddde725dSArmin Le Grand 
204ddde725dSArmin Le Grand                 if(xBreakIterator.is())
205ddde725dSArmin Le Grand                 {
206ddde725dSArmin Le Grand                     const rtl::OUString& rTxt = mpSource->getText();
207ddde725dSArmin Le Grand                     const sal_Int32 nTextLength(mpSource->getTextLength());
208ddde725dSArmin Le Grand                     const ::com::sun::star::lang::Locale& rLocale = mpSource->getLocale();
209ddde725dSArmin Le Grand                     const sal_Int32 nTextPosition(mpSource->getTextPosition());
210ddde725dSArmin Le Grand                     sal_Int32 nCurrent(nTextPosition);
211ddde725dSArmin Le Grand 
212ddde725dSArmin Le Grand                     switch(aBreakupUnit)
213ddde725dSArmin Le Grand                     {
214ddde725dSArmin Le Grand                         case BreakupUnit_character:
215ddde725dSArmin Le Grand                         {
216ddde725dSArmin Le Grand                             sal_Int32 nDone;
217ddde725dSArmin Le Grand                             sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
218ddde725dSArmin Le Grand                             sal_Int32 a(nTextPosition);
219ddde725dSArmin Le Grand 
220ddde725dSArmin Le Grand                             for(; a < nTextPosition + nTextLength; a++)
221ddde725dSArmin Le Grand                             {
222ddde725dSArmin Le Grand                                 if(a == nNextCellBreak)
223ddde725dSArmin Le Grand                                 {
224ddde725dSArmin Le Grand                                     breakupPortion(aTempResult, nCurrent, a - nCurrent);
225ddde725dSArmin Le Grand                                     nCurrent = a;
226ddde725dSArmin Le Grand                                     nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
227ddde725dSArmin Le Grand                                 }
228ddde725dSArmin Le Grand                             }
229ddde725dSArmin Le Grand 
230ddde725dSArmin Le Grand                             breakupPortion(aTempResult, nCurrent, a - nCurrent);
231ddde725dSArmin Le Grand                             break;
232ddde725dSArmin Le Grand                         }
233ddde725dSArmin Le Grand                         case BreakupUnit_word:
234ddde725dSArmin Le Grand                         {
235ddde725dSArmin Le Grand                             ::com::sun::star::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True));
236ddde725dSArmin Le Grand                             sal_Int32 a(nTextPosition);
237ddde725dSArmin Le Grand 
238ddde725dSArmin Le Grand                             for(; a < nTextPosition + nTextLength; a++)
239ddde725dSArmin Le Grand                             {
240ddde725dSArmin Le Grand                                 if(a == nNextWordBoundary.endPos)
241ddde725dSArmin Le Grand                                 {
242ddde725dSArmin Le Grand                                     breakupPortion(aTempResult, nCurrent, a - nCurrent);
243ddde725dSArmin Le Grand                                     nCurrent = a;
244ddde725dSArmin Le Grand                                     nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True);
245ddde725dSArmin Le Grand                                 }
246ddde725dSArmin Le Grand                             }
247ddde725dSArmin Le Grand 
248ddde725dSArmin Le Grand                             breakupPortion(aTempResult, nCurrent, a - nCurrent);
249ddde725dSArmin Le Grand                             break;
250ddde725dSArmin Le Grand                         }
251ddde725dSArmin Le Grand                         case BreakupUnit_sentence:
252ddde725dSArmin Le Grand                         {
253ddde725dSArmin Le Grand                             sal_Int32 nNextSentenceBreak(xBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
254ddde725dSArmin Le Grand                             sal_Int32 a(nTextPosition);
255ddde725dSArmin Le Grand 
256ddde725dSArmin Le Grand                             for(; a < nTextPosition + nTextLength; a++)
257ddde725dSArmin Le Grand                             {
258ddde725dSArmin Le Grand                                 if(a == nNextSentenceBreak)
259ddde725dSArmin Le Grand                                 {
260ddde725dSArmin Le Grand                                     breakupPortion(aTempResult, nCurrent, a - nCurrent);
261ddde725dSArmin Le Grand                                     nCurrent = a;
262ddde725dSArmin Le Grand                                     nNextSentenceBreak = xBreakIterator->endOfSentence(rTxt, a + 1, rLocale);
263ddde725dSArmin Le Grand                                 }
264ddde725dSArmin Le Grand                             }
265ddde725dSArmin Le Grand 
266ddde725dSArmin Le Grand                             breakupPortion(aTempResult, nCurrent, a - nCurrent);
267ddde725dSArmin Le Grand                             break;
268ddde725dSArmin Le Grand                         }
269ddde725dSArmin Le Grand                     }
270ddde725dSArmin Le Grand                 }
271ddde725dSArmin Le Grand 
272ddde725dSArmin Le Grand                 mxResult = Primitive2DVectorToPrimitive2DSequence(aTempResult);
273ddde725dSArmin Le Grand             }
274ddde725dSArmin Le Grand         }
275ddde725dSArmin Le Grand 
276ddde725dSArmin Le Grand         const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const
277ddde725dSArmin Le Grand         {
278ddde725dSArmin Le Grand             if(mxResult.hasElements())
279ddde725dSArmin Le Grand             {
280ddde725dSArmin Le Grand                 return mxResult;
281ddde725dSArmin Le Grand             }
282ddde725dSArmin Le Grand             else if(mpSource)
283ddde725dSArmin Le Grand             {
284ddde725dSArmin Le Grand                 const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit);
285ddde725dSArmin Le Grand             }
286ddde725dSArmin Le Grand 
287ddde725dSArmin Le Grand             return mxResult;
288ddde725dSArmin Le Grand         }
289ddde725dSArmin Le Grand 
290ddde725dSArmin Le Grand 	} // end of namespace primitive2d
291ddde725dSArmin Le Grand } // end of namespace drawinglayer
292ddde725dSArmin Le Grand 
293ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
294ddde725dSArmin Le Grand // eof
295