xref: /core/oox/source/drawingml/textcharacterproperties.cxx (revision 3eac847927a0cdfa40c3fea38c473ed2ad7faecc)
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 <drawingml/textcharacterproperties.hxx>
21 #include <com/sun/star/lang/Locale.hpp>
22 #include <com/sun/star/awt/FontSlant.hpp>
23 #include <com/sun/star/awt/FontWeight.hpp>
24 #include <com/sun/star/i18n/ScriptType.hpp>
25 #include <comphelper/sequence.hxx>
26 #include <i18nlangtag/languagetag.hxx>
27 #include <i18nlangtag/mslangid.hxx>
28 #include <editeng/escapementitem.hxx>
29 #include <docmodel/uno/UnoComplexColor.hxx>
30 #include <oox/helper/helper.hxx>
31 #include <oox/helper/propertyset.hxx>
32 #include <oox/helper/graphichelper.hxx>
33 #include <oox/core/xmlfilterbase.hxx>
34 #include <oox/drawingml/drawingmltypes.hxx>
35 #include <oox/token/properties.hxx>
36 #include <oox/token/tokens.hxx>
37 
38 using ::oox::core::XmlFilterBase;
39 using namespace ::com::sun::star;
40 using namespace ::com::sun::star::uno;
41 using namespace ::com::sun::star::beans;
42 
43 namespace oox::drawingml {
44 
assignUsed(const TextCharacterProperties & rSourceProps)45 void TextCharacterProperties::assignUsed( const TextCharacterProperties& rSourceProps )
46 {
47     // overwrite all properties existing in rSourceProps
48     maHyperlinkPropertyMap.assignUsed( rSourceProps.maHyperlinkPropertyMap );
49     maLatinFont.assignIfUsed( rSourceProps.maLatinFont );
50     maLatinThemeFont.assignIfUsed( rSourceProps.maLatinThemeFont );
51     maAsianFont.assignIfUsed( rSourceProps.maAsianFont );
52     maAsianThemeFont.assignIfUsed( rSourceProps.maAsianThemeFont );
53     maComplexFont.assignIfUsed( rSourceProps.maComplexFont );
54     maComplexThemeFont.assignIfUsed( rSourceProps.maComplexThemeFont );
55     maSymbolFont.assignIfUsed( rSourceProps.maSymbolFont );
56     maHighlightColor.assignIfUsed( rSourceProps.maHighlightColor );
57     maUnderlineColor.assignIfUsed( rSourceProps.maUnderlineColor );
58     assignIfUsed( moLang, rSourceProps.moLang );
59     assignIfUsed( moHeight, rSourceProps.moHeight );
60     assignIfUsed( moFontScale, rSourceProps.moFontScale);
61     assignIfUsed( moSpacing, rSourceProps.moSpacing );
62     assignIfUsed( moUnderline, rSourceProps.moUnderline );
63     assignIfUsed( moBaseline, rSourceProps.moBaseline );
64     assignIfUsed( moStrikeout, rSourceProps.moStrikeout );
65     assignIfUsed( moCaseMap, rSourceProps.moCaseMap );
66     assignIfUsed( moBold, rSourceProps.moBold );
67     assignIfUsed( moItalic, rSourceProps.moItalic );
68     assignIfUsed( moUnderlineLineFollowText, rSourceProps.moUnderlineLineFollowText );
69     assignIfUsed( moUnderlineFillFollowText, rSourceProps.moUnderlineFillFollowText );
70     assignIfUsed( moTextOutlineProperties, rSourceProps.moTextOutlineProperties);
71 
72     maTextEffectsProperties = rSourceProps.maTextEffectsProperties;
73     mpEffectPropertiesPtr->assignUsed(*rSourceProps.mpEffectPropertiesPtr);
74     maFillProperties.assignUsed( rSourceProps.maFillProperties );
75 }
76 
pushToPropMap(PropertyMap & rPropMap,const XmlFilterBase & rFilter) const77 void TextCharacterProperties::pushToPropMap( PropertyMap& rPropMap, const XmlFilterBase& rFilter ) const
78 {
79     OUString aFontName;
80     sal_Int16 nFontPitch = 0;
81     sal_Int16 nFontFamily = 0;
82 
83     bool bRet = maLatinFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
84     if (!bRet)
85         // In case there is no direct font, try to look it up as a theme reference.
86         bRet = maLatinThemeFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
87     if (bRet)
88     {
89         rPropMap.setProperty( PROP_CharFontName, aFontName);
90         rPropMap.setProperty( PROP_CharFontPitch, nFontPitch);
91         rPropMap.setProperty( PROP_CharFontFamily, nFontFamily);
92     }
93 
94     bRet = maAsianFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
95     if (!bRet)
96         bRet = maAsianThemeFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
97     if (bRet)
98     {
99         rPropMap.setProperty( PROP_CharFontNameAsian, aFontName);
100         rPropMap.setProperty( PROP_CharFontPitchAsian, nFontFamily);
101         rPropMap.setProperty( PROP_CharFontFamilyAsian, nFontPitch);
102     }
103 
104     bRet = maComplexFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
105     if (!bRet)
106         bRet = maComplexThemeFont.getFontData( aFontName, nFontPitch, nFontFamily, nullptr, rFilter );
107     if (bRet)
108     {
109         rPropMap.setProperty( PROP_CharFontNameComplex, aFontName);
110         rPropMap.setProperty( PROP_CharFontPitchComplex, nFontPitch);
111         rPropMap.setProperty( PROP_CharFontFamilyComplex, nFontFamily);
112     }
113 
114     if ( maFillProperties.moFillType.has_value() )
115     {
116         Color aColor = maFillProperties.getBestSolidColor();
117         bool bContoured = false;
118 
119         // noFill doesn't exist for characters. Map noFill to 99% transparency
120         if (maFillProperties.moFillType.value() == XML_noFill)
121             aColor.addTransformation(XML_alpha, 1000);
122 
123         // tdf#137438 Emulate text outline color/transparency.
124         // If the outline color dominates, then use it as the text color.
125         if (moTextOutlineProperties.has_value()
126             && moTextOutlineProperties.value().maLineFill.moFillType.has_value()
127             && moTextOutlineProperties.value().maLineFill.moFillType.value() != XML_noFill)
128         {
129             Color aLineColor = moTextOutlineProperties.value().maLineFill.getBestSolidColor();
130             sal_Int16 nLineTransparency = aLineColor.getTransparency();
131 
132             // tdf#127696 If the text color is white (and the outline color doesn't dominate),
133             //            then this is contoured text in LO.
134             if (nLineTransparency < aColor.getTransparency()
135                 || (bContoured = aColor.getColor(rFilter.getGraphicHelper()) == COL_WHITE))
136                 aColor = std::move(aLineColor);
137         }
138         rPropMap.setProperty(PROP_CharColor, aColor.getColor(rFilter.getGraphicHelper()));
139 
140         // set theme color
141         model::ComplexColor aComplexColor = aColor.getComplexColor();
142         sal_Int32 nToken = Color::getColorMapToken(aColor.getSchemeColorName());
143         if (nToken != -1)
144         {
145             rFilter.getGraphicHelper().getSchemeColorToken(nToken);
146             model::ThemeColorType eThemeColorType = schemeTokenToThemeColorType(nToken);
147             aComplexColor.setThemeColor(eThemeColorType);
148         }
149         rPropMap.setProperty(PROP_CharComplexColor, model::color::createXComplexColor(aComplexColor));
150         rPropMap.setProperty(PROP_CharContoured, bContoured);
151 
152         if (aColor.hasTransparency())
153         {
154             const auto nTransparency = aColor.getTransparency();
155             rPropMap.setProperty(PROP_CharTransparence, nTransparency);
156 
157             // WORKAROUND: Fully transparent white has the same value as COL_AUTO, avoid collision
158             if (nTransparency == 100
159                 && aColor.getColor(rFilter.getGraphicHelper()).GetRGBColor() == COL_AUTO.GetRGBColor())
160                 rPropMap.setProperty(PROP_CharColor, ::Color(ColorTransparency, 0xFFFFFFFE));
161         }
162     }
163 
164     if( moLang.has_value() && !moLang.value().isEmpty() )
165     {
166         LanguageTag aTag(moLang.value());
167         lang::Locale aLocale(aTag.getLocale());
168         switch(MsLangId::getScriptType(aTag.getLanguageType()))
169         {
170             case css::i18n::ScriptType::LATIN:
171                 rPropMap.setProperty( PROP_CharLocale, aLocale);break;
172             case css::i18n::ScriptType::ASIAN:
173                 rPropMap.setProperty( PROP_CharLocaleAsian, aLocale);break;
174             case css::i18n::ScriptType::COMPLEX:
175                 rPropMap.setProperty( PROP_CharLocaleComplex, aLocale);break;
176         }
177     }
178 
179     if( moHeight.has_value() )
180     {
181         float fHeight = GetFontHeight( moHeight.value() );
182         if (moFontScale.has_value())
183             fHeight *= (moFontScale.value() / 100000);
184         rPropMap.setProperty( PROP_CharHeight, fHeight);
185         rPropMap.setProperty( PROP_CharHeightAsian, fHeight);
186         rPropMap.setProperty( PROP_CharHeightComplex, fHeight);
187     }
188 
189     rPropMap.setProperty( PROP_CharKerning, static_cast<sal_Int16>(GetTextSpacingPoint( moSpacing.value_or( 0 ) )));
190 
191     rPropMap.setProperty( PROP_CharUnderline, GetFontUnderline( moUnderline.value_or( XML_none ) ));
192     rPropMap.setProperty( PROP_CharStrikeout, GetFontStrikeout( moStrikeout.value_or( XML_noStrike ) ));
193     rPropMap.setProperty( PROP_CharCaseMap, GetCaseMap( moCaseMap.value_or( XML_none ) ));
194 
195     if( moBaseline.has_value() ) {
196         rPropMap.setProperty( PROP_CharEscapement, sal_Int16(moBaseline.value_or( 0 ) / 1000));
197         rPropMap.setProperty( PROP_CharEscapementHeight, sal_Int8(DFLT_ESC_PROP));
198     } else {
199         rPropMap.setProperty( PROP_CharEscapement, sal_Int16(0));
200         rPropMap.setProperty( PROP_CharEscapementHeight, sal_Int8(100)); // 100%
201     }
202 
203     float fWeight = moBold.value_or( false ) ? awt::FontWeight::BOLD : awt::FontWeight::NORMAL;
204     rPropMap.setProperty( PROP_CharWeight, fWeight);
205     rPropMap.setProperty( PROP_CharWeightAsian, fWeight);
206     rPropMap.setProperty( PROP_CharWeightComplex, fWeight);
207 
208     awt::FontSlant eSlant = moItalic.value_or( false ) ? awt::FontSlant_ITALIC : awt::FontSlant_NONE;
209     rPropMap.setProperty( PROP_CharPosture, eSlant);
210     rPropMap.setProperty( PROP_CharPostureAsian, eSlant);
211     rPropMap.setProperty( PROP_CharPostureComplex, eSlant);
212 
213     bool bUnderlineFillFollowText = moUnderlineFillFollowText.value_or( false );
214     if( moUnderline.has_value() && maUnderlineColor.isUsed() && !bUnderlineFillFollowText )
215     {
216         rPropMap.setProperty( PROP_CharUnderlineHasColor, true);
217         rPropMap.setProperty( PROP_CharUnderlineColor, maUnderlineColor.getColor( rFilter.getGraphicHelper() ));
218         model::ComplexColor aComplexColor = maUnderlineColor.getComplexColor();
219         rPropMap.setProperty( PROP_CharUnderlineComplexColor, model::color::createXComplexColor(aComplexColor));
220     }
221     else
222     {
223         rPropMap.setProperty( PROP_CharUnderlineHasColor, false);
224         rPropMap.setProperty( PROP_CharUnderlineColor, sal_Int32(-1));
225     }
226 
227     if (maHighlightColor.isUsed() && maHighlightColor.getTransparency() != 100)
228     {
229         rPropMap.setProperty(PROP_CharBackColor, maHighlightColor.getColor( rFilter.getGraphicHelper() ));
230         model::ComplexColor aComplexColor = maHighlightColor.getComplexColor();
231         rPropMap.setProperty(PROP_CharBackgroundComplexColor, model::color::createXComplexColor(aComplexColor));
232     }
233     else
234         rPropMap.setProperty( PROP_CharBackColor, sal_Int32(-1));
235 }
236 
pushToGrabBag(PropertySet & rPropSet,const std::vector<PropertyValue> & aVectorOfPropertyValues)237 static void pushToGrabBag( PropertySet& rPropSet, const std::vector<PropertyValue>& aVectorOfPropertyValues )
238 {
239     if (!rPropSet.hasProperty(PROP_CharInteropGrabBag) || aVectorOfPropertyValues.empty())
240         return;
241     Sequence<PropertyValue> aGrabBag;
242     Any aAnyGrabBag = rPropSet.getAnyProperty(PROP_CharInteropGrabBag);
243     aAnyGrabBag >>= aGrabBag;
244 
245     rPropSet.setAnyProperty(PROP_CharInteropGrabBag, Any(comphelper::concatSequences(aGrabBag, aVectorOfPropertyValues)));
246 }
247 
pushToPropSet(PropertySet & rPropSet,const XmlFilterBase & rFilter) const248 void TextCharacterProperties::pushToPropSet( PropertySet& rPropSet, const XmlFilterBase& rFilter ) const
249 {
250     PropertyMap aPropMap;
251     pushToPropMap( aPropMap, rFilter );
252     rPropSet.setProperties( aPropMap );
253     pushToGrabBag(rPropSet, maTextEffectsProperties);
254 }
255 
getCharHeightPoints(float fDefault) const256 float TextCharacterProperties::getCharHeightPoints( float fDefault ) const
257 {
258     return moHeight.has_value() ? GetFontHeight( moHeight.value() ) : fDefault;
259 }
260 
261 } // namespace oox::drawingml
262 
263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
264