cURL: follow redirects
[LibreOffice.git] / toolkit / source / helper / formpdfexport.cxx
blobe02ba19bbdfac537c368620f1ded9599c0450c73
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <toolkit/helper/formpdfexport.hxx>
23 #include <com/sun/star/container/XIndexAccess.hpp>
24 #include <com/sun/star/container/XNameAccess.hpp>
25 #include <com/sun/star/container/XNameContainer.hpp>
26 #include <com/sun/star/form/XForm.hpp>
27 #include <com/sun/star/container/XChild.hpp>
28 #include <com/sun/star/lang/XServiceInfo.hpp>
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/form/FormComponentType.hpp>
31 #include <com/sun/star/awt/TextAlign.hpp>
32 #include <com/sun/star/style/VerticalAlignment.hpp>
33 #include <com/sun/star/form/FormButtonType.hpp>
34 #include <com/sun/star/form/FormSubmitMethod.hpp>
36 #include <toolkit/helper/vclunohelper.hxx>
37 #include <vcl/pdfextoutdevdata.hxx>
38 #include <vcl/outdev.hxx>
40 #include <functional>
41 #include <algorithm>
42 #include <iterator>
45 namespace toolkitform
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::uno;
51 using namespace ::com::sun::star::awt;
52 using namespace ::com::sun::star::style;
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::form;
55 using namespace ::com::sun::star::lang;
56 using namespace ::com::sun::star::container;
58 static const char FM_PROP_NAME[] = "Name";
60 namespace
63 /** determines the FormComponentType of a form control
65 sal_Int16 classifyFormControl( const Reference< XPropertySet >& _rxModel )
67 static const char FM_PROP_CLASSID[] = "ClassId";
68 sal_Int16 nControlType = FormComponentType::CONTROL;
70 Reference< XPropertySetInfo > xPSI;
71 if ( _rxModel.is() )
72 xPSI = _rxModel->getPropertySetInfo();
73 if ( xPSI.is() && xPSI->hasPropertyByName( FM_PROP_CLASSID ) )
75 OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_CLASSID ) >>= nControlType );
78 return nControlType;
82 /** (default-)creates a PDF widget according to a given FormComponentType
84 vcl::PDFWriter::AnyWidget* createDefaultWidget( sal_Int16 _nFormComponentType )
86 switch ( _nFormComponentType )
88 case FormComponentType::COMMANDBUTTON:
89 return new vcl::PDFWriter::PushButtonWidget;
90 case FormComponentType::CHECKBOX:
91 return new vcl::PDFWriter::CheckBoxWidget;
92 case FormComponentType::RADIOBUTTON:
93 return new vcl::PDFWriter::RadioButtonWidget;
94 case FormComponentType::LISTBOX:
95 return new vcl::PDFWriter::ListBoxWidget;
96 case FormComponentType::COMBOBOX:
97 return new vcl::PDFWriter::ComboBoxWidget;
99 case FormComponentType::TEXTFIELD:
100 case FormComponentType::FILECONTROL:
101 case FormComponentType::DATEFIELD:
102 case FormComponentType::TIMEFIELD:
103 case FormComponentType::NUMERICFIELD:
104 case FormComponentType::CURRENCYFIELD:
105 case FormComponentType::PATTERNFIELD:
106 return new vcl::PDFWriter::EditWidget;
108 return nullptr;
112 /** determines a unique number for the radio group which the given radio
113 button model belongs to
115 The number is guaranteed to be
116 <ul><li>unique within the document in which the button lives</li>
117 <li>the same for subsequent calls with other radio button models,
118 which live in the same document, and belong to the same group</li>
119 </ul>
121 @precond
122 the model must be part of the form component hierarchy in a document
124 sal_Int32 determineRadioGroupId( const Reference< XPropertySet >& _rxRadioModel )
126 OSL_ENSURE( classifyFormControl( _rxRadioModel ) == FormComponentType::RADIOBUTTON,
127 "determineRadioGroupId: this *is* no radio button model!" );
128 // The fact that radio button groups need to be unique within the complete
129 // host document makes it somewhat difficult ...
130 // Problem is that two form radio buttons belong to the same group if
131 // - they have the same parent
132 // - AND they have the same name
133 // This implies that we need some knowledge about (potentially) *all* radio button
134 // groups in the document.
136 // get the root-level container
137 Reference< XChild > xChild( _rxRadioModel, UNO_QUERY );
138 Reference< XForm > xParentForm( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY );
139 OSL_ENSURE( xParentForm.is(), "determineRadioGroupId: no parent form -> group id!" );
140 if ( !xParentForm.is() )
141 return -1;
143 while ( xParentForm.is() )
145 xChild = xParentForm.get();
146 xParentForm.set(xChild->getParent(), css::uno::UNO_QUERY);
148 Reference< XIndexAccess > xRoot( xChild->getParent(), UNO_QUERY );
149 OSL_ENSURE( xRoot.is(), "determineRadioGroupId: unable to determine the root of the form component hierarchy!" );
150 if ( !xRoot.is() )
151 return -1;
153 // count the leafs in the hierarchy, until we encounter radio button
154 ::std::vector< Reference< XIndexAccess > > aAncestors;
155 ::std::vector< sal_Int32 > aPath;
157 Reference< XInterface > xNormalizedLookup( _rxRadioModel, UNO_QUERY );
158 OUString sRadioGroupName;
159 OSL_VERIFY( _rxRadioModel->getPropertyValue( FM_PROP_NAME ) >>= sRadioGroupName );
161 Reference< XIndexAccess > xCurrentContainer( xRoot );
162 sal_Int32 nStartWithChild = 0;
163 sal_Int32 nGroupsEncountered = 0;
166 Reference< XNameAccess > xElementNameAccess( xCurrentContainer, UNO_QUERY );
167 OSL_ENSURE( xElementNameAccess.is(), "determineRadioGroupId: no name container?" );
168 if ( !xElementNameAccess.is() )
169 return -1;
171 if ( nStartWithChild == 0 )
172 { // we encounter this container the first time. In particular, we did not
173 // just step up
174 nGroupsEncountered += xElementNameAccess->getElementNames().getLength();
175 // this is way too much: Not all of the elements in the current container
176 // may form groups, especially if they're forms. But anyway, this number is
177 // sufficient for our purpose. Finally, the container contains *at most*
178 // that much groups
181 sal_Int32 nCount = xCurrentContainer->getCount();
182 sal_Int32 i;
183 for ( i = nStartWithChild; i < nCount; ++i )
185 Reference< XInterface > xElement( xCurrentContainer->getByIndex( i ), UNO_QUERY );
186 if ( !xElement.is() )
188 OSL_FAIL( "determineRadioGroupId: very suspicious!" );
189 continue;
192 Reference< XIndexAccess > xNewContainer( xElement, UNO_QUERY );
193 if ( xNewContainer.is() )
195 // step down the hierarchy
196 aAncestors.push_back( xCurrentContainer );
197 xCurrentContainer = xNewContainer;
198 aPath.push_back( i );
199 nStartWithChild = 0;
200 break;
201 // out of the inner loop, but continue with the outer loop
204 if ( xElement.get() == xNormalizedLookup.get() )
206 // look up the name of the radio group in the list of all element names
207 Sequence< OUString > aElementNames( xElementNameAccess->getElementNames() );
208 const OUString* pElementNames = aElementNames.getConstArray();
209 const OUString* pElementNamesEnd = pElementNames + aElementNames.getLength();
210 while ( pElementNames != pElementNamesEnd )
212 if ( *pElementNames == sRadioGroupName )
214 sal_Int32 nLocalGroupIndex = pElementNames - aElementNames.getConstArray();
215 OSL_ENSURE( nLocalGroupIndex < xElementNameAccess->getElementNames().getLength(),
216 "determineRadioGroupId: inconsistency!" );
218 sal_Int32 nGlobalGroupId = nGroupsEncountered - xElementNameAccess->getElementNames().getLength() + nLocalGroupIndex;
219 return nGlobalGroupId;
221 ++pElementNames;
223 OSL_FAIL( "determineRadioGroupId: did not find the radios element name!" );
227 if ( !( i < nCount ) )
229 // the loop terminated because there were no more elements
230 // -> step up, if possible
231 if ( aAncestors.empty() )
232 break;
234 xCurrentContainer = aAncestors.back(); aAncestors.pop_back();
235 nStartWithChild = aPath.back() + 1; aPath.pop_back();
238 while ( true );
239 return -1;
243 /** copies a StringItemList to a PDF widget's list
245 void getStringItemVector( const Reference< XPropertySet >& _rxModel, ::std::vector< OUString >& _rVector )
247 Sequence< OUString > aListEntries;
248 OSL_VERIFY( _rxModel->getPropertyValue( "StringItemList" ) >>= aListEntries );
249 ::std::copy( aListEntries.begin(), aListEntries.end(),
250 ::std::back_insert_iterator< ::std::vector< OUString > >( _rVector ) );
255 /** creates a PDF compatible control descriptor for the given control
257 std::unique_ptr<vcl::PDFWriter::AnyWidget> describePDFControl( const Reference< XControl >& _rxControl,
258 vcl::PDFExtOutDevData& i_pdfExportData )
260 std::unique_ptr<vcl::PDFWriter::AnyWidget> Descriptor;
261 OSL_ENSURE( _rxControl.is(), "describePDFControl: invalid (NULL) control!" );
262 if ( !_rxControl.is() )
263 return Descriptor;
267 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
268 sal_Int16 nControlType = classifyFormControl( xModelProps );
269 Descriptor.reset( createDefaultWidget( nControlType ) );
270 if ( !Descriptor.get() )
271 // no PDF widget available for this
272 return Descriptor;
274 Reference< XPropertySetInfo > xPSI( xModelProps->getPropertySetInfo() );
275 Reference< XServiceInfo > xSI( xModelProps, UNO_QUERY );
276 OSL_ENSURE( xSI.is(), "describePDFControl: no service info!" );
277 // if we survived classifyFormControl, then it's a real form control, and they all have
278 // service infos
281 // set the common widget properties
284 // Name, Description, Text
285 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_NAME ) >>= Descriptor->Name );
286 OSL_VERIFY( xModelProps->getPropertyValue( "HelpText" ) >>= Descriptor->Description );
287 Any aText;
288 static const char FM_PROP_TEXT[] = "Text";
289 static const char FM_PROP_LABEL[] = "Label";
290 if ( xPSI->hasPropertyByName( FM_PROP_TEXT ) )
291 aText = xModelProps->getPropertyValue( FM_PROP_TEXT );
292 else if ( xPSI->hasPropertyByName( FM_PROP_LABEL ) )
293 aText = xModelProps->getPropertyValue( FM_PROP_LABEL );
294 if ( aText.hasValue() )
295 OSL_VERIFY( aText >>= Descriptor->Text );
298 // readonly
299 static const char FM_PROP_READONLY[] = "ReadOnly";
300 if ( xPSI->hasPropertyByName( FM_PROP_READONLY ) )
301 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= Descriptor->ReadOnly );
304 // border
306 static const char FM_PROP_BORDER[] = "Border";
307 if ( xPSI->hasPropertyByName( FM_PROP_BORDER ) )
309 sal_Int16 nBorderType = 0;
310 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_BORDER ) >>= nBorderType );
311 Descriptor->Border = ( nBorderType != 0 );
313 OUString sBorderColorPropertyName( "BorderColor" );
314 if ( xPSI->hasPropertyByName( sBorderColorPropertyName ) )
316 sal_Int32 nBoderColor = COL_TRANSPARENT;
317 if ( xModelProps->getPropertyValue( sBorderColorPropertyName ) >>= nBoderColor )
318 Descriptor->BorderColor = Color( nBoderColor );
319 else
320 Descriptor->BorderColor = Color( COL_BLACK );
326 // background color
327 static const char FM_PROP_BACKGROUNDCOLOR[] = "BackgroundColor";
328 if ( xPSI->hasPropertyByName( FM_PROP_BACKGROUNDCOLOR ) )
330 sal_Int32 nBackColor = COL_TRANSPARENT;
331 xModelProps->getPropertyValue( FM_PROP_BACKGROUNDCOLOR ) >>= nBackColor;
332 Descriptor->Background = true;
333 Descriptor->BackgroundColor = Color( nBackColor );
337 // text color
338 static const char FM_PROP_TEXTCOLOR[] = "TextColor";
339 if ( xPSI->hasPropertyByName( FM_PROP_TEXTCOLOR ) )
341 sal_Int32 nTextColor = COL_TRANSPARENT;
342 xModelProps->getPropertyValue( FM_PROP_TEXTCOLOR ) >>= nTextColor;
343 Descriptor->TextColor = Color( nTextColor );
347 // text style
348 Descriptor->TextStyle = DrawTextFlags::NONE;
350 // multi line and word break
351 // The MultiLine property of the control is mapped to both the "MULTILINE" and
352 // "WORDBREAK" style flags
353 static const char FM_PROP_MULTILINE[] = "MultiLine";
354 if ( xPSI->hasPropertyByName( FM_PROP_MULTILINE ) )
356 bool bMultiLine = false;
357 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_MULTILINE ) >>= bMultiLine );
358 if ( bMultiLine )
359 Descriptor->TextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
362 // horizontal alignment
363 static const char FM_PROP_ALIGN[] = "Align";
364 if ( xPSI->hasPropertyByName( FM_PROP_ALIGN ) )
366 sal_Int16 nAlign = awt::TextAlign::LEFT;
367 xModelProps->getPropertyValue( FM_PROP_ALIGN ) >>= nAlign;
368 // TODO: when the property is VOID - are there situations/UIs where this
369 // means something else than LEFT?
370 switch ( nAlign )
372 case awt::TextAlign::LEFT: Descriptor->TextStyle |= DrawTextFlags::Left; break;
373 case awt::TextAlign::CENTER: Descriptor->TextStyle |= DrawTextFlags::Center; break;
374 case awt::TextAlign::RIGHT: Descriptor->TextStyle |= DrawTextFlags::Right; break;
375 default:
376 OSL_FAIL( "describePDFControl: invalid text align!" );
380 // vertical alignment
382 OUString sVertAlignPropertyName( "VerticalAlign" );
383 if ( xPSI->hasPropertyByName( sVertAlignPropertyName ) )
385 sal_Int16 nAlign = VerticalAlignment_MIDDLE;
386 xModelProps->getPropertyValue( sVertAlignPropertyName ) >>= nAlign;
387 switch ( nAlign )
389 case VerticalAlignment_TOP: Descriptor->TextStyle |= DrawTextFlags::Top; break;
390 case VerticalAlignment_MIDDLE: Descriptor->TextStyle |= DrawTextFlags::VCenter; break;
391 case VerticalAlignment_BOTTOM: Descriptor->TextStyle |= DrawTextFlags::Bottom; break;
392 default:
393 OSL_FAIL( "describePDFControl: invalid vertical text align!" );
398 // font
399 static const char FM_PROP_FONT[] = "FontDescriptor";
400 if ( xPSI->hasPropertyByName( FM_PROP_FONT ) )
402 FontDescriptor aUNOFont;
403 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_FONT ) >>= aUNOFont );
404 Descriptor->TextFont = VCLUnoHelper::CreateFont( aUNOFont, vcl::Font() );
407 // tab order
408 OUString aTabIndexString( "TabIndex" );
409 if ( xPSI->hasPropertyByName( aTabIndexString ) )
411 sal_Int16 nIndex = -1;
412 OSL_VERIFY( xModelProps->getPropertyValue( aTabIndexString ) >>= nIndex );
413 Descriptor->TabOrder = nIndex;
417 // special widget properties
419 // edits
420 if ( Descriptor->getType() == vcl::PDFWriter::Edit )
422 vcl::PDFWriter::EditWidget* pEditWidget = static_cast< vcl::PDFWriter::EditWidget* >( Descriptor.get() );
424 // multiline (already flagged in the TextStyle)
425 pEditWidget->MultiLine = bool( Descriptor->TextStyle & DrawTextFlags::MultiLine );
427 // password input
428 OUString sEchoCharPropName( "EchoChar" );
429 if ( xPSI->hasPropertyByName( sEchoCharPropName ) )
431 sal_Int16 nEchoChar = 0;
432 if ( ( xModelProps->getPropertyValue( sEchoCharPropName ) >>= nEchoChar ) && ( nEchoChar != 0 ) )
433 pEditWidget->Password = true;
436 // file select
437 if ( xSI->supportsService( "com.sun.star.form.component.FileControl" ) )
438 pEditWidget->FileSelect = true;
440 // maximum text length
441 static const char FM_PROP_MAXTEXTLEN[] = "MaxTextLen";
442 if ( xPSI->hasPropertyByName( FM_PROP_MAXTEXTLEN ) )
444 sal_Int16 nMaxTextLength = 0;
445 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_MAXTEXTLEN ) >>= nMaxTextLength );
446 if ( nMaxTextLength <= 0 )
447 // "-1" has a special meaning for database-bound controls
448 nMaxTextLength = 0;
449 pEditWidget->MaxLen = nMaxTextLength;
454 // buttons
455 if ( Descriptor->getType() == vcl::PDFWriter::PushButton )
457 vcl::PDFWriter::PushButtonWidget* pButtonWidget = static_cast< vcl::PDFWriter::PushButtonWidget* >( Descriptor.get() );
458 FormButtonType eButtonType = FormButtonType_PUSH;
459 OSL_VERIFY( xModelProps->getPropertyValue("ButtonType") >>= eButtonType );
460 static const char FM_PROP_TARGET_URL[] = "TargetURL";
461 if ( eButtonType == FormButtonType_SUBMIT )
463 // if a button is a submit button, then it uses the URL at its parent form
464 Reference< XChild > xChild( xModelProps, UNO_QUERY );
465 Reference < XPropertySet > xParentProps;
466 if ( xChild.is() )
467 xParentProps.set(xChild->getParent(), css::uno::UNO_QUERY);
468 if ( xParentProps.is() )
470 Reference< XServiceInfo > xParentSI( xParentProps, UNO_QUERY );
471 if ( xParentSI.is() && xParentSI->supportsService("com.sun.star.form.component.HTMLForm") )
473 OSL_VERIFY( xParentProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= pButtonWidget->URL );
474 pButtonWidget->Submit = true;
475 FormSubmitMethod eMethod = FormSubmitMethod_POST;
476 OSL_VERIFY( xParentProps->getPropertyValue("SubmitMethod") >>= eMethod );
477 pButtonWidget->SubmitGet = (eMethod == FormSubmitMethod_GET);
481 else if ( eButtonType == FormButtonType_URL )
483 OUString sURL;
484 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= sURL );
485 const bool bDocumentLocalTarget = sURL.startsWith("#");
486 if ( bDocumentLocalTarget )
488 // Register the destination for future handling ...
489 pButtonWidget->Dest = i_pdfExportData.RegisterDest();
491 // and put it into the bookmarks, to ensure the future handling really happens
492 ::std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks( i_pdfExportData.GetBookmarks() );
493 vcl::PDFExtOutDevBookmarkEntry aBookmark;
494 aBookmark.nDestId = pButtonWidget->Dest;
495 aBookmark.aBookmark = sURL;
496 rBookmarks.push_back( aBookmark );
498 else
499 pButtonWidget->URL = sURL;
501 pButtonWidget->Submit = false;
504 // TODO:
505 // In PDF files, buttons are either reset, url or submit buttons. So if we have a simple PUSH button
506 // in a document, then this means that we do not export a SubmitToURL, which means that in PDF,
507 // the button is used as reset button.
508 // Is this desired? If no, we would have to reset Descriptor to NULL here, in case eButtonType
509 // != FormButtonType_SUBMIT && != FormButtonType_RESET
511 // the PDF exporter defaults the text style, if 0. To prevent this, we have to transfer the UNO
512 // defaults to the PDF widget
513 if ( pButtonWidget->TextStyle == DrawTextFlags::NONE )
514 pButtonWidget->TextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter;
518 // check boxes
519 static const char FM_PROP_STATE[] = "State";
520 if ( Descriptor->getType() == vcl::PDFWriter::CheckBox )
522 vcl::PDFWriter::CheckBoxWidget* pCheckBoxWidget = static_cast< vcl::PDFWriter::CheckBoxWidget* >( Descriptor.get() );
523 sal_Int16 nState = 0;
524 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_STATE ) >>= nState );
525 pCheckBoxWidget->Checked = ( nState != 0 );
529 // radio buttons
530 if ( Descriptor->getType() == vcl::PDFWriter::RadioButton )
532 vcl::PDFWriter::RadioButtonWidget* pRadioWidget = static_cast< vcl::PDFWriter::RadioButtonWidget* >( Descriptor.get() );
533 sal_Int16 nState = 0;
534 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_STATE ) >>= nState );
535 pRadioWidget->Selected = ( nState != 0 );
536 pRadioWidget->RadioGroup = determineRadioGroupId( xModelProps );
539 xModelProps->getPropertyValue( "RefValue" ) >>= pRadioWidget->OnValue;
541 catch(...)
543 pRadioWidget->OnValue = "On";
548 // list boxes
549 if ( Descriptor->getType() == vcl::PDFWriter::ListBox )
551 vcl::PDFWriter::ListBoxWidget* pListWidget = static_cast< vcl::PDFWriter::ListBoxWidget* >( Descriptor.get() );
553 // drop down
554 OSL_VERIFY( xModelProps->getPropertyValue( "Dropdown" ) >>= pListWidget->DropDown );
556 // multi selection
557 OSL_VERIFY( xModelProps->getPropertyValue("MultiSelection") >>= pListWidget->MultiSelect );
559 // entries
560 getStringItemVector( xModelProps, pListWidget->Entries );
562 // get selected items
563 Sequence< sal_Int16 > aSelectIndices;
564 OSL_VERIFY( xModelProps->getPropertyValue("SelectedItems") >>= aSelectIndices );
565 if( aSelectIndices.getLength() > 0 )
567 pListWidget->SelectedEntries.resize( 0 );
568 for( sal_Int32 i = 0; i < aSelectIndices.getLength(); i++ )
570 sal_Int16 nIndex = aSelectIndices.getConstArray()[i];
571 if( nIndex >= 0 && nIndex < (sal_Int16)pListWidget->Entries.size() )
572 pListWidget->SelectedEntries.push_back( nIndex );
578 // combo boxes
579 if ( Descriptor->getType() == vcl::PDFWriter::ComboBox )
581 vcl::PDFWriter::ComboBoxWidget* pComboWidget = static_cast< vcl::PDFWriter::ComboBoxWidget* >( Descriptor.get() );
583 // entries
584 getStringItemVector( xModelProps, pComboWidget->Entries );
588 // some post-processing
590 // text line ends
591 // some controls may (always or dependent on other settings) return UNIX line ends
592 Descriptor->Text = convertLineEnd(Descriptor->Text, LINEEND_CRLF);
594 catch( const Exception& )
596 OSL_FAIL( "describePDFControl: caught an exception!" );
598 return Descriptor;
602 } // namespace toolkitform
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */