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 : #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
21 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
22 : #include <drawinglayer/attribute/strokeattribute.hxx>
23 : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
24 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 : #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
26 : #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
27 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
28 : #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
29 : #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
30 : #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
31 :
32 :
33 :
34 : namespace drawinglayer
35 : {
36 : namespace primitive2d
37 : {
38 0 : void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
39 : std::vector< Primitive2DReference >& rTarget,
40 : basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans,
41 : const OUString& rText,
42 : sal_Int32 nTextPosition,
43 : sal_Int32 nTextLength,
44 : const ::std::vector< double >& rDXArray,
45 : const attribute::FontAttribute& rFontAttribute) const
46 : {
47 : // create the SimpleTextPrimitive needed in any case
48 : rTarget.push_back(Primitive2DReference(
49 : new TextSimplePortionPrimitive2D(
50 : rDecTrans.getB2DHomMatrix(),
51 : rText,
52 : nTextPosition,
53 : nTextLength,
54 : rDXArray,
55 : rFontAttribute,
56 0 : getLocale(),
57 0 : getFontColor())));
58 :
59 : // see if something else needs to be done
60 0 : const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
61 0 : const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
62 0 : const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
63 :
64 0 : if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
65 : {
66 : // common preparations
67 0 : TextLayouterDevice aTextLayouter;
68 :
69 : // TextLayouterDevice is needed to get metrics for text decorations like
70 : // underline/strikeout/emphasis marks from it. For setup, the font size is needed
71 : aTextLayouter.setFontAttribute(
72 0 : getFontAttribute(),
73 0 : rDecTrans.getScale().getX(),
74 0 : rDecTrans.getScale().getY(),
75 0 : getLocale());
76 :
77 : // get text width
78 0 : double fTextWidth(0.0);
79 :
80 0 : if(rDXArray.empty())
81 : {
82 0 : fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
83 : }
84 : else
85 : {
86 0 : fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
87 0 : const double fFontScaleX(rDecTrans.getScale().getX());
88 :
89 0 : if(!basegfx::fTools::equal(fFontScaleX, 1.0)
90 0 : && !basegfx::fTools::equalZero(fFontScaleX))
91 : {
92 : // need to take FontScaling out of the DXArray
93 0 : fTextWidth /= fFontScaleX;
94 : }
95 : }
96 :
97 0 : if(bOverlineUsed)
98 : {
99 : // create primitive geometry for overline
100 : rTarget.push_back(Primitive2DReference(
101 : new TextLinePrimitive2D(
102 : rDecTrans.getB2DHomMatrix(),
103 : fTextWidth,
104 : aTextLayouter.getOverlineOffset(),
105 : aTextLayouter.getOverlineHeight(),
106 : getFontOverline(),
107 0 : getOverlineColor())));
108 : }
109 :
110 0 : if(bUnderlineUsed)
111 : {
112 : // create primitive geometry for underline
113 : rTarget.push_back(Primitive2DReference(
114 : new TextLinePrimitive2D(
115 : rDecTrans.getB2DHomMatrix(),
116 : fTextWidth,
117 : aTextLayouter.getUnderlineOffset(),
118 : aTextLayouter.getUnderlineHeight(),
119 : getFontUnderline(),
120 0 : getTextlineColor())));
121 : }
122 :
123 0 : if(bStrikeoutUsed)
124 : {
125 : // create primitive geometry for strikeout
126 0 : if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
127 : {
128 : // strikeout with character
129 0 : const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
130 :
131 : rTarget.push_back(Primitive2DReference(
132 : new TextCharacterStrikeoutPrimitive2D(
133 : rDecTrans.getB2DHomMatrix(),
134 : fTextWidth,
135 0 : getFontColor(),
136 : aStrikeoutChar,
137 0 : getFontAttribute(),
138 0 : getLocale())));
139 : }
140 : else
141 : {
142 : // strikeout with geometry
143 : rTarget.push_back(Primitive2DReference(
144 : new TextGeometryStrikeoutPrimitive2D(
145 : rDecTrans.getB2DHomMatrix(),
146 : fTextWidth,
147 0 : getFontColor(),
148 : aTextLayouter.getUnderlineHeight(),
149 : aTextLayouter.getStrikeoutOffset(),
150 0 : getTextStrikeout())));
151 : }
152 0 : }
153 : }
154 :
155 : // TODO: Handle Font Emphasis Above/Below
156 0 : }
157 :
158 0 : Primitive2DSequence TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
159 : {
160 0 : if(getWordLineMode())
161 : {
162 : // support for single word mode; split to single word primitives
163 : // using TextBreakupHelper
164 0 : const TextBreakupHelper aTextBreakupHelper(*this);
165 0 : const Primitive2DSequence aBroken(aTextBreakupHelper.getResult(BreakupUnit_word));
166 :
167 0 : if(aBroken.hasElements())
168 : {
169 : // was indeed split to several words, use as result
170 0 : return aBroken;
171 : }
172 : else
173 : {
174 : // no split, was already a single word. Continue to
175 : // decompse local entity
176 0 : }
177 : }
178 0 : std::vector< Primitive2DReference > aNewPrimitives;
179 0 : basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
180 0 : Primitive2DSequence aRetval;
181 :
182 : // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
183 : // Strikeout, etc...
184 : // prepare new font attributes WITHOUT outline
185 : const attribute::FontAttribute aNewFontAttribute(
186 0 : getFontAttribute().getFamilyName(),
187 0 : getFontAttribute().getStyleName(),
188 0 : getFontAttribute().getWeight(),
189 0 : getFontAttribute().getSymbol(),
190 0 : getFontAttribute().getVertical(),
191 0 : getFontAttribute().getItalic(),
192 0 : getFontAttribute().getMonospaced(),
193 : false, // no outline anymore, handled locally
194 0 : getFontAttribute().getRTL(),
195 0 : getFontAttribute().getBiDiStrong());
196 :
197 : // handle as one word
198 0 : impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
199 :
200 : // convert to Primitive2DSequence
201 0 : const sal_uInt32 nMemberCount(aNewPrimitives.size());
202 :
203 0 : if(nMemberCount)
204 : {
205 0 : aRetval.realloc(nMemberCount);
206 :
207 0 : for(sal_uInt32 a(0); a < nMemberCount; a++)
208 : {
209 0 : aRetval[a] = aNewPrimitives[a];
210 : }
211 : }
212 :
213 : // Handle Shadow, Outline and TextRelief
214 0 : if(aRetval.hasElements())
215 : {
216 : // outline AND shadow depend on NO TextRelief (see dialog)
217 0 : const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
218 0 : const bool bHasShadow(!bHasTextRelief && getShadow());
219 0 : const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
220 :
221 0 : if(bHasShadow || bHasTextRelief || bHasOutline)
222 : {
223 0 : Primitive2DReference aShadow;
224 :
225 0 : if(bHasShadow)
226 : {
227 : // create shadow with current content (in aRetval). Text shadow
228 : // is constant, relative to font size, rotated with the text and has a
229 : // constant color.
230 : // shadow parameter values
231 : static double fFactor(1.0 / 24.0);
232 0 : const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
233 0 : static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
234 :
235 : // preapare shadow transform matrix
236 : const basegfx::B2DHomMatrix aShadowTransform(basegfx::tools::createTranslateB2DHomMatrix(
237 0 : fTextShadowOffset, fTextShadowOffset));
238 :
239 : // create shadow primitive
240 0 : aShadow = Primitive2DReference(new ShadowPrimitive2D(
241 : aShadowTransform,
242 : aShadowColor,
243 0 : aRetval));
244 : }
245 :
246 0 : if(bHasTextRelief)
247 : {
248 : // create emboss using an own helper primitive since this will
249 : // be view-dependent
250 0 : const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
251 0 : const bool bDefaultTextColor(aBBlack == getFontColor());
252 0 : TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED);
253 :
254 0 : if(bDefaultTextColor)
255 : {
256 0 : if(TEXT_RELIEF_ENGRAVED == getTextRelief())
257 : {
258 0 : aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT;
259 : }
260 : else
261 : {
262 0 : aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT;
263 : }
264 : }
265 : else
266 : {
267 0 : if(TEXT_RELIEF_ENGRAVED == getTextRelief())
268 : {
269 0 : aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED;
270 : }
271 : else
272 : {
273 0 : aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED;
274 : }
275 : }
276 :
277 : Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
278 : aRetval,
279 0 : aDecTrans.getTranslate(),
280 : aDecTrans.getRotate(),
281 0 : aTextEffectStyle2D));
282 0 : aRetval = Primitive2DSequence(&aNewTextEffect, 1);
283 : }
284 0 : else if(bHasOutline)
285 : {
286 : // create outline using an own helper primitive since this will
287 : // be view-dependent
288 : Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
289 : aRetval,
290 0 : aDecTrans.getTranslate(),
291 : aDecTrans.getRotate(),
292 0 : TEXTEFFECTSTYLE2D_OUTLINE));
293 0 : aRetval = Primitive2DSequence(&aNewTextEffect, 1);
294 : }
295 :
296 0 : if(aShadow.is())
297 : {
298 : // put shadow in front if there is one to paint timely before
299 : // but placed behind content
300 0 : const Primitive2DSequence aContent(aRetval);
301 0 : aRetval = Primitive2DSequence(&aShadow, 1);
302 0 : appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent);
303 0 : }
304 : }
305 : }
306 :
307 0 : return aRetval;
308 : }
309 :
310 0 : TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
311 : // TextSimplePortionPrimitive2D parameters
312 : const basegfx::B2DHomMatrix& rNewTransform,
313 : const OUString& rText,
314 : sal_Int32 nTextPosition,
315 : sal_Int32 nTextLength,
316 : const ::std::vector< double >& rDXArray,
317 : const attribute::FontAttribute& rFontAttribute,
318 : const ::com::sun::star::lang::Locale& rLocale,
319 : const basegfx::BColor& rFontColor,
320 :
321 : // local parameters
322 : const basegfx::BColor& rOverlineColor,
323 : const basegfx::BColor& rTextlineColor,
324 : TextLine eFontOverline,
325 : TextLine eFontUnderline,
326 : bool bUnderlineAbove,
327 : TextStrikeout eTextStrikeout,
328 : bool bWordLineMode,
329 : TextEmphasisMark eTextEmphasisMark,
330 : bool bEmphasisMarkAbove,
331 : bool bEmphasisMarkBelow,
332 : TextRelief eTextRelief,
333 : bool bShadow)
334 : : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, rDXArray, rFontAttribute, rLocale, rFontColor),
335 : maOverlineColor(rOverlineColor),
336 : maTextlineColor(rTextlineColor),
337 : meFontOverline(eFontOverline),
338 : meFontUnderline(eFontUnderline),
339 : meTextStrikeout(eTextStrikeout),
340 : meTextEmphasisMark(eTextEmphasisMark),
341 : meTextRelief(eTextRelief),
342 : mbUnderlineAbove(bUnderlineAbove),
343 : mbWordLineMode(bWordLineMode),
344 : mbEmphasisMarkAbove(bEmphasisMarkAbove),
345 : mbEmphasisMarkBelow(bEmphasisMarkBelow),
346 0 : mbShadow(bShadow)
347 : {
348 0 : }
349 :
350 0 : bool TextDecoratedPortionPrimitive2D::decoratedIsNeeded() const
351 : {
352 0 : return (TEXT_LINE_NONE != getFontOverline()
353 0 : || TEXT_LINE_NONE != getFontUnderline()
354 0 : || TEXT_STRIKEOUT_NONE != getTextStrikeout()
355 0 : || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark()
356 0 : || TEXT_RELIEF_NONE != getTextRelief()
357 0 : || getShadow());
358 : }
359 :
360 0 : bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
361 : {
362 0 : if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
363 : {
364 0 : const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive;
365 :
366 0 : return (getOverlineColor() == rCompare.getOverlineColor()
367 0 : && getTextlineColor() == rCompare.getTextlineColor()
368 0 : && getFontOverline() == rCompare.getFontOverline()
369 0 : && getFontUnderline() == rCompare.getFontUnderline()
370 0 : && getTextStrikeout() == rCompare.getTextStrikeout()
371 0 : && getTextEmphasisMark() == rCompare.getTextEmphasisMark()
372 0 : && getTextRelief() == rCompare.getTextRelief()
373 0 : && getUnderlineAbove() == rCompare.getUnderlineAbove()
374 0 : && getWordLineMode() == rCompare.getWordLineMode()
375 0 : && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
376 0 : && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
377 0 : && getShadow() == rCompare.getShadow());
378 : }
379 :
380 0 : return false;
381 : }
382 :
383 : // #i96475#
384 : // Added missing implementation. Decorations may (will) stick out of the text's
385 : // inking area, so add them if needed
386 0 : basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
387 : {
388 0 : if(decoratedIsNeeded())
389 : {
390 : // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
391 : // the own local decomposition for computation and thus creates all necessary
392 : // geometric objects
393 0 : return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
394 : }
395 : else
396 : {
397 : // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
398 0 : return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
399 : }
400 : }
401 :
402 : // provide unique ID
403 0 : ImplPrimitive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
404 :
405 : } // end of namespace primitive2d
406 : } // end of namespace drawinglayer
407 :
408 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|