/** determines the FormComponentType of a form control
*/
sal_Int16 classifyFormControl( const Reference< XPropertySet >& _rxModel )
{ static constexpr OUString FM_PROP_CLASSID = u"ClassId"_ustr;
sal_Int16 nControlType = FormComponentType::CONTROL;
Reference< XPropertySetInfo > xPSI; if ( _rxModel.is() )
xPSI = _rxModel->getPropertySetInfo(); if ( xPSI.is() && xPSI->hasPropertyByName( FM_PROP_CLASSID ) )
{ if( ! (_rxModel->getPropertyValue( FM_PROP_CLASSID ) >>= nControlType) ) {
SAL_WARN("toolkit.helper", "classifyFormControl: unable to get property " << FM_PROP_CLASSID);
}
}
return nControlType;
}
/** (default-)creates a PDF widget according to a given FormComponentType
*/
std::unique_ptr<vcl::PDFWriter::AnyWidget> createDefaultWidget( sal_Int16 _nFormComponentType )
{ switch ( _nFormComponentType )
{ case FormComponentType::COMMANDBUTTON: return std::make_unique<vcl::PDFWriter::PushButtonWidget>(); case FormComponentType::CHECKBOX: return std::make_unique<vcl::PDFWriter::CheckBoxWidget>(); case FormComponentType::RADIOBUTTON: return std::make_unique<vcl::PDFWriter::RadioButtonWidget>(); case FormComponentType::LISTBOX: return std::make_unique<vcl::PDFWriter::ListBoxWidget>(); case FormComponentType::COMBOBOX: return std::make_unique<vcl::PDFWriter::ComboBoxWidget>();
case FormComponentType::TEXTFIELD: case FormComponentType::FILECONTROL: case FormComponentType::DATEFIELD: case FormComponentType::TIMEFIELD: case FormComponentType::NUMERICFIELD: case FormComponentType::CURRENCYFIELD: case FormComponentType::PATTERNFIELD: return std::make_unique<vcl::PDFWriter::EditWidget>();
} return nullptr;
}
/** determines a unique number for the radio group which the given radio buttonmodelbelongsto
@precond themodelmustbepartoftheformcomponenthierarchyinadocument
*/
sal_Int32 determineRadioGroupId( const Reference< XPropertySet >& _rxRadioModel )
{
OSL_ENSURE( classifyFormControl( _rxRadioModel ) == FormComponentType::RADIOBUTTON, "determineRadioGroupId: this *is* no radio button model!" ); // The fact that radio button groups need to be unique within the complete // host document makes it somewhat difficult ... // Problem is that two form radio buttons belong to the same group if // - they have the same parent // - AND they have the same name or group name // This implies that we need some knowledge about (potentially) *all* radio button // groups in the document.
// get the root-level container
Reference< XChild > xChild( _rxRadioModel, UNO_QUERY );
Reference< XForm > xParentForm( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY );
OSL_ENSURE( xParentForm.is(), "determineRadioGroupId: no parent form -> group id!" ); if ( !xParentForm.is() ) return -1;
while ( xParentForm.is() )
{
xChild = xParentForm.get();
xParentForm.set(xChild->getParent(), css::uno::UNO_QUERY);
}
Reference< XIndexAccess > xRoot( xChild->getParent(), UNO_QUERY );
OSL_ENSURE( xRoot.is(), "determineRadioGroupId: unable to determine the root of the form component hierarchy!" ); if ( !xRoot.is() ) return -1;
// count the leafs in the hierarchy, until we encounter radio button
::std::vector< Reference< XIndexAccess > > aAncestors;
::std::vector< sal_Int32 > aPath;
Reference< XInterface > xNormalizedLookup( _rxRadioModel, UNO_QUERY );
Reference< XIndexAccess > xCurrentContainer( xRoot );
sal_Int32 nStartWithChild = 0;
sal_Int32 nGroupsEncountered = 0; do
{
std::unordered_map<OUString,sal_Int32> GroupNameMap;
std::unordered_map<OUString,sal_Int32> SharedNameMap;
sal_Int32 nCount = xCurrentContainer->getCount();
sal_Int32 i; for ( i = nStartWithChild; i < nCount; ++i )
{
Reference< XInterface > xElement( xCurrentContainer->getByIndex( i ), UNO_QUERY ); if ( !xElement.is() )
{
OSL_FAIL( "determineRadioGroupId: very suspicious!" ); continue;
}
Reference< XIndexAccess > xNewContainer( xElement, UNO_QUERY ); if ( xNewContainer.is() )
{ // step down the hierarchy
aAncestors.push_back( xCurrentContainer );
xCurrentContainer = std::move(xNewContainer);
aPath.push_back( i );
nStartWithChild = 0; break; // out of the inner loop, but continue with the outer loop
}
if ( xElement.get() == xNormalizedLookup.get() )
{ // Our radio button is in this container. // Now take the time to ID this container's groups and return the button's groupId for ( i = 0; i < nCount; ++i )
{ try
{
xElement.set( xCurrentContainer->getByIndex( i ), UNO_QUERY_THROW );
Reference< XServiceInfo > xModelSI( xElement, UNO_QUERY_THROW ); if ( xModelSI->supportsService(u"com.sun.star.awt.UnoControlRadioButtonModel"_ustr) )
{
Reference< XPropertySet > aProps( xElement, UNO_QUERY_THROW );
OUString sGroupName;
aProps->getPropertyValue(u"GroupName"_ustr) >>= sGroupName; if ( !sGroupName.isEmpty() )
{ // map: unique key is the group name, so attempts to add a different ID value // for an existing group are ignored - keeping the first ID - perfect for this scenario.
GroupNameMap.emplace( sGroupName, nGroupsEncountered + i );
if ( xElement.get() == xNormalizedLookup.get() ) return GroupNameMap[sGroupName];
} else
{ // Old implementation didn't have a GroupName, just identical Control names.
aProps->getPropertyValue( FM_PROP_NAME ) >>= sGroupName;
SharedNameMap.emplace( sGroupName, nGroupsEncountered + i );
if ( xElement.get() == xNormalizedLookup.get() ) return SharedNameMap[sGroupName];
}
}
} catch( uno::Exception& )
{
DBG_UNHANDLED_EXCEPTION("toolkit");
}
}
SAL_WARN("toolkit.helper","determineRadioGroupId: did not find the radios element's group!" );
}
}
// we encounter this container the first time. In particular, we did not just step up if ( nStartWithChild == 0 )
{ // Our control wasn't in this container, so consider every item to be a possible unique group. // This is way too much: Not all of the elements in the current container will form groups. // But anyway, this number is sufficient for our purpose, since sequential group ids are not required. // Ultimately, the container contains *at most* this many groups.
nGroupsEncountered += nCount;
}
if ( i >= nCount )
{ // the loop terminated because there were no more elements // -> step up, if possible if ( aAncestors.empty() ) break;
/** copies a StringItemList to a PDF widget's list
*/ void getStringItemVector( const Reference< XPropertySet >& _rxModel, ::std::vector< OUString >& _rVector )
{
Sequence< OUString > aListEntries; if( ! (_rxModel->getPropertyValue( u"StringItemList"_ustr ) >>= aListEntries) ) {
SAL_WARN("toolkit.helper", "getStringItemVector: unable to get property StringItemList");
}
_rVector.insert( _rVector.end(), std::cbegin(aListEntries), std::cend(aListEntries) );
}
}
/** creates a PDF compatible control descriptor for the given control
*/
std::unique_ptr<vcl::PDFWriter::AnyWidget> describePDFControl( const Reference< XControl >& _rxControl,
vcl::PDFExtOutDevData& i_pdfExportData )
{
std::unique_ptr<vcl::PDFWriter::AnyWidget> Descriptor;
OSL_ENSURE( _rxControl.is(), "describePDFControl: invalid (NULL) control!" ); if ( !_rxControl.is() ) return Descriptor;
try
{
Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
sal_Int16 nControlType = classifyFormControl( xModelProps );
Descriptor = createDefaultWidget( nControlType ); if (!Descriptor) // no PDF widget available for this return Descriptor;
Reference< XPropertySetInfo > xPSI( xModelProps->getPropertySetInfo() );
Reference< XServiceInfo > xSI( xModelProps, UNO_QUERY );
OSL_ENSURE( xSI.is(), "describePDFControl: no service info!" ); // if we survived classifyFormControl, then it's a real form control, and they all have // service infos
// text color static constexpr OUString FM_PROP_TEXTCOLOR = u"TextColor"_ustr; if ( xPSI->hasPropertyByName( FM_PROP_TEXTCOLOR ) )
{
Color nTextColor = COL_TRANSPARENT;
xModelProps->getPropertyValue( FM_PROP_TEXTCOLOR ) >>= nTextColor;
Descriptor->TextColor = nTextColor;
}
// text style
Descriptor->TextStyle = DrawTextFlags::NONE;
// multi line and word break // The MultiLine property of the control is mapped to both the "MULTILINE" and // "WORDBREAK" style flags static constexpr OUString FM_PROP_MULTILINE = u"MultiLine"_ustr; if ( xPSI->hasPropertyByName( FM_PROP_MULTILINE ) )
{ bool bMultiLine = false; if( ! (xModelProps->getPropertyValue( FM_PROP_MULTILINE ) >>= bMultiLine) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property " << FM_PROP_MULTILINE); if ( bMultiLine )
Descriptor->TextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
}
// horizontal alignment static constexpr OUString FM_PROP_ALIGN = u"Align"_ustr; if ( xPSI->hasPropertyByName( FM_PROP_ALIGN ) )
{
sal_Int16 nAlign = awt::TextAlign::LEFT;
xModelProps->getPropertyValue( FM_PROP_ALIGN ) >>= nAlign; // TODO: when the property is VOID - are there situations/UIs where this // means something else than LEFT? switch ( nAlign )
{ case awt::TextAlign::LEFT: Descriptor->TextStyle |= DrawTextFlags::Left; break; case awt::TextAlign::CENTER: Descriptor->TextStyle |= DrawTextFlags::Center; break; case awt::TextAlign::RIGHT: Descriptor->TextStyle |= DrawTextFlags::Right; break; default:
OSL_FAIL( "describePDFControl: invalid text align!" );
}
}
// buttons if ( Descriptor->getType() == vcl::PDFWriter::PushButton )
{
vcl::PDFWriter::PushButtonWidget* pButtonWidget = static_cast< vcl::PDFWriter::PushButtonWidget* >( Descriptor.get() );
FormButtonType eButtonType = FormButtonType_PUSH; if( ! (xModelProps->getPropertyValue(u"ButtonType"_ustr) >>= eButtonType) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property ButtonType"); static constexpr OUString FM_PROP_TARGET_URL = u"TargetURL"_ustr; if ( eButtonType == FormButtonType_SUBMIT )
{ // if a button is a submit button, then it uses the URL at its parent form
Reference< XChild > xChild( xModelProps, UNO_QUERY );
Reference < XPropertySet > xParentProps; if ( xChild.is() )
xParentProps.set(xChild->getParent(), css::uno::UNO_QUERY); if ( xParentProps.is() )
{
Reference< XServiceInfo > xParentSI( xParentProps, UNO_QUERY ); if ( xParentSI.is() && xParentSI->supportsService(u"com.sun.star.form.component.HTMLForm"_ustr) )
{ if( ! (xParentProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= pButtonWidget->URL) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property " << FM_PROP_TARGET_URL);
pButtonWidget->Submit = true;
FormSubmitMethod eMethod = FormSubmitMethod_POST; if( ! (xParentProps->getPropertyValue(u"SubmitMethod"_ustr) >>= eMethod) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property " << FM_PROP_TARGET_URL);
pButtonWidget->SubmitGet = (eMethod == FormSubmitMethod_GET);
}
}
} elseif ( eButtonType == FormButtonType_URL )
{
OUString sURL; if( ! (xModelProps->getPropertyValue( FM_PROP_TARGET_URL ) >>= sURL) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property " << FM_PROP_TARGET_URL); constbool bDocumentLocalTarget = sURL.startsWith("#"); if ( bDocumentLocalTarget )
{ // Register the destination for future handling ...
pButtonWidget->Dest = i_pdfExportData.RegisterDest();
// and put it into the bookmarks, to ensure the future handling really happens
::std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks( i_pdfExportData.GetBookmarks() );
vcl::PDFExtOutDevBookmarkEntry aBookmark;
aBookmark.nDestId = pButtonWidget->Dest;
aBookmark.aBookmark = sURL;
rBookmarks.push_back( aBookmark );
} else
pButtonWidget->URL = sURL;
pButtonWidget->Submit = false;
}
// TODO: // In PDF files, buttons are either reset, url or submit buttons. So if we have a simple PUSH button // in a document, then this means that we do not export a SubmitToURL, which means that in PDF, // the button is used as reset button. // Is this desired? If no, we would have to reset Descriptor to NULL here, in case eButtonType // != FormButtonType_SUBMIT && != FormButtonType_RESET
// the PDF exporter defaults the text style, if 0. To prevent this, we have to transfer the UNO // defaults to the PDF widget if ( pButtonWidget->TextStyle == DrawTextFlags::NONE )
pButtonWidget->TextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter;
}
// list boxes if ( Descriptor->getType() == vcl::PDFWriter::ListBox )
{
vcl::PDFWriter::ListBoxWidget* pListWidget = static_cast< vcl::PDFWriter::ListBoxWidget* >( Descriptor.get() );
// drop down if( ! (xModelProps->getPropertyValue( u"Dropdown"_ustr ) >>= pListWidget->DropDown) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property Dropdown");
// multi selection if( ! (xModelProps->getPropertyValue(u"MultiSelection"_ustr) >>= pListWidget->MultiSelect) )
SAL_WARN("toolkit.helper", "describePDFControl: unable to get property MultiSelection");
// text line ends // some controls may (always or dependent on other settings) return UNIX line ends
Descriptor->Text = convertLineEnd(Descriptor->Text, LINEEND_CRLF);
} catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "toolkit", "describePDFControl" );
} return Descriptor;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.