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