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