/src/libreoffice/oox/source/vml/vmldrawing.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/vml/vmldrawing.hxx> |
21 | | |
22 | | #include <algorithm> |
23 | | #include <com/sun/star/beans/XPropertySet.hpp> |
24 | | #include <com/sun/star/drawing/XControlShape.hpp> |
25 | | #include <com/sun/star/drawing/XDrawPage.hpp> |
26 | | #include <com/sun/star/drawing/XShapes.hpp> |
27 | | #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
28 | | #include <com/sun/star/text/HoriOrientation.hpp> |
29 | | #include <com/sun/star/text/RelOrientation.hpp> |
30 | | #include <com/sun/star/text/VertOrientation.hpp> |
31 | | #include <osl/diagnose.h> |
32 | | #include <rtl/ustring.hxx> |
33 | | #include <sal/log.hxx> |
34 | | #include <oox/core/xmlfilterbase.hxx> |
35 | | #include <oox/helper/containerhelper.hxx> |
36 | | #include <oox/ole/axcontrol.hxx> |
37 | | #include <oox/vml/vmlshape.hxx> |
38 | | #include <oox/vml/vmlshapecontainer.hxx> |
39 | | #include <comphelper/diagnose_ex.hxx> |
40 | | #include <tools/gen.hxx> |
41 | | #include <o3tl/string_view.hxx> |
42 | | |
43 | | namespace oox::vml { |
44 | | |
45 | | using namespace ::com::sun::star; |
46 | | using namespace ::com::sun::star::awt; |
47 | | using namespace ::com::sun::star::beans; |
48 | | using namespace ::com::sun::star::drawing; |
49 | | using namespace ::com::sun::star::lang; |
50 | | using namespace ::com::sun::star::text; |
51 | | using namespace ::com::sun::star::uno; |
52 | | |
53 | | using ::oox::core::XmlFilterBase; |
54 | | |
55 | | namespace { |
56 | | |
57 | | /** Returns the textual representation of a numeric VML shape identifier. */ |
58 | | OUString lclGetShapeId( sal_Int32 nShapeId ) |
59 | 49 | { |
60 | | // identifier consists of a literal NUL character, a lowercase 's', and the id |
61 | 49 | static constexpr OUStringLiteral aStr = u"\0s"; |
62 | 49 | return aStr + OUString::number( nShapeId ); |
63 | 49 | } |
64 | | |
65 | | /** Returns the numeric VML shape identifier from its textual representation. */ |
66 | | sal_Int32 lclGetShapeId( std::u16string_view rShapeId ) |
67 | 27 | { |
68 | | // identifier consists of a literal NUL character, a lowercase 's', and the id |
69 | 27 | return ((rShapeId.size() >= 3) && (rShapeId[ 0 ] == '\0') && (rShapeId[ 1 ] == 's')) ? o3tl::toInt32(rShapeId.substr( 2 )) : -1; |
70 | 27 | } |
71 | | |
72 | | } // namespace |
73 | | |
74 | | OleObjectInfo::OleObjectInfo( bool bDmlShape ) : |
75 | 21 | mbAutoLoad( false ), |
76 | 21 | mbDmlShape( bDmlShape ) |
77 | 21 | { |
78 | 21 | } |
79 | | |
80 | | void OleObjectInfo::setShapeId( sal_Int32 nShapeId ) |
81 | 21 | { |
82 | 21 | maShapeId = lclGetShapeId( nShapeId ); |
83 | 21 | } |
84 | | |
85 | | ControlInfo::ControlInfo() |
86 | 28 | : mbTextContentShape(false) |
87 | 28 | { |
88 | 28 | } |
89 | | |
90 | | void ControlInfo::setShapeId( sal_Int32 nShapeId ) |
91 | 28 | { |
92 | 28 | maShapeId = lclGetShapeId(nShapeId); |
93 | 28 | } |
94 | | |
95 | | Drawing::Drawing( XmlFilterBase& rFilter, const Reference< XDrawPage >& rxDrawPage, DrawingType eType ) : |
96 | 33.6k | mrFilter( rFilter ), |
97 | 33.6k | mxDrawPage( rxDrawPage ), |
98 | 33.6k | mxShapes( new ShapeContainer( *this ) ), |
99 | 33.6k | meType( eType ) |
100 | 33.6k | { |
101 | 33.6k | OSL_ENSURE( mxDrawPage.is(), "Drawing::Drawing - missing UNO draw page" ); |
102 | 33.6k | } |
103 | | |
104 | | Drawing::~Drawing() |
105 | 33.6k | { |
106 | 33.6k | } |
107 | | |
108 | | ::oox::ole::EmbeddedForm& Drawing::getControlForm() const |
109 | 24 | { |
110 | 24 | if (!mxCtrlForm) |
111 | 19 | mxCtrlForm.reset( new ::oox::ole::EmbeddedForm( |
112 | 19 | mrFilter.getModel(), mxDrawPage, mrFilter.getGraphicHelper() ) ); |
113 | 24 | return *mxCtrlForm; |
114 | 24 | } |
115 | | |
116 | | void Drawing::registerBlockId( sal_Int32 nBlockId ) |
117 | 188 | { |
118 | 188 | OSL_ENSURE( nBlockId > 0, "Drawing::registerBlockId - invalid block index" ); |
119 | 188 | if( nBlockId > 0 ) |
120 | 188 | { |
121 | | // lower_bound() returns iterator pointing to element equal to nBlockId, if existing |
122 | 188 | BlockIdVector::iterator aIt = ::std::lower_bound( maBlockIds.begin(), maBlockIds.end(), nBlockId ); |
123 | 188 | if( (aIt == maBlockIds.end()) || (nBlockId != *aIt) ) |
124 | 188 | maBlockIds.insert( aIt, nBlockId ); |
125 | 188 | } |
126 | 188 | } |
127 | | |
128 | | void Drawing::registerOleObject( const OleObjectInfo& rOleObject ) |
129 | 16 | { |
130 | 16 | OSL_ENSURE( !rOleObject.maShapeId.isEmpty(), "Drawing::registerOleObject - missing OLE object shape id" ); |
131 | 16 | OSL_ENSURE( maOleObjects.count( rOleObject.maShapeId ) == 0, "Drawing::registerOleObject - OLE object already registered" ); |
132 | 16 | maOleObjects.emplace( rOleObject.maShapeId, rOleObject ); |
133 | 16 | } |
134 | | |
135 | | void Drawing::registerControl( const ControlInfo& rControl ) |
136 | 28 | { |
137 | 28 | OSL_ENSURE( !rControl.maShapeId.isEmpty(), "Drawing::registerControl - missing form control shape id" ); |
138 | 28 | OSL_ENSURE( !rControl.maName.isEmpty(), "Drawing::registerControl - missing form control name" ); |
139 | 28 | OSL_ENSURE( maControls.count( rControl.maShapeId ) == 0, "Drawing::registerControl - form control already registered" ); |
140 | 28 | maControls.emplace( rControl.maShapeId, rControl ); |
141 | 28 | } |
142 | | |
143 | | void Drawing::finalizeFragmentImport() |
144 | 2.48k | { |
145 | 2.48k | mxShapes->finalizeFragmentImport(); |
146 | 2.48k | } |
147 | | |
148 | | void Drawing::convertAndInsert() const |
149 | 39.5k | { |
150 | 39.5k | Reference< XShapes > xShapes( mxDrawPage ); |
151 | 39.5k | mxShapes->convertAndInsert( xShapes ); |
152 | | |
153 | | // Group together form control radio buttons that are in the same groupBox |
154 | 39.5k | std::map<OUString, tools::Rectangle> GroupBoxMap; |
155 | 39.5k | std::map<Reference< XPropertySet >, tools::Rectangle> RadioButtonMap; |
156 | 101k | for ( sal_Int32 i = 0; i < xShapes->getCount(); ++i ) |
157 | 62.1k | { |
158 | 62.1k | try |
159 | 62.1k | { |
160 | 62.1k | Reference< XControlShape > xCtrlShape( xShapes->getByIndex(i), UNO_QUERY ); |
161 | 62.1k | if (!xCtrlShape.is()) |
162 | 62.1k | continue; |
163 | 0 | Reference< XControlModel > xCtrlModel( xCtrlShape->getControl(), UNO_SET_THROW ); |
164 | 0 | Reference< XServiceInfo > xModelSI (xCtrlModel, UNO_QUERY_THROW ); |
165 | 0 | Reference< XPropertySet > aProps( xCtrlModel, UNO_QUERY_THROW ); |
166 | |
|
167 | 0 | OUString sName; |
168 | 0 | aProps->getPropertyValue(u"Name"_ustr) >>= sName; |
169 | 0 | const ::Point aPoint( xCtrlShape->getPosition().X, xCtrlShape->getPosition().Y ); |
170 | 0 | const ::Size aSize( xCtrlShape->getSize().Width, xCtrlShape->getSize().Height ); |
171 | 0 | const tools::Rectangle aRect( aPoint, aSize ); |
172 | 0 | if ( !sName.isEmpty() |
173 | 0 | && xModelSI->supportsService(u"com.sun.star.awt.UnoControlGroupBoxModel"_ustr) ) |
174 | 0 | { |
175 | 0 | GroupBoxMap[sName] = aRect; |
176 | 0 | } |
177 | 0 | else if ( xModelSI->supportsService(u"com.sun.star.awt.UnoControlRadioButtonModel"_ustr) ) |
178 | 0 | { |
179 | 0 | OUString sGroupName; |
180 | 0 | aProps->getPropertyValue(u"GroupName"_ustr) >>= sGroupName; |
181 | | // only Form Controls are affected by Group Boxes - see drawingfragment.cxx |
182 | 0 | if ( sGroupName == "autoGroup_formControl" ) |
183 | 0 | RadioButtonMap[aProps] = aRect; |
184 | 0 | } |
185 | 0 | } |
186 | 62.1k | catch (uno::Exception&) |
187 | 62.1k | { |
188 | 0 | DBG_UNHANDLED_EXCEPTION("oox.vml"); |
189 | 0 | } |
190 | 62.1k | } |
191 | 39.5k | for ( const auto& BoxItr : GroupBoxMap ) |
192 | 0 | { |
193 | 0 | const uno::Any aGroup( "autoGroup_" + BoxItr.first ); |
194 | 0 | for ( auto RadioItr = RadioButtonMap.begin(); RadioItr != RadioButtonMap.end(); ) |
195 | 0 | { |
196 | 0 | if ( BoxItr.second.Contains(RadioItr->second) ) |
197 | 0 | { |
198 | 0 | RadioItr->first->setPropertyValue(u"GroupName"_ustr, aGroup ); |
199 | | // If conflict, first created GroupBox wins |
200 | 0 | RadioItr = RadioButtonMap.erase(RadioItr); |
201 | 0 | } |
202 | 0 | else |
203 | 0 | ++RadioItr; |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | 39.5k | } |
208 | | |
209 | | sal_Int32 Drawing::getLocalShapeIndex( std::u16string_view rShapeId ) const |
210 | 27 | { |
211 | 27 | sal_Int32 nShapeId = lclGetShapeId( rShapeId ); |
212 | 27 | if( nShapeId <= 0 ) return -1; |
213 | | |
214 | | /* Shapes in a drawing are counted per registered shape identifier blocks |
215 | | as stored in the o:idmap element. The contents of this element have |
216 | | been stored in our member maBlockIds. Each block represents 1024 shape |
217 | | identifiers, starting with identifier 1 for the block #0. This means, |
218 | | block #0 represents the identifiers 1-1024, block #1 represents the |
219 | | identifiers 1025-2048, and so on. The local shape index has to be |
220 | | calculated according to all blocks registered for this drawing. |
221 | | |
222 | | Example: |
223 | | Registered for this drawing are blocks #1 and #3 (shape identifiers |
224 | | 1025-2048 and 3073-4096). |
225 | | Shape identifier 1025 -> local shape index 1. |
226 | | Shape identifier 1026 -> local shape index 2. |
227 | | ... |
228 | | Shape identifier 2048 -> local shape index 1024. |
229 | | Shape identifier 3073 -> local shape index 1025. |
230 | | ... |
231 | | Shape identifier 4096 -> local shape index 2048. |
232 | | */ |
233 | | |
234 | | // get block id from shape id and find its index in the list of used blocks |
235 | 27 | sal_Int32 nBlockId = (nShapeId - 1) / 1024; |
236 | 27 | BlockIdVector::iterator aIt = ::std::lower_bound( maBlockIds.begin(), maBlockIds.end(), nBlockId ); |
237 | 27 | sal_Int32 nIndex = static_cast< sal_Int32 >( aIt - maBlockIds.begin() ); |
238 | | |
239 | | // block id not found in set -> register it now (value of nIndex remains valid) |
240 | 27 | if( (aIt == maBlockIds.end()) || (*aIt != nBlockId) ) |
241 | 0 | maBlockIds.insert( aIt, nBlockId ); |
242 | | |
243 | | // get one-based offset of shape id in its block |
244 | 27 | sal_Int32 nBlockOffset = (nShapeId - 1) % 1024 + 1; |
245 | | |
246 | | // calculate the local shape index |
247 | 27 | sal_Int32 nRet; |
248 | 27 | if (o3tl::checked_add(1024 * nIndex, nBlockOffset, nRet)) |
249 | 0 | { |
250 | 0 | SAL_WARN("oox", "getLocalShapeIndex: overflow on " << 1024 * nIndex << " + " << nBlockOffset); |
251 | 0 | nRet = -1; |
252 | 0 | } |
253 | 27 | return nRet; |
254 | 27 | } |
255 | | |
256 | | const OleObjectInfo* Drawing::getOleObjectInfo( const OUString& rShapeId ) const |
257 | 1.10k | { |
258 | 1.10k | return ContainerHelper::getMapElement( maOleObjects, rShapeId ); |
259 | 1.10k | } |
260 | | |
261 | | const ControlInfo* Drawing::getControlInfo( const OUString& rShapeId ) const |
262 | 1.07k | { |
263 | 1.07k | return ContainerHelper::getMapElement( maControls, rShapeId ); |
264 | 1.07k | } |
265 | | |
266 | | Reference< XShape > Drawing::createAndInsertXShape( const OUString& rService, |
267 | | const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const |
268 | 3.09k | { |
269 | 3.09k | OSL_ENSURE( !rService.isEmpty(), "Drawing::createAndInsertXShape - missing UNO shape service name" ); |
270 | 3.09k | OSL_ENSURE( rxShapes.is(), "Drawing::createAndInsertXShape - missing XShapes container" ); |
271 | 3.09k | Reference< XShape > xShape; |
272 | 3.09k | if( !rService.isEmpty() && rxShapes.is() ) try |
273 | 3.09k | { |
274 | 3.09k | Reference< XMultiServiceFactory > xModelFactory( mrFilter.getModelFactory(), UNO_SET_THROW ); |
275 | 3.09k | xShape.set( xModelFactory->createInstance( rService ), UNO_QUERY_THROW ); |
276 | 3.09k | if ( rService != "com.sun.star.text.TextFrame" ) |
277 | 2.20k | { |
278 | | // insert shape into passed shape collection (maybe drawpage or group shape) |
279 | 2.20k | rxShapes->add( xShape ); |
280 | 2.20k | xShape->setPosition( awt::Point( rShapeRect.X, rShapeRect.Y ) ); |
281 | 2.20k | } |
282 | 888 | else |
283 | 888 | { |
284 | 888 | Reference< XPropertySet > xPropSet( xShape, UNO_QUERY_THROW ); |
285 | 888 | xPropSet->setPropertyValue( u"HoriOrient"_ustr, Any( HoriOrientation::NONE ) ); |
286 | 888 | xPropSet->setPropertyValue( u"VertOrient"_ustr, Any( VertOrientation::NONE ) ); |
287 | 888 | xPropSet->setPropertyValue( u"HoriOrientPosition"_ustr, Any( rShapeRect.X ) ); |
288 | 888 | xPropSet->setPropertyValue( u"VertOrientPosition"_ustr, Any( rShapeRect.Y ) ); |
289 | 888 | xPropSet->setPropertyValue( u"HoriOrientRelation"_ustr, Any( RelOrientation::FRAME ) ); |
290 | 888 | xPropSet->setPropertyValue( u"VertOrientRelation"_ustr, Any( RelOrientation::FRAME ) ); |
291 | 888 | } |
292 | 3.09k | xShape->setSize( awt::Size( rShapeRect.Width, rShapeRect.Height ) ); |
293 | 3.09k | } |
294 | 3.09k | catch( const Exception& ) |
295 | 3.09k | { |
296 | 11 | TOOLS_WARN_EXCEPTION( "oox", "Drawing::createAndInsertXShape - error during shape object creation" ); |
297 | 11 | } |
298 | 3.09k | OSL_ENSURE( xShape.is(), "Drawing::createAndInsertXShape - cannot instantiate shape object" ); |
299 | 3.09k | return xShape; |
300 | 3.09k | } |
301 | | |
302 | | Reference< XShape > Drawing::createAndInsertXControlShape( const ::oox::ole::EmbeddedControl& rControl, |
303 | | const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect, sal_Int32& rnCtrlIndex ) const |
304 | 24 | { |
305 | 24 | Reference< XShape > xShape; |
306 | 24 | try |
307 | 24 | { |
308 | | // create control model and insert it into the form of the draw page |
309 | 24 | Reference< XControlModel > xCtrlModel( getControlForm().convertAndInsert( rControl, rnCtrlIndex ), UNO_SET_THROW ); |
310 | | |
311 | | // create the control shape |
312 | 24 | xShape = createAndInsertXShape( u"com.sun.star.drawing.ControlShape"_ustr, rxShapes, rShapeRect ); |
313 | | |
314 | | // set the control model at the shape |
315 | 24 | Reference< XControlShape >( xShape, UNO_QUERY_THROW )->setControl( xCtrlModel ); |
316 | 24 | } |
317 | 24 | catch (Exception const&) |
318 | 24 | { |
319 | 24 | TOOLS_WARN_EXCEPTION("oox", "exception inserting Shape"); |
320 | 24 | } |
321 | 24 | return xShape; |
322 | 24 | } |
323 | | |
324 | | bool Drawing::isShapeSupported( const ShapeBase& /*rShape*/ ) const |
325 | 3.04k | { |
326 | 3.04k | return true; |
327 | 3.04k | } |
328 | | |
329 | | OUString Drawing::getShapeBaseName( const ShapeBase& /*rShape*/ ) const |
330 | 2.07k | { |
331 | 2.07k | return OUString(); |
332 | 2.07k | } |
333 | | |
334 | | bool Drawing::convertClientAnchor( awt::Rectangle& /*orShapeRect*/, const OUString& /*rShapeAnchor*/ ) const |
335 | 0 | { |
336 | 0 | return false; |
337 | 0 | } |
338 | | |
339 | | Reference< XShape > Drawing::createAndInsertClientXShape( const ShapeBase& /*rShape*/, |
340 | | const Reference< XShapes >& /*rxShapes*/, const awt::Rectangle& /*rShapeRect*/ ) const |
341 | 0 | { |
342 | 0 | return Reference< XShape >(); |
343 | 0 | } |
344 | | |
345 | | void Drawing::notifyXShapeInserted( const Reference< XShape >& /*rxShape*/, |
346 | | const awt::Rectangle& /*rShapeRect*/, const ShapeBase& /*rShape*/, bool /*bGroupChild*/ ) |
347 | 3.04k | { |
348 | 3.04k | } |
349 | | |
350 | | } // namespace oox::vml |
351 | | |
352 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |