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/textparagraphpropertiescontext.hxx>
21 
22 #include <com/sun/star/text/WritingMode2.hpp>
23 #include <com/sun/star/style/ParagraphAdjust.hpp>
24 #include <com/sun/star/xml/sax/SAXException.hpp>
25 
26 #include <svx/unopage.hxx>
27 #include <sal/log.hxx>
28 
29 #include <drawingml/colorchoicecontext.hxx>
30 #include <drawingml/textcharacterpropertiescontext.hxx>
31 #include <drawingml/fillproperties.hxx>
32 #include <oox/helper/attributelist.hxx>
33 #include "textspacingcontext.hxx"
34 #include "texttabstoplistcontext.hxx"
35 #include <oox/token/namespaces.hxx>
36 #include <oox/token/properties.hxx>
37 #include <oox/token/tokens.hxx>
38 
39 using namespace ::oox::core;
40 using namespace ::com::sun::star::uno;
41 using namespace ::com::sun::star::xml::sax;
42 using namespace ::com::sun::star::style;
43 using namespace ::com::sun::star::text;
44 
45 namespace oox { namespace drawingml {
46 
47 // CT_TextParagraphProperties
TextParagraphPropertiesContext(ContextHandler2Helper const & rParent,const AttributeList & rAttribs,TextParagraphProperties & rTextParagraphProperties)48 TextParagraphPropertiesContext::TextParagraphPropertiesContext( ContextHandler2Helper const & rParent,
49                                                                 const AttributeList& rAttribs,
50                                                                 TextParagraphProperties& rTextParagraphProperties )
51 : ContextHandler2( rParent )
52 , mrTextParagraphProperties( rTextParagraphProperties )
53 , mrBulletList( rTextParagraphProperties.getBulletList() )
54 {
55     OUString sValue;
56 
57     PropertyMap& rPropertyMap( mrTextParagraphProperties.getTextParagraphPropertyMap() );
58 
59     // ST_TextAlignType
60     if ( rAttribs.hasAttribute( XML_algn ) )
61     {
62         mrTextParagraphProperties.getParaAdjust() = GetParaAdjust( rAttribs.getToken( XML_algn, XML_l ) );
63     }
64     // TODO see to do the same with RubyAdjust
65 
66     // ST_Coordinate32
67 //  sValue = rAttribs.getString( XML_defTabSz ).get();    SJ: we need to be able to set the default tab size for each text object,
68 //                                                          this is possible at the moment only for the whole document.
69 //  sal_Int32 nDefTabSize = ( sValue.getLength() == 0 ? 0 : GetCoordinate(  sValue ) );
70     // TODO
71 
72 //  bool bEaLineBrk = rAttribs.getBool( XML_eaLnBrk, true );
73     if ( rAttribs.hasAttribute( XML_latinLnBrk ) )
74     {
75         bool bLatinLineBrk = rAttribs.getBool( XML_latinLnBrk, true );
76         rPropertyMap.setProperty( PROP_ParaIsHyphenation, bLatinLineBrk);
77     }
78     // TODO see what to do with Asian hyphenation
79 
80     // ST_TextFontAlignType
81     // TODO
82 //  sal_Int32 nFontAlign = rAttribs.getToken( XML_fontAlgn, XML_base );
83 
84     if ( rAttribs.hasAttribute( XML_hangingPunct ) )
85     {
86         bool bHangingPunct = rAttribs.getBool( XML_hangingPunct, false );
87         rPropertyMap.setProperty( PROP_ParaIsHangingPunctuation, bHangingPunct);
88     }
89 
90   // ST_Coordinate
91     if ( rAttribs.hasAttribute( XML_indent ) )
92     {
93         sValue = rAttribs.getString( XML_indent ).get();
94         mrTextParagraphProperties.getFirstLineIndentation() = boost::optional< sal_Int32 >( sValue.isEmpty() ? 0 : GetCoordinate( sValue ) );
95     }
96 
97   // ST_TextIndentLevelType
98     // -1 is an invalid value and denote the lack of level
99     sal_Int32 nLevel = rAttribs.getInteger( XML_lvl, 0 );
100     if( nLevel > 8 || nLevel < 0 )
101     {
102         nLevel = 0;
103     }
104 
105     mrTextParagraphProperties.setLevel( static_cast< sal_Int16 >( nLevel ) );
106 
107     char name[] = "Outline X";
108     name[8] = static_cast<char>( '1' + nLevel );
109     const OUString sStyleNameValue( OUString::createFromAscii( name ) );
110     mrBulletList.setStyleName( sStyleNameValue );
111 
112     // ST_TextMargin
113     // ParaLeftMargin
114     if ( rAttribs.hasAttribute( XML_marL ) )
115     {
116         sValue = rAttribs.getString( XML_marL ).get();
117         mrTextParagraphProperties.getParaLeftMargin() = boost::optional< sal_Int32 >( sValue.isEmpty() ? 0 : GetCoordinate( sValue ) );
118     }
119 
120     // ParaRightMargin
121     if ( rAttribs.hasAttribute( XML_marR ) )
122     {
123         sValue = rAttribs.getString( XML_marR ).get();
124         sal_Int32 nMarR  = sValue.isEmpty() ? 0 : GetCoordinate( sValue ) ;
125         rPropertyMap.setProperty( PROP_ParaRightMargin, nMarR);
126     }
127 
128     if ( rAttribs.hasAttribute( XML_rtl ) )
129     {
130         bool bRtl = rAttribs.getBool( XML_rtl, false );
131         rPropertyMap.setProperty( PROP_WritingMode, ( bRtl ? WritingMode2::RL_TB : WritingMode2::LR_TB ));
132     }
133 }
134 
~TextParagraphPropertiesContext()135 TextParagraphPropertiesContext::~TextParagraphPropertiesContext()
136 {
137     PropertyMap& rPropertyMap( mrTextParagraphProperties.getTextParagraphPropertyMap() );
138     if ( mrTextParagraphProperties.getLineSpacing().bHasValue )
139         rPropertyMap.setProperty( PROP_ParaLineSpacing, mrTextParagraphProperties.getLineSpacing().toLineSpacing());
140     else
141         rPropertyMap.setProperty( PROP_ParaLineSpacing, css::style::LineSpacing( css::style::LineSpacingMode::PROP, 100 ));
142 
143     ::std::vector< TabStop >::size_type nTabCount = maTabList.size();
144     if( nTabCount != 0 )
145     {
146         Sequence< TabStop > aSeq( nTabCount );
147         TabStop * aArray = aSeq.getArray();
148         OSL_ENSURE( aArray != nullptr, "sequence array is NULL" );
149         ::std::copy( maTabList.begin(), maTabList.end(), aArray );
150         rPropertyMap.setProperty( PROP_ParaTabStops, aSeq);
151     }
152 
153     if (mxBlipProps.get() && mxBlipProps->mxFillGraphic.is())
154         mrBulletList.setGraphic(mxBlipProps->mxFillGraphic);
155 
156     if( mrBulletList.is() )
157         rPropertyMap.setProperty( PROP_IsNumbering, true);
158     sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
159     rPropertyMap.setProperty( PROP_NumberingLevel, nLevel);
160     rPropertyMap.setProperty( PROP_NumberingIsNumber, true);
161 
162     if( mrTextParagraphProperties.getParaAdjust() )
163         rPropertyMap.setProperty( PROP_ParaAdjust, mrTextParagraphProperties.getParaAdjust().get());
164 }
165 
onCreateContext(sal_Int32 aElementToken,const AttributeList & rAttribs)166 ContextHandlerRef TextParagraphPropertiesContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
167 {
168     switch( aElementToken )
169     {
170         case A_TOKEN( lnSpc ):          // CT_TextSpacing
171             return new TextSpacingContext( *this, mrTextParagraphProperties.getLineSpacing() );
172         case A_TOKEN( spcBef ):         // CT_TextSpacing
173             return new TextSpacingContext( *this, mrTextParagraphProperties.getParaTopMargin() );
174         case A_TOKEN( spcAft ):         // CT_TextSpacing
175             return new TextSpacingContext( *this, mrTextParagraphProperties.getParaBottomMargin() );
176         // EG_TextBulletColor
177         case A_TOKEN( buClrTx ):        // CT_TextBulletColorFollowText ???
178             mrBulletList.mbBulletColorFollowText <<= true;
179             break;
180         case A_TOKEN( buClr ):          // CT_Color
181             return new ColorContext( *this, *mrBulletList.maBulletColorPtr );
182         // EG_TextBulletSize
183         case A_TOKEN( buSzTx ):         // CT_TextBulletSizeFollowText
184             mrBulletList.setBulletSize(100);
185             break;
186         case A_TOKEN( buSzPct ):        // CT_TextBulletSizePercent
187             mrBulletList.setBulletSize( std::lround( GetPercent( rAttribs.getString( XML_val ).get() ) / 1000.f ) );
188             break;
189         case A_TOKEN( buSzPts ):        // CT_TextBulletSizePoint
190             mrBulletList.setBulletSize(0);
191             mrBulletList.setFontSize( static_cast<sal_Int16>(GetTextSize( rAttribs.getString( XML_val ).get() ) ) );
192             break;
193 
194         // EG_TextBulletTypeface
195         case A_TOKEN( buFontTx ):       // CT_TextBulletTypefaceFollowText
196             mrBulletList.mbBulletFontFollowText <<= true;
197             break;
198         case A_TOKEN( buFont ):         // CT_TextFont
199             mrBulletList.maBulletFont.setAttributes( rAttribs );
200             break;
201 
202         // EG_TextBullet
203         case A_TOKEN( buNone ):         // CT_TextNoBullet
204             mrBulletList.setNone();
205             break;
206         case A_TOKEN( buAutoNum ):      // CT_TextAutonumberBullet
207         {
208             try {
209                 sal_Int32 nType = rAttribs.getToken( XML_type, 0 );
210                 sal_Int32 nStartAt = rAttribs.getInteger( XML_startAt, 1 );
211                 if( nStartAt > 32767 )
212                 {
213                     nStartAt = 32767;
214                 }
215                 else if( nStartAt < 1 )
216                 {
217                     nStartAt = 1;
218                 }
219                 mrBulletList.setStartAt( nStartAt );
220                 mrBulletList.setType( nType );
221             }
222             catch(SAXException& /* e */ )
223             {
224                 SAL_WARN("oox", "OOX: SAXException in XML_buAutoNum");
225             }
226             break;
227         }
228         case A_TOKEN( buChar ):         // CT_TextCharBullet
229             try {
230 
231                 mrBulletList.setBulletChar( rAttribs.getString( XML_char ).get() );
232                 mrBulletList.setSuffixNone();
233             }
234             catch(SAXException& /* e */)
235             {
236                 SAL_WARN("oox", "OOX: SAXException in XML_buChar");
237             }
238             break;
239         case A_TOKEN( buBlip ):         // CT_TextBlipBullet
240             {
241                 mxBlipProps.reset( new BlipFillProperties );
242                 return new BlipFillContext( *this, rAttribs, *mxBlipProps );
243             }
244         case A_TOKEN( tabLst ):         // CT_TextTabStopList
245             return new TextTabStopListContext( *this, maTabList );
246         case A_TOKEN( defRPr ):         // CT_TextCharacterProperties
247             return new TextCharacterPropertiesContext( *this, rAttribs, mrTextParagraphProperties.getTextCharacterProperties() );
248         case W_TOKEN( jc ):
249             {
250                 OptValue< OUString > oParaAdjust = rAttribs.getString( W_TOKEN(val) );
251                 if( oParaAdjust.has() && !oParaAdjust.get().isEmpty() )
252                 {
253                     const OUString& sParaAdjust = oParaAdjust.get();
254                     if( sParaAdjust == "left" )
255                         mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_LEFT);
256                     else if ( sParaAdjust == "right" )
257                         mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_RIGHT);
258                     else if ( sParaAdjust == "center" )
259                         mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_CENTER);
260                     else if ( sParaAdjust == "both" )
261                         mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_BLOCK);
262                 }
263             }
264             break;
265         case W_TOKEN( spacing ):
266             {
267                 // Spacing before
268                 if( !rAttribs.getBool(W_TOKEN(beforeAutospacing), false) )
269                 {
270                     OptValue<sal_Int32> oBefore = rAttribs.getInteger(W_TOKEN(before));
271                     if (oBefore.has())
272                     {
273                         TextSpacing& rSpacing = mrTextParagraphProperties.getParaTopMargin();
274                         rSpacing.nUnit = TextSpacing::Unit::Points;
275                         rSpacing.nValue = TWIPS_TO_MM(oBefore.get());
276                         rSpacing.bHasValue = true;
277                     }
278                     else
279                     {
280                         OptValue<sal_Int32> oBeforeLines = rAttribs.getInteger(W_TOKEN(beforeLines));
281                         if (oBeforeLines.has())
282                         {
283                             TextSpacing& rSpacing = mrTextParagraphProperties.getParaTopMargin();
284                             rSpacing.nUnit = TextSpacing::Unit::Percent;
285                             rSpacing.nValue = oBeforeLines.get() * MAX_PERCENT / 100;
286                             rSpacing.bHasValue = true;
287                         }
288                     }
289                 }
290 
291                 // Spacing after
292                 if( !rAttribs.getBool(W_TOKEN(afterAutospacing), false) )
293                 {
294                     OptValue<sal_Int32> oAfter = rAttribs.getInteger(W_TOKEN(after));
295                     if (oAfter.has())
296                     {
297                         TextSpacing& rSpacing = mrTextParagraphProperties.getParaBottomMargin();
298                         rSpacing.nUnit = TextSpacing::Unit::Points;
299                         rSpacing.nValue = TWIPS_TO_MM(oAfter.get());
300                         rSpacing.bHasValue = true;
301                     }
302                     else
303                     {
304                         OptValue<sal_Int32> oAfterLines = rAttribs.getInteger(W_TOKEN(afterLines));
305                         if (oAfterLines.has())
306                         {
307                             TextSpacing& rSpacing = mrTextParagraphProperties.getParaBottomMargin();
308                             rSpacing.nUnit = TextSpacing::Unit::Percent;
309                             rSpacing.nValue = oAfterLines.get() * MAX_PERCENT / 100;
310                             rSpacing.bHasValue = true;
311                         }
312                     }
313                 }
314 
315                 // Line spacing
316                 OptValue<OUString> oLineRule = rAttribs.getString(W_TOKEN(lineRule));
317                 OptValue<sal_Int32> oLineSpacing = rAttribs.getInteger(W_TOKEN(line));
318                 if (oLineSpacing.has())
319                 {
320                     TextSpacing& rLineSpacing = mrTextParagraphProperties.getLineSpacing();
321                     if( !oLineRule.has() || oLineRule.get() == "auto" )
322                     {
323                         rLineSpacing.nUnit = TextSpacing::Unit::Percent;
324                         rLineSpacing.nValue = oLineSpacing.get() * MAX_PERCENT / 240;
325                     }
326                     else
327                     {
328                         rLineSpacing.nUnit = TextSpacing::Unit::Points;
329                         rLineSpacing.nValue = TWIPS_TO_MM(oLineSpacing.get());
330                     }
331                     rLineSpacing.bHasValue = true;
332                 }
333             }
334             break;
335         default:
336             SAL_WARN("oox", "TextParagraphPropertiesContext::onCreateContext: unhandled element: " << getBaseToken(aElementToken));
337     }
338     return this;
339 }
340 
341 } }
342 
343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
344