1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_toolkit.hxx"
30 
31 #include <toolkit/helper/formpdfexport.hxx>
32 
33 /** === begin UNO includes === **/
34 #include <com/sun/star/container/XIndexAccess.hpp>
35 #include <com/sun/star/container/XNameAccess.hpp>
36 #include <com/sun/star/container/XNameContainer.hpp>
37 #include <com/sun/star/form/XForm.hpp>
38 #include <com/sun/star/container/XChild.hpp>
39 #include <com/sun/star/lang/XServiceInfo.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/form/FormComponentType.hpp>
42 #include <com/sun/star/awt/TextAlign.hpp>
43 #include <com/sun/star/style/VerticalAlignment.hpp>
44 #include <com/sun/star/form/FormButtonType.hpp>
45 #include <com/sun/star/form/FormSubmitMethod.hpp>
46 /** === end UNO includes === **/
47 
48 #include <toolkit/helper/vclunohelper.hxx>
49 #include <tools/diagnose_ex.h>
50 #include <vcl/pdfextoutdevdata.hxx>
51 #include <vcl/outdev.hxx>
52 
53 #include <functional>
54 #include <algorithm>
55 
56 //........................................................................
57 namespace toolkitform
58 {
59 //........................................................................
60 
61     using namespace ::com::sun::star;
62     using namespace ::com::sun::star::uno;
63     using namespace ::com::sun::star::awt;
64     using namespace ::com::sun::star::style;
65     using namespace ::com::sun::star::beans;
66     using namespace ::com::sun::star::form;
67     using namespace ::com::sun::star::lang;
68     using namespace ::com::sun::star::container;
69 
70     // used strings
71     static const ::rtl::OUString FM_PROP_CLASSID(RTL_CONSTASCII_USTRINGPARAM("ClassId"));
72     static const ::rtl::OUString FM_PROP_NAME(RTL_CONSTASCII_USTRINGPARAM("Name"));
73     static const ::rtl::OUString FM_PROP_STRINGITEMLIST(RTL_CONSTASCII_USTRINGPARAM("StringItemList"));
74     static const ::rtl::OUString FM_PROP_HELPTEXT(RTL_CONSTASCII_USTRINGPARAM("HelpText"));
75     static const ::rtl::OUString FM_PROP_TEXT(RTL_CONSTASCII_USTRINGPARAM("Text"));
76     static const ::rtl::OUString FM_PROP_LABEL(RTL_CONSTASCII_USTRINGPARAM("Label"));
77     static const ::rtl::OUString FM_PROP_READONLY(RTL_CONSTASCII_USTRINGPARAM("ReadOnly"));
78     static const ::rtl::OUString FM_PROP_BORDER(RTL_CONSTASCII_USTRINGPARAM("Border"));
79     static const ::rtl::OUString FM_PROP_BACKGROUNDCOLOR(RTL_CONSTASCII_USTRINGPARAM("BackgroundColor"));
80     static const ::rtl::OUString FM_PROP_TEXTCOLOR(RTL_CONSTASCII_USTRINGPARAM("TextColor"));
81     static const ::rtl::OUString FM_PROP_MULTILINE(RTL_CONSTASCII_USTRINGPARAM("MultiLine"));
82     static const ::rtl::OUString FM_PROP_ALIGN(RTL_CONSTASCII_USTRINGPARAM("Align"));
83     static const ::rtl::OUString FM_PROP_FONT(RTL_CONSTASCII_USTRINGPARAM("FontDescriptor"));
84     static const ::rtl::OUString FM_PROP_MAXTEXTLEN(RTL_CONSTASCII_USTRINGPARAM("MaxTextLen"));
85     static const ::rtl::OUString FM_PROP_TARGET_URL(RTL_CONSTASCII_USTRINGPARAM("TargetURL"));
86     static const ::rtl::OUString FM_PROP_STATE(RTL_CONSTASCII_USTRINGPARAM("State"));
87     static const ::rtl::OUString FM_PROP_REFVALUE(RTL_CONSTASCII_USTRINGPARAM("RefValue"));
88     static const ::rtl::OUString FM_PROP_DROPDOWN(RTL_CONSTASCII_USTRINGPARAM("Dropdown"));
89     static const ::rtl::OUString FM_SUN_COMPONENT_FILECONTROL(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.form.component.FileControl"));
90 
91     namespace
92     {
93         //--------------------------------------------------------------------
94         /** determines the FormComponentType of a form control
95         */
96         sal_Int16 classifyFormControl( const Reference< XPropertySet >& _rxModel ) SAL_THROW(( Exception ))
97         {
98             sal_Int16 nControlType = FormComponentType::CONTROL;
99 
100             Reference< XPropertySetInfo > xPSI;
101             if ( _rxModel.is() )
102                 xPSI = _rxModel->getPropertySetInfo();
103             if ( xPSI.is() && xPSI->hasPropertyByName( FM_PROP_CLASSID ) )
104             {
105                 OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_CLASSID ) >>= nControlType );
106             }
107 
108             return nControlType;
109         }
110 
111 	    //--------------------------------------------------------------------
112         /** (default-)creates a PDF widget according to a given FormComponentType
113         */
114         ::vcl::PDFWriter::AnyWidget* createDefaultWidget( sal_Int16 _nFormComponentType )
115         {
116             switch ( _nFormComponentType )
117             {
118             case FormComponentType::COMMANDBUTTON:
119                 return new ::vcl::PDFWriter::PushButtonWidget;
120             case FormComponentType::CHECKBOX:
121                 return new ::vcl::PDFWriter::CheckBoxWidget;
122             case FormComponentType::RADIOBUTTON:
123                 return new ::vcl::PDFWriter::RadioButtonWidget;
124             case FormComponentType::LISTBOX:
125                 return new ::vcl::PDFWriter::ListBoxWidget;
126             case FormComponentType::COMBOBOX:
127                 return new ::vcl::PDFWriter::ComboBoxWidget;
128 
129             case FormComponentType::TEXTFIELD:
130             case FormComponentType::FILECONTROL:
131             case FormComponentType::DATEFIELD:
132             case FormComponentType::TIMEFIELD:
133             case FormComponentType::NUMERICFIELD:
134             case FormComponentType::CURRENCYFIELD:
135             case FormComponentType::PATTERNFIELD:
136                 return new ::vcl::PDFWriter::EditWidget;
137             }
138             return NULL;
139         }
140 
141 	    //--------------------------------------------------------------------
142         /** determines a unique number for the radio group which the given radio
143             button model belongs to
144 
145             The number is guaranteed to be
146             <ul><li>unique within the document in which the button lives</li>
147                 <li>the same for subsequent calls with other radio button models,
148                     which live in the same document, and belong to the same group</li>
149             </ul>
150 
151             @precond
152                 the model must be part of the form component hierarchy in a document
153         */
154         sal_Int32 determineRadioGroupId( const Reference< XPropertySet >& _rxRadioModel ) SAL_THROW((Exception))
155         {
156             OSL_ENSURE( classifyFormControl( _rxRadioModel ) == FormComponentType::RADIOBUTTON,
157                 "determineRadioGroupId: this *is* no radio button model!" );
158             // The fact that radio button groups need to be unique within the complete
159             // host document makes it somewhat difficult ...
160             // Problem is that two form radio buttons belong to the same group if
161             // - they have the same parent
162             // - AND they have the same name
163             // This implies that we need some knowledge about (potentially) *all* radio button
164             // groups in the document.
165 
166             // get the root-level container
167             Reference< XChild > xChild( _rxRadioModel, UNO_QUERY );
168             Reference< XForm > xParentForm( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY );
169             OSL_ENSURE( xParentForm.is(), "determineRadioGroupId: no parent form -> group id!" );
170             if ( !xParentForm.is() )
171                 return -1;
172 
173             while ( xParentForm.is() )
174             {
175                 xChild = xParentForm.get();
176                 xParentForm = xParentForm.query( xChild->getParent() );
177             }
178             Reference< XIndexAccess > xRoot( xChild->getParent(), UNO_QUERY );
179             OSL_ENSURE( xRoot.is(), "determineRadioGroupId: unable to determine the root of the form component hierarchy!" );
180             if ( !xRoot.is() )
181                 return -1;
182 
183             // count the leafs in the hierarchy, until we encounter radio button
184             ::std::vector< Reference< XIndexAccess > > aAncestors;
185             ::std::vector< sal_Int32 >                 aPath;
186 
187             Reference< XInterface > xNormalizedLookup( _rxRadioModel, UNO_QUERY );
188             ::rtl::OUString sRadioGroupName;
189             OSL_VERIFY( _rxRadioModel->getPropertyValue( FM_PROP_NAME ) >>= sRadioGroupName );
190 
191             Reference< XIndexAccess > xCurrentContainer( xRoot );
192             sal_Int32 nStartWithChild = 0;
193             sal_Int32 nGroupsEncountered = 0;
194             do
195             {
196                 Reference< XNameAccess > xElementNameAccess( xCurrentContainer, UNO_QUERY );
197                 OSL_ENSURE( xElementNameAccess.is(), "determineRadioGroupId: no name container?" );
198                 if ( !xElementNameAccess.is() )
199                     return -1;
200 
201                 if ( nStartWithChild == 0 )
202                 {   // we encounter this container the first time. In particular, we did not
203                     // just step up
204                     nGroupsEncountered += xElementNameAccess->getElementNames().getLength();
205                         // this is way too much: Not all of the elements in the current container
206                         // may form groups, especially if they're forms. But anyway, this number is
207                         // sufficient for our purpose. Finally, the container contains *at most*
208                         // that much groups
209                 }
210 
211                 sal_Int32 nCount = xCurrentContainer->getCount();
212                 sal_Int32 i;
213                 for ( i = nStartWithChild; i < nCount; ++i )
214                 {
215                     Reference< XInterface > xElement( xCurrentContainer->getByIndex( i ), UNO_QUERY );
216                     if ( !xElement.is() )
217                     {
218                         OSL_ENSURE( sal_False, "determineRadioGroupId: very suspicious!" );
219                         continue;
220                     }
221 
222                     Reference< XIndexAccess > xNewContainer( xElement, UNO_QUERY );
223                     if ( xNewContainer.is() )
224                     {
225                         // step down the hierarchy
226                         aAncestors.push_back( xCurrentContainer );
227                         xCurrentContainer = xNewContainer;
228                         aPath.push_back( i );
229                         nStartWithChild = 0;
230                         break;
231                             // out of the inner loop, but continue with the outer loop
232                     }
233 
234                     if ( xElement.get() == xNormalizedLookup.get() )
235                     {
236                         // look up the name of the radio group in the list of all element names
237                         Sequence< ::rtl::OUString > aElementNames( xElementNameAccess->getElementNames() );
238                         const ::rtl::OUString* pElementNames = aElementNames.getConstArray();
239                         const ::rtl::OUString* pElementNamesEnd = pElementNames + aElementNames.getLength();
240                         while ( pElementNames != pElementNamesEnd )
241                         {
242                             if ( *pElementNames == sRadioGroupName )
243                             {
244                                 sal_Int32 nLocalGroupIndex = pElementNames - aElementNames.getConstArray();
245                                 OSL_ENSURE( nLocalGroupIndex < xElementNameAccess->getElementNames().getLength(),
246                                     "determineRadioGroupId: inconsistency!" );
247 
248                                 sal_Int32 nGlobalGroupId = nGroupsEncountered - xElementNameAccess->getElementNames().getLength() + nLocalGroupIndex;
249                                 return nGlobalGroupId;
250                             }
251                             ++pElementNames;
252                         }
253                         OSL_ENSURE( sal_False, "determineRadioGroupId: did not find the radios element name!" );
254                     }
255                 }
256 
257                 if ( !( i < nCount ) )
258                 {
259                     // the loop terminated because there were no more elements
260                     // -> step up, if possible
261                     if ( aAncestors.empty() )
262                         break;
263 
264                     xCurrentContainer = aAncestors.back(); aAncestors.pop_back();
265                     nStartWithChild = aPath.back() + 1; aPath.pop_back();
266                 }
267             }
268             while ( true );
269             return -1;
270         }
271 
272         //--------------------------------------------------------------------
273         /** copies a StringItemList to a PDF widget's list
274         */
275         void getStringItemVector( const Reference< XPropertySet >& _rxModel, ::std::vector< ::rtl::OUString >& _rVector )
276         {
277             Sequence< ::rtl::OUString > aListEntries;
278             OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_STRINGITEMLIST ) >>= aListEntries );
279             ::std::copy( aListEntries.getConstArray(), aListEntries.getConstArray() + aListEntries.getLength(),
280                 ::std::back_insert_iterator< ::std::vector< ::rtl::OUString > >( _rVector ) );
281         }
282     }
283 
284 	//--------------------------------------------------------------------
285     /** creates a PDF compatible control descriptor for the given control
286     */
287     void TOOLKIT_DLLPUBLIC describePDFControl( const Reference< XControl >& _rxControl,
288         ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget >& _rpDescriptor, ::vcl::PDFExtOutDevData& i_pdfExportData ) SAL_THROW(())
289     {
290         _rpDescriptor.reset( NULL );
291         OSL_ENSURE( _rxControl.is(), "describePDFControl: invalid (NULL) control!" );
292         if ( !_rxControl.is() )
293             return;
294 
295         try
296         {
297             Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
298             sal_Int16 nControlType = classifyFormControl( xModelProps );
299             _rpDescriptor.reset( createDefaultWidget( nControlType ) );
300             if ( !_rpDescriptor.get() )
301                 // no PDF widget available for this
302                 return;
303 
304             Reference< XPropertySetInfo > xPSI( xModelProps->getPropertySetInfo() );
305             Reference< XServiceInfo > xSI( xModelProps, UNO_QUERY );
306             OSL_ENSURE( xSI.is(), "describePDFControl: no service info!" );
307                 // if we survived classifyFormControl, then it's a real form control, and they all have
308                 // service infos
309 
310             // ================================
311             // set the common widget properties
312 
313             // --------------------------------
314             // Name, Description, Text
315             OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_NAME ) >>= _rpDescriptor->Name );
316             OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_HELPTEXT ) >>= _rpDescriptor->Description );
317             Any aText;
318             if ( xPSI->hasPropertyByName( FM_PROP_TEXT ) )
319                 aText = xModelProps->getPropertyValue( FM_PROP_TEXT );
320             else if ( xPSI->hasPropertyByName( FM_PROP_LABEL ) )
321                 aText = xModelProps->getPropertyValue( FM_PROP_LABEL );
322             if ( aText.hasValue() )
323                 OSL_VERIFY( aText >>= _rpDescriptor->Text );
324 
325             // --------------------------------
326             // readonly
327             if ( xPSI->hasPropertyByName( FM_PROP_READONLY ) )
328                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= _rpDescriptor->ReadOnly );
329 
330             // --------------------------------
331             // border
332             {
333                 if ( xPSI->hasPropertyByName( FM_PROP_BORDER ) )
334                 {
335                     sal_Int16 nBorderType = 0;
336                     OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_BORDER ) >>= nBorderType );
337                     _rpDescriptor->Border = ( nBorderType != 0 );
338 
339                     ::rtl::OUString sBorderColorPropertyName( RTL_CONSTASCII_USTRINGPARAM( "BorderColor" ) );
340                     if ( xPSI->hasPropertyByName( sBorderColorPropertyName ) )
341                     {
342                         sal_Int32 nBoderColor = COL_TRANSPARENT;
343                         if ( xModelProps->getPropertyValue( sBorderColorPropertyName ) >>= nBoderColor )
344                             _rpDescriptor->BorderColor = Color( nBoderColor );
345                         else
346                             _rpDescriptor->BorderColor = Color( COL_BLACK );
347                     }
348                 }
349             }
350 
351             // --------------------------------
352             // background color
353             if ( xPSI->hasPropertyByName( FM_PROP_BACKGROUNDCOLOR ) )
354             {
355                 sal_Int32 nBackColor = COL_TRANSPARENT;
356                 xModelProps->getPropertyValue( FM_PROP_BACKGROUNDCOLOR ) >>= nBackColor;
357                 _rpDescriptor->Background = true;
358                 _rpDescriptor->BackgroundColor = Color( nBackColor );
359             }
360 
361             // --------------------------------
362             // text color
363             if ( xPSI->hasPropertyByName( FM_PROP_TEXTCOLOR ) )
364             {
365                 sal_Int32 nTextColor = COL_TRANSPARENT;
366                 xModelProps->getPropertyValue( FM_PROP_TEXTCOLOR ) >>= nTextColor;
367                 _rpDescriptor->TextColor = Color( nTextColor );
368             }
369 
370             // --------------------------------
371             // text style
372             _rpDescriptor->TextStyle = 0;
373             // ............................
374             // multi line and word break
375             // The MultiLine property of the control is mapped to both the "MULTILINE" and
376             // "WORDBREAK" style flags
377             if ( xPSI->hasPropertyByName( FM_PROP_MULTILINE ) )
378             {
379                 sal_Bool bMultiLine = sal_False;
380                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_MULTILINE ) >>= bMultiLine );
381                 if ( bMultiLine )
382                     _rpDescriptor->TextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
383             }
384             // ............................
385             // horizontal alignment
386             if ( xPSI->hasPropertyByName( FM_PROP_ALIGN ) )
387             {
388                 sal_Int16 nAlign = awt::TextAlign::LEFT;
389                 xModelProps->getPropertyValue( FM_PROP_ALIGN ) >>= nAlign;
390                 // TODO: when the property is VOID - are there situations/UIs where this
391                 // means something else than LEFT?
392                 switch ( nAlign )
393                 {
394                 case awt::TextAlign::LEFT:  _rpDescriptor->TextStyle |= TEXT_DRAW_LEFT; break;
395                 case awt::TextAlign::CENTER:  _rpDescriptor->TextStyle |= TEXT_DRAW_CENTER; break;
396                 case awt::TextAlign::RIGHT:  _rpDescriptor->TextStyle |= TEXT_DRAW_RIGHT; break;
397                 default:
398                     OSL_ENSURE( sal_False, "describePDFControl: invalid text align!" );
399                 }
400             }
401             // ............................
402             // vertical alignment
403             {
404                 ::rtl::OUString sVertAlignPropertyName( RTL_CONSTASCII_USTRINGPARAM( "VerticalAlign" ) );
405                 if ( xPSI->hasPropertyByName( sVertAlignPropertyName ) )
406                 {
407                     sal_Int16 nAlign = VerticalAlignment_MIDDLE;
408                     xModelProps->getPropertyValue( sVertAlignPropertyName ) >>= nAlign;
409                     switch ( nAlign )
410                     {
411                     case VerticalAlignment_TOP:  _rpDescriptor->TextStyle |= TEXT_DRAW_TOP; break;
412                     case VerticalAlignment_MIDDLE:  _rpDescriptor->TextStyle |= TEXT_DRAW_VCENTER; break;
413                     case VerticalAlignment_BOTTOM:  _rpDescriptor->TextStyle |= TEXT_DRAW_BOTTOM; break;
414                     default:
415                         OSL_ENSURE( sal_False, "describePDFControl: invalid vertical text align!" );
416                     }
417                 }
418             }
419 
420             // font
421             if ( xPSI->hasPropertyByName( FM_PROP_FONT ) )
422             {
423                 FontDescriptor aUNOFont;
424                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_FONT ) >>= aUNOFont );
425                 _rpDescriptor->TextFont = VCLUnoHelper::CreateFont( aUNOFont, Font() );
426             }
427 
428             // tab order
429             rtl::OUString aTabIndexString( RTL_CONSTASCII_USTRINGPARAM( "TabIndex" ) );
430             if ( xPSI->hasPropertyByName( aTabIndexString ) )
431             {
432                 sal_Int16 nIndex = -1;
433                 OSL_VERIFY( xModelProps->getPropertyValue( aTabIndexString ) >>= nIndex );
434                 _rpDescriptor->TabOrder = nIndex;
435             }
436 
437             // ================================
438             // special widget properties
439             // --------------------------------
440             // edits
441             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::Edit )
442             {
443                 ::vcl::PDFWriter::EditWidget* pEditWidget = static_cast< ::vcl::PDFWriter::EditWidget* >( _rpDescriptor.get() );
444                 // ............................
445                 // multiline (already flagged in the TextStyle)
446                 pEditWidget->MultiLine = ( _rpDescriptor->TextStyle & TEXT_DRAW_MULTILINE ) != 0;
447                 // ............................
448                 // password input
449                 ::rtl::OUString sEchoCharPropName( RTL_CONSTASCII_USTRINGPARAM( "EchoChar" ) );
450                 if ( xPSI->hasPropertyByName( sEchoCharPropName ) )
451                 {
452                     sal_Int16 nEchoChar = 0;
453                     if ( ( xModelProps->getPropertyValue( sEchoCharPropName ) >>= nEchoChar ) && ( nEchoChar != 0 ) )
454                         pEditWidget->Password = true;
455                 }
456                 // ............................
457                 // file select
458                 if ( xSI->supportsService( FM_SUN_COMPONENT_FILECONTROL ) )
459                     pEditWidget->FileSelect = true;
460                 // ............................
461                 // maximum text length
462                 if ( xPSI->hasPropertyByName( FM_PROP_MAXTEXTLEN ) )
463                 {
464                     sal_Int16 nMaxTextLength = 0;
465                     OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_MAXTEXTLEN ) >>= nMaxTextLength );
466                     if ( nMaxTextLength <= 0 )
467                         // "-1" has a special meaning for database-bound controls
468                         nMaxTextLength = 0;
469                     pEditWidget->MaxLen = nMaxTextLength;
470                 }
471             }
472 
473             // --------------------------------
474             // buttons
475             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::PushButton )
476             {
477                 ::vcl::PDFWriter::PushButtonWidget* pButtonWidget = static_cast< ::vcl::PDFWriter::PushButtonWidget* >( _rpDescriptor.get() );
478                 FormButtonType eButtonType = FormButtonType_PUSH;
479                 OSL_VERIFY( xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ButtonType" ) ) ) >>= eButtonType );
480                 if ( eButtonType == FormButtonType_SUBMIT )
481                 {
482                     // if a button is a submit button, then it uses the URL at it's parent form
483                     Reference< XChild > xChild( xModelProps, UNO_QUERY );
484                     Reference < XPropertySet > xParentProps;
485                     if ( xChild.is() )
486                         xParentProps = xParentProps.query( xChild->getParent() );
487                     if ( xParentProps.is() )
488                     {
489                         Reference< XServiceInfo > xParentSI( xParentProps, UNO_QUERY );
490                         if ( xParentSI.is() && xParentSI->supportsService( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.form.component.HTMLForm" ) ) ) )
491                         {
492                             OSL_VERIFY( xParentProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= pButtonWidget->URL );
493                             pButtonWidget->Submit = true;
494                             FormSubmitMethod eMethod = FormSubmitMethod_POST;
495                             OSL_VERIFY( xParentProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SubmitMethod" ) ) ) >>= eMethod );
496                             pButtonWidget->SubmitGet = (eMethod == FormSubmitMethod_GET);
497                         }
498                     }
499                 }
500                 else if ( eButtonType == FormButtonType_URL )
501                 {
502                     ::rtl::OUString sURL;
503                     OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= sURL );
504                     const bool bDocumentLocalTarget = ( sURL.getLength() > 0 ) && ( sURL.getStr()[0] == '#' );
505                     if ( bDocumentLocalTarget )
506                     {
507                         const ::rtl::OUString sDestinationName( sURL.copy(1) );
508                         // Register the destination for for future handling ...
509                         pButtonWidget->Dest = i_pdfExportData.RegisterDest();
510 
511                         // and put it into the bookmarks, to ensure the future handling really happens
512                         ::std::vector< ::vcl::PDFExtOutDevBookmarkEntry >& rBookmarks( i_pdfExportData.GetBookmarks() );
513                         ::vcl::PDFExtOutDevBookmarkEntry aBookmark;
514                         aBookmark.nDestId = pButtonWidget->Dest;
515                         aBookmark.aBookmark = sURL;
516                         rBookmarks.push_back( aBookmark );
517                     }
518                     else
519                         pButtonWidget->URL = sURL;
520 
521                     pButtonWidget->Submit = false;
522                 }
523 
524                // TODO:
525                 // In PDF files, buttons are either reset, url or submit buttons. So if we have a simple PUSH button
526                 // in a document, then this means that we do not export a SubmitToURL, which means that in PDF,
527                 // the button is used as reset button.
528                 // Is this desired? If no, we would have to reset _rpDescriptor to NULL here, in case eButtonType
529                 // != FormButtonType_SUBMIT && != FormButtonType_RESET
530 
531                 // the PDF exporter defaults the text style, if 0. To prevent this, we have to transfer the UNO
532                 // defaults to the PDF widget
533                 if ( !pButtonWidget->TextStyle )
534                     pButtonWidget->TextStyle = TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER;
535             }
536 
537             // --------------------------------
538             // check boxes
539             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::CheckBox )
540             {
541                 ::vcl::PDFWriter::CheckBoxWidget* pCheckBoxWidget = static_cast< ::vcl::PDFWriter::CheckBoxWidget* >( _rpDescriptor.get() );
542                 sal_Int16 nState = 0;
543                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_STATE ) >>= nState );
544                 pCheckBoxWidget->Checked = ( nState != 0 );
545             }
546 
547             // --------------------------------
548             // radio buttons
549             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::RadioButton )
550             {
551                 ::vcl::PDFWriter::RadioButtonWidget* pRadioWidget = static_cast< ::vcl::PDFWriter::RadioButtonWidget* >( _rpDescriptor.get() );
552                 sal_Int16 nState = 0;
553                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_STATE ) >>= nState );
554                 pRadioWidget->Selected = ( nState != 0 );
555                 pRadioWidget->RadioGroup = determineRadioGroupId( xModelProps );
556                 try
557                 {
558                     xModelProps->getPropertyValue( FM_PROP_REFVALUE ) >>= pRadioWidget->OnValue;
559                 }
560                 catch(...)
561                 {
562                     pRadioWidget->OnValue = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "On" ) );
563                 }
564             }
565 
566             // --------------------------------
567             // list boxes
568             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::ListBox )
569             {
570                 ::vcl::PDFWriter::ListBoxWidget* pListWidget = static_cast< ::vcl::PDFWriter::ListBoxWidget* >( _rpDescriptor.get() );
571                 // ............................
572                 // drop down
573                 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_DROPDOWN ) >>= pListWidget->DropDown );
574                 // ............................
575                 // multi selection
576                 OSL_VERIFY( xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MultiSelection" ) ) ) >>= pListWidget->MultiSelect );
577                 // ............................
578                 // entries
579                 getStringItemVector( xModelProps, pListWidget->Entries );
580                 // since we explicitly list the entries in the order in which they appear, they should not be
581                 // resorted by the PDF viewer
582                 pListWidget->Sort = false;
583 
584                 // get selected items
585                 Sequence< sal_Int16 > aSelectIndices;
586                 OSL_VERIFY( xModelProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SelectedItems" ) ) ) >>= aSelectIndices );
587                 if( aSelectIndices.getLength() > 0 )
588                 {
589                     pListWidget->SelectedEntries.resize( 0 );
590                     for( sal_Int32 i = 0; i < aSelectIndices.getLength(); i++ )
591                     {
592                         sal_Int16 nIndex = aSelectIndices.getConstArray()[i];
593                         if( nIndex >= 0 && nIndex < (sal_Int16)pListWidget->Entries.size() )
594                             pListWidget->SelectedEntries.push_back( nIndex );
595                     }
596                 }
597             }
598 
599             // --------------------------------
600             // combo boxes
601             if ( _rpDescriptor->getType() == ::vcl::PDFWriter::ComboBox )
602             {
603                 ::vcl::PDFWriter::ComboBoxWidget* pComboWidget = static_cast< ::vcl::PDFWriter::ComboBoxWidget* >( _rpDescriptor.get() );
604                 // ............................
605                 // entries
606                 getStringItemVector( xModelProps, pComboWidget->Entries );
607                 // same reasoning as above
608                 pComboWidget->Sort = false;
609             }
610 
611             // ================================
612             // some post-processing
613             // --------------------------------
614             // text line ends
615             // some controls may (always or dependent on other settings) return UNIX line ends
616             String aConverter( _rpDescriptor->Text );
617             _rpDescriptor->Text = aConverter.ConvertLineEnd( LINEEND_CRLF );
618         }
619         catch( const Exception& )
620         {
621             DBG_UNHANDLED_EXCEPTION();
622         }
623     }
624 
625 //........................................................................
626 } // namespace toolkitform
627 //........................................................................
628