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 <oox/helper/attributelist.hxx>
21 #include <oox/vml/vmlformatting.hxx>
22 #include <oox/vml/vmltextboxcontext.hxx>
23 #include <oox/vml/vmlshape.hxx>
24 #include <oox/token/namespaces.hxx>
25 #include <oox/token/tokens.hxx>
26 #include <osl/diagnose.h>
27 #include <sal/log.hxx>
28
29 namespace oox::vml {
30
31 using ::oox::core::ContextHandler2;
32 using ::oox::core::ContextHandler2Helper;
33 using ::oox::core::ContextHandlerRef;
34
TextPortionContext(ContextHandler2Helper const & rParent,TextBox & rTextBox,TextParagraphModel const & rParagraph,const TextFontModel & rParentFont,sal_Int32 nElement,const AttributeList & rAttribs)35 TextPortionContext::TextPortionContext( ContextHandler2Helper const & rParent,
36 TextBox& rTextBox, TextParagraphModel const & rParagraph, const TextFontModel& rParentFont,
37 sal_Int32 nElement, const AttributeList& rAttribs ) :
38 ContextHandler2( rParent ),
39 mrTextBox( rTextBox ),
40 maParagraph( rParagraph ),
41 maFont( rParentFont ),
42 mnInitialPortions( rTextBox.getPortionCount() )
43 {
44 switch( nElement )
45 {
46 case XML_font:
47 maFont.moName = rAttribs.getXString( XML_face );
48 maFont.moColor = rAttribs.getXString( XML_color );
49 maFont.monSize = rAttribs.getInteger( XML_size );
50 break;
51 case XML_u:
52 OSL_ENSURE( !maFont.monUnderline, "TextPortionContext::TextPortionContext - nested <u> elements" );
53 maFont.monUnderline = (rAttribs.getToken( XML_class, XML_TOKEN_INVALID ) == XML_font4) ? XML_double : XML_single;
54 break;
55 case XML_sub:
56 case XML_sup:
57 OSL_ENSURE( !maFont.monEscapement, "TextPortionContext::TextPortionContext - nested <sub> or <sup> elements" );
58 maFont.monEscapement = nElement;
59 break;
60 case XML_b:
61 OSL_ENSURE( !maFont.mobBold, "TextPortionContext::TextPortionContext - nested <b> elements" );
62 maFont.mobBold = true;
63 break;
64 case XML_i:
65 OSL_ENSURE( !maFont.mobItalic, "TextPortionContext::TextPortionContext - nested <i> elements" );
66 maFont.mobItalic = true;
67 break;
68 case XML_s:
69 OSL_ENSURE( !maFont.mobStrikeout, "TextPortionContext::TextPortionContext - nested <s> elements" );
70 maFont.mobStrikeout = true;
71 break;
72 case OOX_TOKEN(dml, blip):
73 {
74 OptValue<OUString> oRelId = rAttribs.getString(R_TOKEN(embed));
75 if (oRelId.has())
76 mrTextBox.mrTypeModel.moGraphicPath = getFragmentPathFromRelId(oRelId.get());
77 }
78 break;
79 case VML_TOKEN(imagedata):
80 {
81 OptValue<OUString> oRelId = rAttribs.getString(R_TOKEN(id));
82 if (oRelId.has())
83 mrTextBox.mrTypeModel.moGraphicPath = getFragmentPathFromRelId(oRelId.get());
84 }
85 break;
86 case XML_span:
87 case W_TOKEN(r):
88 break;
89 default:
90 OSL_ENSURE( false, "TextPortionContext::TextPortionContext - unknown element" );
91 }
92 }
93
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)94 ContextHandlerRef TextPortionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
95 {
96 OSL_ENSURE( nElement != XML_font, "TextPortionContext::onCreateContext - nested <font> elements" );
97 if (getNamespace(getCurrentElement()) == NMSP_doc)
98 return this;
99 return new TextPortionContext( *this, mrTextBox, maParagraph, maFont, nElement, rAttribs );
100 }
101
onCharacters(const OUString & rChars)102 void TextPortionContext::onCharacters( const OUString& rChars )
103 {
104 if (getNamespace(getCurrentElement()) == NMSP_doc && getCurrentElement() != W_TOKEN(t))
105 return;
106
107 switch( getCurrentElement() )
108 {
109 case XML_span:
110 // replace all NBSP characters with SP
111 mrTextBox.appendPortion( maParagraph, maFont, rChars.replace( 0xA0, ' ' ) );
112 break;
113 default:
114 mrTextBox.appendPortion( maParagraph, maFont, rChars );
115 }
116 }
117
onStartElement(const AttributeList & rAttribs)118 void TextPortionContext::onStartElement(const AttributeList& rAttribs)
119 {
120 switch (getCurrentElement())
121 {
122 case W_TOKEN(b):
123 maFont.mobBold = true;
124 break;
125 case W_TOKEN(sz):
126 maFont.monSize = rAttribs.getInteger( W_TOKEN(val) );
127 break;
128 case W_TOKEN(br):
129 mrTextBox.appendPortion( maParagraph, maFont, "\n" );
130 break;
131 case W_TOKEN(color):
132 maFont.moColor = rAttribs.getString( W_TOKEN(val) );
133 break;
134 case W_TOKEN(spacing):
135 maFont.monSpacing = rAttribs.getInteger(W_TOKEN(val));
136 break;
137 case W_TOKEN(r):
138 case W_TOKEN(rPr):
139 case W_TOKEN(t):
140 break;
141 case W_TOKEN(rFonts):
142 // See https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.runfonts(v=office.14).aspx
143 maFont.moName = rAttribs.getString(W_TOKEN(ascii));
144 maFont.moNameAsian = rAttribs.getString(W_TOKEN(eastAsia));
145 maFont.moNameComplex = rAttribs.getString(W_TOKEN(cs));
146 break;
147 default:
148 SAL_INFO("oox", "unhandled: 0x" << std::hex<< getCurrentElement());
149 break;
150 }
151 }
152
onEndElement()153 void TextPortionContext::onEndElement()
154 {
155 if (getNamespace(getCurrentElement()) == NMSP_doc && getCurrentElement() != W_TOKEN(t))
156 return;
157
158 /* A child element without own child elements may contain a single space
159 character, for example:
160
161 <div>
162 <font><i>abc</i></font>
163 <font> </font>
164 <font><b>def</b></font>
165 </div>
166
167 represents the italic text 'abc', an unformatted space character, and
168 the bold text 'def'. Unfortunately, the XML parser skips the space
169 character without issuing a 'characters' event. The class member
170 'mnInitialPortions' contains the number of text portions existing when
171 this context has been constructed. If no text has been added in the
172 meantime, the space character has to be added manually.
173 */
174 if( mrTextBox.getPortionCount() == mnInitialPortions )
175 mrTextBox.appendPortion( maParagraph, maFont, OUString( ' ' ) );
176 }
177
TextBoxContext(ContextHandler2Helper const & rParent,TextBox & rTextBox,const AttributeList & rAttribs,const GraphicHelper & graphicHelper)178 TextBoxContext::TextBoxContext( ContextHandler2Helper const & rParent, TextBox& rTextBox, const AttributeList& rAttribs,
179 const GraphicHelper& graphicHelper ) :
180 ContextHandler2( rParent ),
181 mrTextBox( rTextBox )
182 {
183 if( rAttribs.getString( XML_insetmode ).get() != "auto" )
184 {
185 OUString inset = rAttribs.getString( XML_inset ).get();
186 OUString value;
187 OUString remainingStr;
188
189 ConversionHelper::separatePair( value, remainingStr, inset, ',' );
190 rTextBox.borderDistanceLeft = ConversionHelper::decodeMeasureToHmm( graphicHelper,
191 value.isEmpty() ? "0.1in" : value, 0, false, false );
192
193 inset = remainingStr;
194 ConversionHelper::separatePair( value, remainingStr, inset, ',' );
195 rTextBox.borderDistanceTop = ConversionHelper::decodeMeasureToHmm( graphicHelper,
196 value.isEmpty() ? "0.05in" : value, 0, false, false );
197
198 inset = remainingStr;
199 ConversionHelper::separatePair( value, remainingStr, inset, ',' );
200 rTextBox.borderDistanceRight = ConversionHelper::decodeMeasureToHmm( graphicHelper,
201 value.isEmpty() ? "0.1in" : value, 0, false, false );
202
203 inset = remainingStr;
204 ConversionHelper::separatePair( value, remainingStr, inset, ',' );
205 rTextBox.borderDistanceBottom = ConversionHelper::decodeMeasureToHmm( graphicHelper,
206 value.isEmpty() ? "0.05in" : value, 0, false, false );
207
208 rTextBox.borderDistanceSet = true;
209 }
210
211 OUString sStyle = rAttribs.getString( XML_style, OUString() );
212 sal_Int32 nIndex = 0;
213 while( nIndex >= 0 )
214 {
215 OUString aName, aValue;
216 if( ConversionHelper::separatePair( aName, aValue, sStyle.getToken( 0, ';', nIndex ), ':' ) )
217 {
218 if( aName == "layout-flow" ) rTextBox.maLayoutFlow = aValue;
219 else if (aName == "mso-fit-shape-to-text")
220 rTextBox.mrTypeModel.mbAutoHeight = true;
221 else if (aName == "mso-layout-flow-alt")
222 rTextBox.mrTypeModel.maLayoutFlowAlt = aValue;
223 else if (aName == "mso-next-textbox")
224 rTextBox.msNextTextbox = aValue;
225 else
226 SAL_WARN("oox", "unhandled style property: " << aName);
227 }
228 }
229 }
230
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)231 ContextHandlerRef TextBoxContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
232 {
233 switch( getCurrentElement() )
234 {
235 case VML_TOKEN( textbox ):
236 if( nElement == XML_div ) return this;
237 else if (nElement == W_TOKEN(txbxContent)) return this;
238 break;
239 case XML_div:
240 if( nElement == XML_font ) return new TextPortionContext( *this, mrTextBox, maParagraph, TextFontModel(), nElement, rAttribs );
241 break;
242 case W_TOKEN(txbxContent):
243 if (nElement == W_TOKEN(p)) return this;
244 break;
245 case W_TOKEN(p):
246 case W_TOKEN(sdtContent):
247 case W_TOKEN(smartTag):
248 if (nElement == W_TOKEN(r))
249 return new TextPortionContext( *this, mrTextBox, maParagraph, TextFontModel(), nElement, rAttribs );
250 else
251 return this;
252 case W_TOKEN(pPr):
253 case W_TOKEN(sdt):
254 return this;
255 default:
256 SAL_INFO("oox", "unhandled 0x" << std::hex << getCurrentElement());
257 break;
258 }
259 return nullptr;
260 }
261
onStartElement(const AttributeList & rAttribs)262 void TextBoxContext::onStartElement(const AttributeList& rAttribs)
263 {
264 switch (getCurrentElement())
265 {
266 case W_TOKEN(jc):
267 maParagraph.moParaAdjust = rAttribs.getString( W_TOKEN(val) );
268 break;
269 case W_TOKEN(pStyle):
270 maParagraph.moParaStyleName = rAttribs.getString( W_TOKEN(val) );
271 break;
272 }
273 }
274
onEndElement()275 void TextBoxContext::onEndElement()
276 {
277 if (getCurrentElement() == W_TOKEN(p))
278 {
279 mrTextBox.appendPortion( maParagraph, TextFontModel(), "\n" );
280 maParagraph = TextParagraphModel();
281 }
282 }
283
284 } // namespace oox::vml
285
286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
287