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 String& rText,
42 : xub_StrLen aTextPosition,
43 : xub_StrLen aTextLength,
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 : aTextPosition,
53 : aTextLength,
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, aTextPosition, aTextLength);
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 : 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 :
312 : // TextSimplePortionPrimitive2D parameters
313 : const basegfx::B2DHomMatrix& rNewTransform,
314 : const String& rText,
315 : xub_StrLen aTextPosition,
316 : xub_StrLen aTextLength,
317 : const ::std::vector< double >& rDXArray,
318 : const attribute::FontAttribute& rFontAttribute,
319 : const ::com::sun::star::lang::Locale& rLocale,
320 : const basegfx::BColor& rFontColor,
321 :
322 : // local parameters
323 : const basegfx::BColor& rOverlineColor,
324 : const basegfx::BColor& rTextlineColor,
325 : TextLine eFontOverline,
326 : TextLine eFontUnderline,
327 : bool bUnderlineAbove,
328 : TextStrikeout eTextStrikeout,
329 : bool bWordLineMode,
330 : TextEmphasisMark eTextEmphasisMark,
331 : bool bEmphasisMarkAbove,
332 : bool bEmphasisMarkBelow,
333 : TextRelief eTextRelief,
334 : bool bShadow)
335 : : TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttribute, rLocale, rFontColor),
336 : maOverlineColor(rOverlineColor),
337 : maTextlineColor(rTextlineColor),
338 : meFontOverline(eFontOverline),
339 : meFontUnderline(eFontUnderline),
340 : meTextStrikeout(eTextStrikeout),
341 : meTextEmphasisMark(eTextEmphasisMark),
342 : meTextRelief(eTextRelief),
343 : mbUnderlineAbove(bUnderlineAbove),
344 : mbWordLineMode(bWordLineMode),
345 : mbEmphasisMarkAbove(bEmphasisMarkAbove),
346 : mbEmphasisMarkBelow(bEmphasisMarkBelow),
347 0 : mbShadow(bShadow)
348 : {
349 0 : }
350 :
351 0 : bool TextDecoratedPortionPrimitive2D::decoratedIsNeeded() const
352 : {
353 0 : return (TEXT_LINE_NONE != getFontOverline()
354 0 : || TEXT_LINE_NONE != getFontUnderline()
355 0 : || TEXT_STRIKEOUT_NONE != getTextStrikeout()
356 0 : || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark()
357 0 : || TEXT_RELIEF_NONE != getTextRelief()
358 0 : || getShadow());
359 : }
360 :
361 0 : bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
362 : {
363 0 : if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
364 : {
365 0 : const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive;
366 :
367 0 : return (getOverlineColor() == rCompare.getOverlineColor()
368 0 : && getTextlineColor() == rCompare.getTextlineColor()
369 0 : && getFontOverline() == rCompare.getFontOverline()
370 0 : && getFontUnderline() == rCompare.getFontUnderline()
371 0 : && getTextStrikeout() == rCompare.getTextStrikeout()
372 0 : && getTextEmphasisMark() == rCompare.getTextEmphasisMark()
373 0 : && getTextRelief() == rCompare.getTextRelief()
374 0 : && getUnderlineAbove() == rCompare.getUnderlineAbove()
375 0 : && getWordLineMode() == rCompare.getWordLineMode()
376 0 : && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
377 0 : && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
378 0 : && getShadow() == rCompare.getShadow());
379 : }
380 :
381 0 : return false;
382 : }
383 :
384 : // #i96475#
385 : // Added missing implementation. Decorations may (will) stick out of the text's
386 : // inking area, so add them if needed
387 0 : basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
388 : {
389 0 : if(decoratedIsNeeded())
390 : {
391 : // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
392 : // the own local decomposition for computation and thus creates all necessary
393 : // geometric objects
394 0 : return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
395 : }
396 : else
397 : {
398 : // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
399 0 : return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
400 : }
401 : }
402 :
403 : // provide unique ID
404 0 : ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
405 :
406 : } // end of namespace primitive2d
407 : } // end of namespace drawinglayer
408 :
409 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|