1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <pagesettings.hxx>
21 
22 #include <algorithm>
23 #include <set>
24 #include <com/sun/star/awt/Size.hpp>
25 #include <com/sun/star/container/XNamed.hpp>
26 #include <com/sun/star/sheet/XHeaderFooterContent.hpp>
27 #include <com/sun/star/sheet/XSpreadsheet.hpp>
28 #include <com/sun/star/style/GraphicLocation.hpp>
29 #include <com/sun/star/style/XStyle.hpp>
30 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
31 #include <com/sun/star/text/XText.hpp>
32 #include <com/sun/star/text/XTextCursor.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <osl/diagnose.h>
35 #include <rtl/strbuf.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <sax/tools/converter.hxx>
38 #include <oox/helper/attributelist.hxx>
39 #include <oox/helper/binaryinputstream.hxx>
40 #include <oox/helper/graphichelper.hxx>
41 #include <oox/helper/propertymap.hxx>
42 #include <oox/helper/propertyset.hxx>
43 #include <oox/token/namespaces.hxx>
44 #include <oox/token/properties.hxx>
45 #include <oox/token/tokens.hxx>
46 #include <oox/core/filterbase.hxx>
47 #include <oox/core/relations.hxx>
48 #include <stylesbuffer.hxx>
49 #include <unitconverter.hxx>
50 #include <document.hxx>
51 #include <biffhelper.hxx>
52 #include <filter/msfilter/util.hxx>
53 
54 namespace oox::xls {
55 
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::uno;
59 
60 using ::oox::core::Relations;
61 
62 namespace {
63 
64 const double OOX_MARGIN_DEFAULT_LR                  = 0.748;    /// Left/right default margin in inches.
65 const double OOX_MARGIN_DEFAULT_TB                  = 0.984;    /// Top/bottom default margin in inches.
66 const double OOX_MARGIN_DEFAULT_HF                  = 0.512;    /// Header/footer default margin in inches.
67 
68 const sal_uInt16 BIFF12_PRINTOPT_HORCENTER          = 0x0001;
69 const sal_uInt16 BIFF12_PRINTOPT_VERCENTER          = 0x0002;
70 const sal_uInt16 BIFF12_PRINTOPT_PRINTHEADING       = 0x0004;
71 const sal_uInt16 BIFF12_PRINTOPT_PRINTGRID          = 0x0008;
72 
73 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFEVEN       = 0x0001;
74 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFFIRST      = 0x0002;
75 
76 const sal_uInt16 BIFF12_PAGESETUP_INROWS            = 0x0001;
77 const sal_uInt16 BIFF12_PAGESETUP_LANDSCAPE         = 0x0002;
78 const sal_uInt16 BIFF12_PAGESETUP_INVALID           = 0x0004;
79 const sal_uInt16 BIFF12_PAGESETUP_BLACKWHITE        = 0x0008;
80 const sal_uInt16 BIFF12_PAGESETUP_DRAFTQUALITY      = 0x0010;
81 const sal_uInt16 BIFF12_PAGESETUP_PRINTNOTES        = 0x0020;
82 const sal_uInt16 BIFF12_PAGESETUP_DEFAULTORIENT     = 0x0040;
83 const sal_uInt16 BIFF12_PAGESETUP_USEFIRSTPAGE      = 0x0080;
84 const sal_uInt16 BIFF12_PAGESETUP_NOTES_END         = 0x0100;   // different to BIFF flag
85 
86 const sal_uInt16 BIFF12_CHARTPAGESETUP_LANDSCAPE    = 0x0001;
87 const sal_uInt16 BIFF12_CHARTPAGESETUP_INVALID      = 0x0002;
88 const sal_uInt16 BIFF12_CHARTPAGESETUP_BLACKWHITE   = 0x0004;
89 const sal_uInt16 BIFF12_CHARTPAGESETUP_DEFAULTORIENT= 0x0008;
90 const sal_uInt16 BIFF12_CHARTPAGESETUP_USEFIRSTPAGE = 0x0010;
91 const sal_uInt16 BIFF12_CHARTPAGESETUP_DRAFTQUALITY = 0x0020;
92 
93 } // namespace
94 
PageSettingsModel()95 PageSettingsModel::PageSettingsModel() :
96     mfLeftMargin( OOX_MARGIN_DEFAULT_LR ),
97     mfRightMargin( OOX_MARGIN_DEFAULT_LR ),
98     mfTopMargin( OOX_MARGIN_DEFAULT_TB ),
99     mfBottomMargin( OOX_MARGIN_DEFAULT_TB ),
100     mfHeaderMargin( OOX_MARGIN_DEFAULT_HF ),
101     mfFooterMargin( OOX_MARGIN_DEFAULT_HF ),
102     mnPaperSize( 1 ),
103     mnPaperWidth( 0 ),
104     mnPaperHeight( 0 ),
105     mnCopies( 1 ),
106     mnScale( 100 ),
107     mnFirstPage( 1 ),
108     mnFitToWidth( 1 ),
109     mnFitToHeight( 1 ),
110     mnHorPrintRes( 600 ),
111     mnVerPrintRes( 600 ),
112     mnOrientation( XML_default ),
113     mnPageOrder( XML_downThenOver ),
114     mnCellComments( XML_none ),
115     mnPrintErrors( XML_displayed ),
116     mbUseEvenHF( false ),
117     mbUseFirstHF( false ),
118     mbValidSettings( true ),
119     mbUseFirstPage( false ),
120     mbBlackWhite( false ),
121     mbDraftQuality( false ),
122     mbFitToPages( false ),
123     mbHorCenter( false ),
124     mbVerCenter( false ),
125     mbPrintGrid( false ),
126     mbPrintHeadings( false )
127 {
128 }
129 
setBiffPrintErrors(sal_uInt8 nPrintErrors)130 void PageSettingsModel::setBiffPrintErrors( sal_uInt8 nPrintErrors )
131 {
132     static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA };
133     mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none );
134 }
135 
PageSettings(const WorksheetHelper & rHelper)136 PageSettings::PageSettings( const WorksheetHelper& rHelper ) :
137     WorksheetHelper( rHelper )
138 {
139 }
140 
importPrintOptions(const AttributeList & rAttribs)141 void PageSettings::importPrintOptions( const AttributeList& rAttribs )
142 {
143     maModel.mbHorCenter     = rAttribs.getBool( XML_horizontalCentered, false );
144     maModel.mbVerCenter     = rAttribs.getBool( XML_verticalCentered, false );
145     maModel.mbPrintGrid     = rAttribs.getBool( XML_gridLines, false );
146     maModel.mbPrintHeadings = rAttribs.getBool( XML_headings, false );
147 }
148 
importPageMargins(const AttributeList & rAttribs)149 void PageSettings::importPageMargins( const AttributeList& rAttribs )
150 {
151     maModel.mfLeftMargin   = rAttribs.getDouble( XML_left,   OOX_MARGIN_DEFAULT_LR );
152     maModel.mfRightMargin  = rAttribs.getDouble( XML_right,  OOX_MARGIN_DEFAULT_LR );
153     maModel.mfTopMargin    = rAttribs.getDouble( XML_top,    OOX_MARGIN_DEFAULT_TB );
154     maModel.mfBottomMargin = rAttribs.getDouble( XML_bottom, OOX_MARGIN_DEFAULT_TB );
155     maModel.mfHeaderMargin = rAttribs.getDouble( XML_header, OOX_MARGIN_DEFAULT_HF );
156     maModel.mfFooterMargin = rAttribs.getDouble( XML_footer, OOX_MARGIN_DEFAULT_HF );
157 }
158 
importPageSetup(const Relations & rRelations,const AttributeList & rAttribs)159 void PageSettings::importPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
160 {
161     OUString aStr;
162     maModel.maBinSettPath   = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
163     maModel.mnPaperSize     = rAttribs.getInteger( XML_paperSize, 1 );
164     aStr                    = rAttribs.getString ( XML_paperWidth, OUString() );
165     ::sax::Converter::convertMeasure(
166             maModel.mnPaperWidth, aStr);
167     aStr                    = rAttribs.getString ( XML_paperHeight, OUString() );
168     ::sax::Converter::convertMeasure(
169             maModel.mnPaperHeight, aStr );
170     maModel.mnCopies        = rAttribs.getInteger( XML_copies, 1 );
171     maModel.mnScale         = rAttribs.getInteger( XML_scale, 100 );
172     maModel.mnFirstPage     = rAttribs.getInteger( XML_firstPageNumber, 1 );
173     maModel.mnFitToWidth    = rAttribs.getInteger( XML_fitToWidth, 1 );
174     maModel.mnFitToHeight   = rAttribs.getInteger( XML_fitToHeight, 1 );
175     maModel.mnHorPrintRes   = rAttribs.getInteger( XML_horizontalDpi, 600 );
176     maModel.mnVerPrintRes   = rAttribs.getInteger( XML_verticalDpi, 600 );
177     maModel.mnOrientation   = rAttribs.getToken( XML_orientation, XML_default );
178     maModel.mnPageOrder     = rAttribs.getToken( XML_pageOrder, XML_downThenOver );
179     maModel.mnCellComments  = rAttribs.getToken( XML_cellComments, XML_none );
180     maModel.mnPrintErrors   = rAttribs.getToken( XML_errors, XML_displayed );
181     maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
182     maModel.mbUseFirstPage  = rAttribs.getBool( XML_useFirstPageNumber, false );
183     maModel.mbBlackWhite    = rAttribs.getBool( XML_blackAndWhite, false );
184     maModel.mbDraftQuality  = rAttribs.getBool( XML_draft, false );
185 }
186 
importChartPageSetup(const Relations & rRelations,const AttributeList & rAttribs)187 void PageSettings::importChartPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
188 {
189     OUString aStr;
190     maModel.maBinSettPath   = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
191     maModel.mnPaperSize     = rAttribs.getInteger( XML_paperSize, 1 );
192     aStr                    = rAttribs.getString ( XML_paperWidth, OUString() );
193     ::sax::Converter::convertMeasure(
194             maModel.mnPaperWidth, aStr );
195     aStr                    = rAttribs.getString ( XML_paperHeight, OUString() );
196     ::sax::Converter::convertMeasure(
197             maModel.mnPaperHeight, aStr );
198     maModel.mnCopies        = rAttribs.getInteger( XML_copies, 1 );
199     maModel.mnFirstPage     = rAttribs.getInteger( XML_firstPageNumber, 1 );
200     maModel.mnHorPrintRes   = rAttribs.getInteger( XML_horizontalDpi, 600 );
201     maModel.mnVerPrintRes   = rAttribs.getInteger( XML_verticalDpi, 600 );
202     maModel.mnOrientation   = rAttribs.getToken( XML_orientation, XML_default );
203     maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
204     maModel.mbUseFirstPage  = rAttribs.getBool( XML_useFirstPageNumber, false );
205     maModel.mbBlackWhite    = rAttribs.getBool( XML_blackAndWhite, false );
206     maModel.mbDraftQuality  = rAttribs.getBool( XML_draft, false );
207 }
208 
importHeaderFooter(const AttributeList & rAttribs)209 void PageSettings::importHeaderFooter( const AttributeList& rAttribs )
210 {
211     maModel.mbUseEvenHF  = rAttribs.getBool( XML_differentOddEven, false );
212     maModel.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false );
213 }
214 
importHeaderFooterCharacters(std::u16string_view rChars,sal_Int32 nElement)215 void PageSettings::importHeaderFooterCharacters( std::u16string_view rChars, sal_Int32 nElement )
216 {
217     switch( nElement )
218     {
219         case XLS_TOKEN( oddHeader ):    maModel.maOddHeader += rChars;      break;
220         case XLS_TOKEN( oddFooter ):    maModel.maOddFooter += rChars;      break;
221         case XLS_TOKEN( evenHeader ):   maModel.maEvenHeader += rChars;     break;
222         case XLS_TOKEN( evenFooter ):   maModel.maEvenFooter += rChars;     break;
223         case XLS_TOKEN( firstHeader ):  maModel.maFirstHeader += rChars;    break;
224         case XLS_TOKEN( firstFooter ):  maModel.maFirstFooter += rChars;    break;
225     }
226 }
227 
importPicture(const Relations & rRelations,const AttributeList & rAttribs)228 void PageSettings::importPicture( const Relations& rRelations, const AttributeList& rAttribs )
229 {
230     importPictureData( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
231 }
232 
importPageMargins(SequenceInputStream & rStrm)233 void PageSettings::importPageMargins( SequenceInputStream& rStrm )
234 {
235     maModel.mfLeftMargin = rStrm.readDouble();
236     maModel.mfRightMargin = rStrm.readDouble();
237     maModel.mfTopMargin = rStrm.readDouble();
238     maModel.mfBottomMargin = rStrm.readDouble();
239     maModel.mfHeaderMargin = rStrm.readDouble();
240     maModel.mfFooterMargin = rStrm.readDouble();
241 }
242 
importPrintOptions(SequenceInputStream & rStrm)243 void PageSettings::importPrintOptions( SequenceInputStream& rStrm )
244 {
245     sal_uInt16 nFlags;
246     nFlags = rStrm.readuInt16();
247     maModel.mbHorCenter     = getFlag( nFlags, BIFF12_PRINTOPT_HORCENTER );
248     maModel.mbVerCenter     = getFlag( nFlags, BIFF12_PRINTOPT_VERCENTER );
249     maModel.mbPrintGrid     = getFlag( nFlags, BIFF12_PRINTOPT_PRINTGRID );
250     maModel.mbPrintHeadings = getFlag( nFlags, BIFF12_PRINTOPT_PRINTHEADING );
251 }
252 
importPageSetup(const Relations & rRelations,SequenceInputStream & rStrm)253 void PageSettings::importPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
254 {
255     OUString aRelId;
256     sal_uInt16 nFlags;
257     maModel.mnPaperSize = rStrm.readInt32();
258     maModel.mnScale = rStrm.readInt32();
259     maModel.mnHorPrintRes = rStrm.readInt32();
260     maModel.mnVerPrintRes = rStrm.readInt32();
261     maModel.mnCopies = rStrm.readInt32();
262     maModel.mnFirstPage = rStrm.readInt32();
263     maModel.mnFitToWidth = rStrm.readInt32();
264     maModel.mnFitToHeight = rStrm.readInt32();
265     nFlags = rStrm.readuInt16();
266     rStrm >> aRelId;
267     maModel.setBiffPrintErrors( extractValue< sal_uInt8 >( nFlags, 9, 2 ) );
268     maModel.maBinSettPath   = rRelations.getFragmentPathFromRelId( aRelId );
269     maModel.mnOrientation   = getFlagValue( nFlags, BIFF12_PAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_PAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
270     maModel.mnPageOrder     = getFlagValue( nFlags, BIFF12_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver );
271     maModel.mnCellComments  = getFlagValue( nFlags, BIFF12_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, BIFF12_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none );
272     maModel.mbValidSettings = !getFlag( nFlags, BIFF12_PAGESETUP_INVALID );
273     maModel.mbUseFirstPage  = getFlag( nFlags, BIFF12_PAGESETUP_USEFIRSTPAGE );
274     maModel.mbBlackWhite    = getFlag( nFlags, BIFF12_PAGESETUP_BLACKWHITE );
275     maModel.mbDraftQuality  = getFlag( nFlags, BIFF12_PAGESETUP_DRAFTQUALITY );
276 }
277 
importChartPageSetup(const Relations & rRelations,SequenceInputStream & rStrm)278 void PageSettings::importChartPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
279 {
280     OUString aRelId;
281     sal_uInt16 nFirstPage, nFlags;
282     maModel.mnPaperSize = rStrm.readInt32();
283     maModel.mnHorPrintRes = rStrm.readInt32();
284     maModel.mnVerPrintRes = rStrm.readInt32();
285     maModel.mnCopies = rStrm.readInt32();
286     nFirstPage = rStrm.readuInt16();
287     nFlags = rStrm.readuInt16();
288     rStrm >> aRelId;
289     maModel.maBinSettPath   = rRelations.getFragmentPathFromRelId( aRelId );
290     maModel.mnFirstPage     = nFirstPage; // 16-bit in CHARTPAGESETUP
291     maModel.mnOrientation   = getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
292     maModel.mbValidSettings = !getFlag( nFlags, BIFF12_CHARTPAGESETUP_INVALID );
293     maModel.mbUseFirstPage  = getFlag( nFlags, BIFF12_CHARTPAGESETUP_USEFIRSTPAGE );
294     maModel.mbBlackWhite    = getFlag( nFlags, BIFF12_CHARTPAGESETUP_BLACKWHITE );
295     maModel.mbDraftQuality  = getFlag( nFlags, BIFF12_CHARTPAGESETUP_DRAFTQUALITY );
296 }
297 
importHeaderFooter(SequenceInputStream & rStrm)298 void PageSettings::importHeaderFooter( SequenceInputStream& rStrm )
299 {
300     sal_uInt16 nFlags;
301     nFlags = rStrm.readuInt16();
302     rStrm   >> maModel.maOddHeader   >> maModel.maOddFooter
303             >> maModel.maEvenHeader  >> maModel.maEvenFooter
304             >> maModel.maFirstHeader >> maModel.maFirstFooter;
305     maModel.mbUseEvenHF  = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFEVEN );
306     maModel.mbUseFirstHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFFIRST );
307 }
308 
importPicture(const Relations & rRelations,SequenceInputStream & rStrm)309 void PageSettings::importPicture( const Relations& rRelations, SequenceInputStream& rStrm )
310 {
311     importPictureData( rRelations, BiffHelper::readString( rStrm ) );
312 }
313 
setFitToPagesMode(bool bFitToPages)314 void PageSettings::setFitToPagesMode( bool bFitToPages )
315 {
316     maModel.mbFitToPages = bFitToPages;
317 }
318 
finalizeImport()319 void PageSettings::finalizeImport()
320 {
321     OUStringBuffer aStyleNameBuffer( "PageStyle_" );
322     Reference<container::XNamed> xSheetName(getSheet(), UNO_QUERY);
323     if( xSheetName.is() )
324         aStyleNameBuffer.append( xSheetName->getName() );
325     else
326         aStyleNameBuffer.append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) );
327     OUString aStyleName = aStyleNameBuffer.makeStringAndClear();
328 
329     Reference<style::XStyle> xStyle = createStyleObject(aStyleName, true);
330     PropertySet aStyleProps( xStyle );
331     getPageSettingsConverter().writePageSettingsProperties( aStyleProps, maModel, getSheetType() );
332 
333     // Set page style name to the sheet.
334     SCTAB nTab = getSheetIndex();
335     getScDocument().SetPageStyle(nTab, aStyleName);
336 }
337 
importPictureData(const Relations & rRelations,const OUString & rRelId)338 void PageSettings::importPictureData( const Relations& rRelations, const OUString& rRelId )
339 {
340     OUString aPicturePath = rRelations.getFragmentPathFromRelId(rRelId);
341     if (!aPicturePath.isEmpty())
342     {
343         maModel.mxGraphic = getBaseFilter().getGraphicHelper().importEmbeddedGraphic(aPicturePath);
344     }
345 }
346 
347 namespace {
348 
349 enum HFPortionId
350 {
351     HF_LEFT,
352     HF_CENTER,
353     HF_RIGHT,
354     HF_COUNT
355 };
356 
357 struct HFPortionInfo
358 {
359     Reference<text::XText>  mxText;                 /// XText interface of this portion.
360     Reference<text::XTextCursor> mxStart;           /// Start position of current text range for formatting.
361     Reference<text::XTextCursor> mxEnd;             /// End position of current text range for formatting.
362     double              mfTotalHeight;          /// Sum of heights of previous lines in points.
363     double              mfCurrHeight;           /// Height of the current text line in points.
364 
365     bool                initialize( const Reference<text::XText>& rxText );
366 };
367 
368 }
369 
initialize(const Reference<text::XText> & rxText)370 bool HFPortionInfo::initialize( const Reference<text::XText>& rxText )
371 {
372     mfTotalHeight = mfCurrHeight = 0.0;
373     mxText = rxText;
374     if( mxText.is() )
375     {
376         mxStart = mxText->createTextCursor();
377         mxEnd = mxText->createTextCursor();
378     }
379     bool bRet = mxText.is() && mxStart.is() && mxEnd.is();
380     OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" );
381     return bRet;
382 }
383 
384 class HeaderFooterParser : public WorkbookHelper
385 {
386 public:
387     explicit            HeaderFooterParser( const WorkbookHelper& rHelper );
388 
389     /** Parses the passed string and creates the header/footer contents.
390         @returns  The total height of the converted header or footer in points. */
391     double              parse(
392                             const Reference<sheet::XHeaderFooterContent>& rxContext,
393                             const OUString& rData );
394 
395 private:
396     /** Returns the current edit engine text object. */
getPortion()397     HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; }
398     /** Returns the start cursor of the current text range. */
getStartPos()399     const Reference<text::XTextCursor>& getStartPos() { return getPortion().mxStart; }
400     /** Returns the end cursor of the current text range. */
getEndPos()401     const Reference<text::XTextCursor>& getEndPos() { return getPortion().mxEnd; }
402 
403     /** Returns the current line height of the specified portion. */
404     double              getCurrHeight( HFPortionId ePortion ) const;
405 
406     /** Updates the current line height of the specified portion, using the current font size. */
407     void                updateCurrHeight( HFPortionId ePortion );
408     /** Updates the current line height, using the current font size. */
409     void                updateCurrHeight();
410 
411     /** Sets the font attributes at the current selection. */
412     void                setAttributes();
413     /** Appends and clears internal string buffer. */
414     void                appendText();
415     /** Appends a line break and adjusts internal text height data. */
416     void                appendLineBreak();
417 
418     /** Creates a text field from the passed service name. */
419     Reference<text::XTextContent> createField( const OUString& rServiceName ) const;
420     /** Appends the passed text field. */
421     void                appendField( const Reference<text::XTextContent>& rxContent );
422 
423     /** Sets the passed font name if it is valid. */
424     void                convertFontName( const OUString& rStyle );
425     /** Converts a font style given as string. */
426     void                convertFontStyle( const OUString& rStyle );
427     /** Converts a font color given as string. */
428     void                convertFontColor( const OUString& rColor );
429 
430     /** Finalizes current portion: sets font attributes and updates text height data. */
431     void                finalizePortion();
432     /** Changes current header/footer portion. */
433     void                setNewPortion( HFPortionId ePortion );
434 
435 private:
436     typedef ::std::vector< HFPortionInfo >  HFPortionInfoVec;
437 
438     const std::set< OString >    maBoldNames;            /// All names for bold font style in lowercase UTF-8.
439     const std::set< OString >    maItalicNames;          /// All names for italic font style in lowercase UTF-8.
440     HFPortionInfoVec    maPortions;
441     HFPortionId         meCurrPortion;          /// Identifier of current H/F portion.
442     OUStringBuffer      maBuffer;               /// Text data to append to current text range.
443     FontModel           maFontModel;            /// Font attributes of current text range.
444 };
445 
446 namespace {
447 
448 // different names for bold font style (lowercase)
449 const char* const sppcBoldNames[] =
450 {
451     "bold",
452     "fett",             // German 'bold'
453     "demibold",
454     "halbfett",         // German 'demibold'
455     "black",
456     "heavy",
457     "f\303\251lk\303\266v\303\251r"  // Hungarian 'bold'
458 };
459 
460 // different names for italic font style (lowercase)
461 const char* const sppcItalicNames[] =
462 {
463     "italic",
464     "kursiv",           // German 'italic'
465     "oblique",
466     "schr\303\204g",    // German 'oblique' with uppercase A umlaut
467     "schr\303\244g",    // German 'oblique' with lowercase A umlaut
468     "d\305\221lt"       // Hungarian 'italic'
469 };
470 
471 } // namespace
472 
473 constexpr OUStringLiteral gaPageNumberService( u"com.sun.star.text.TextField.PageNumber" );
474 constexpr OUStringLiteral gaPageCountService( u"com.sun.star.text.TextField.PageCount" );
475 constexpr OUStringLiteral gaSheetNameService( u"com.sun.star.text.TextField.SheetName" );
476 constexpr OUStringLiteral gaFileNameService( u"com.sun.star.text.TextField.FileName" );
477 constexpr OUStringLiteral gaDateTimeService( u"com.sun.star.text.TextField.DateTime" );
478 
HeaderFooterParser(const WorkbookHelper & rHelper)479 HeaderFooterParser::HeaderFooterParser( const WorkbookHelper& rHelper ) :
480     WorkbookHelper( rHelper ),
481     maBoldNames( sppcBoldNames, sppcBoldNames + SAL_N_ELEMENTS(sppcBoldNames) ),
482     maItalicNames( sppcItalicNames, sppcItalicNames + SAL_N_ELEMENTS(sppcItalicNames) ),
483     maPortions( static_cast< size_t >( HF_COUNT ) ),
484     meCurrPortion( HF_CENTER )
485 {
486 }
487 
parse(const Reference<sheet::XHeaderFooterContent> & rxContext,const OUString & rData)488 double HeaderFooterParser::parse( const Reference<sheet::XHeaderFooterContent>& rxContext, const OUString& rData )
489 {
490     if( !rxContext.is() || rData.isEmpty() ||
491             !maPortions[ HF_LEFT ].initialize( rxContext->getLeftText() ) ||
492             !maPortions[ HF_CENTER ].initialize( rxContext->getCenterText() ) ||
493             !maPortions[ HF_RIGHT ].initialize( rxContext->getRightText() ) )
494         return 0.0;
495 
496     meCurrPortion = HF_CENTER;
497     maBuffer.setLength( 0 );
498     maFontModel = getStyles().getDefaultFontModel();
499     OUStringBuffer aFontName;           // current font name
500     OUStringBuffer aFontStyle;          // current font style
501     sal_Int32 nFontHeight = 0;          // current font height
502 
503     /** State of the parser. */
504     enum
505     {
506         STATE_TEXT,         /// Literal text data.
507         STATE_TOKEN,        /// Control token following a '&' character.
508         STATE_FONTNAME,     /// Font name ('&' is followed by '"', reads until next '"' or ',').
509         STATE_FONTSTYLE,    /// Font style name (font part after ',', reads until next '"').
510         STATE_FONTHEIGHT    /// Font height ('&' is followed by num. digits, reads until non-digit).
511     }
512     eState = STATE_TEXT;
513 
514     const sal_Unicode* pcChar = rData.getStr();
515     const sal_Unicode* pcEnd = pcChar + rData.getLength();
516     for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar )
517     {
518         sal_Unicode cChar = *pcChar;
519         switch( eState )
520         {
521             case STATE_TEXT:
522             {
523                 switch( cChar )
524                 {
525                     case '&':           // new token
526                         appendText();
527                         eState = STATE_TOKEN;
528                     break;
529                     case '\n':          // line break
530                         appendText();
531                         appendLineBreak();
532                     break;
533                     default:
534                         maBuffer.append( cChar );
535                 }
536             }
537             break;
538 
539             case STATE_TOKEN:
540             {
541                 // default: back to text mode, may be changed in specific cases
542                 eState = STATE_TEXT;
543                 // ignore case of token codes
544                 if( ('a' <= cChar) && (cChar <= 'z') )
545                     cChar = (cChar - 'a') + 'A';
546                 switch( cChar )
547                 {
548                     case '&':   maBuffer.append( cChar );   break;  // the '&' character
549 
550                     case 'L':   setNewPortion( HF_LEFT );   break;  // left portion
551                     case 'C':   setNewPortion( HF_CENTER ); break;  // center portion
552                     case 'R':   setNewPortion( HF_RIGHT );  break;  // right portion
553 
554                     case 'P':   // page number
555                         appendField( createField( gaPageNumberService ) );
556                     break;
557                     case 'N':   // total page count
558                         appendField( createField( gaPageCountService ) );
559                     break;
560                     case 'A':   // current sheet name
561                         appendField( createField( gaSheetNameService ) );
562                     break;
563 
564                     case 'F':   // file name
565                     {
566                         Reference<text::XTextContent> xContent = createField( gaFileNameService );
567                         PropertySet aPropSet( xContent );
568                         aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::NAME_AND_EXT );
569                         appendField( xContent );
570                     }
571                     break;
572                     case 'Z':   // file path (without file name), OOXML, BIFF12, and BIFF8 only
573                     {
574                         Reference<text::XTextContent> xContent = createField( gaFileNameService );
575                         PropertySet aPropSet( xContent );
576                         // FilenameDisplayFormat::PATH not supported by Calc
577                         aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::FULL );
578                         appendField( xContent );
579                         /*  path only is not supported -- if we find a '&Z&F'
580                             combination for path/name, skip the '&F' part */
581                         if( (pcChar + 2 < pcEnd) && (pcChar[ 1 ] == '&') && ((pcChar[ 2 ] == 'f') || (pcChar[ 2 ] == 'F')) )
582                             pcChar += 2;
583                     }
584                     break;
585                     case 'D':   // date
586                     {
587                         Reference<text::XTextContent> xContent = createField( gaDateTimeService );
588                         PropertySet aPropSet( xContent );
589                         aPropSet.setProperty( PROP_IsDate, true );
590                         appendField( xContent );
591                     }
592                     break;
593                     case 'T':   // time
594                     {
595                         Reference<text::XTextContent> xContent = createField( gaDateTimeService );
596                         PropertySet aPropSet( xContent );
597                         aPropSet.setProperty( PROP_IsDate, false );
598                         appendField( xContent );
599                     }
600                     break;
601 
602                     case 'B':   // bold
603                         setAttributes();
604                         maFontModel.mbBold = !maFontModel.mbBold;
605                     break;
606                     case 'I':   // italic
607                         setAttributes();
608                         maFontModel.mbItalic = !maFontModel.mbItalic;
609                     break;
610                     case 'U':   // underline
611                         setAttributes();
612                         maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_single) ? XML_none : XML_single;
613                     break;
614                     case 'E':   // double underline
615                         setAttributes();
616                         maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_double) ? XML_none : XML_double;
617                     break;
618                     case 'S':   // strikeout
619                         setAttributes();
620                         maFontModel.mbStrikeout = !maFontModel.mbStrikeout;
621                     break;
622                     case 'X':   // superscript
623                         setAttributes();
624                         maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_superscript) ? XML_baseline : XML_superscript;
625                     break;
626                     case 'Y':   // subscript
627                         setAttributes();
628                         maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_subscript) ? XML_baseline : XML_subscript;
629                     break;
630                     case 'O':   // outlined
631                         setAttributes();
632                         maFontModel.mbOutline = !maFontModel.mbOutline;
633                     break;
634                     case 'H':   // shadow
635                         setAttributes();
636                         maFontModel.mbShadow = !maFontModel.mbShadow;
637                     break;
638 
639                     case 'K':   // text color (not in BIFF)
640                         if( pcChar + 6 < pcEnd )
641                         {
642                             setAttributes();
643                             // eat the following 6 characters
644                             convertFontColor( OUString( pcChar + 1, 6 ) );
645                             pcChar += 6;
646                         }
647                     break;
648 
649                     case '\"':  // font name
650                         aFontName.setLength( 0 );
651                         aFontStyle.setLength( 0 );
652                         eState = STATE_FONTNAME;
653                     break;
654                     default:
655                         if( ('0' <= cChar) && (cChar <= '9') )    // font size
656                         {
657                             nFontHeight = cChar - '0';
658                             eState = STATE_FONTHEIGHT;
659                         }
660                 }
661             }
662             break;
663 
664             case STATE_FONTNAME:
665             {
666                 switch( cChar )
667                 {
668                     case '\"':
669                         setAttributes();
670                         convertFontName( aFontName.makeStringAndClear() );
671                         eState = STATE_TEXT;
672                     break;
673                     case ',':
674                         eState = STATE_FONTSTYLE;
675                     break;
676                     default:
677                         aFontName.append( cChar );
678                 }
679             }
680             break;
681 
682             case STATE_FONTSTYLE:
683             {
684                 switch( cChar )
685                 {
686                     case '\"':
687                         setAttributes();
688                         convertFontName( aFontName.makeStringAndClear() );
689                         convertFontStyle( aFontStyle.makeStringAndClear() );
690                         eState = STATE_TEXT;
691                     break;
692                     default:
693                         aFontStyle.append( cChar );
694                 }
695             }
696             break;
697 
698             case STATE_FONTHEIGHT:
699             {
700                 if( ('0' <= cChar) && (cChar <= '9') )
701                 {
702                     if( nFontHeight >= 0 )
703                     {
704                         nFontHeight *= 10;
705                         nFontHeight += (cChar - '0');
706                         if( nFontHeight > 1000 )
707                             nFontHeight = -1;
708                     }
709                 }
710                 else
711                 {
712                     if( nFontHeight > 0 )
713                     {
714                         setAttributes();
715                         maFontModel.mfHeight = nFontHeight;
716                     }
717                     --pcChar;
718                     eState = STATE_TEXT;
719                 }
720             }
721             break;
722         }
723     }
724 
725     // finalize
726     finalizePortion();
727     maPortions[ HF_LEFT   ].mfTotalHeight += getCurrHeight( HF_LEFT );
728     maPortions[ HF_CENTER ].mfTotalHeight += getCurrHeight( HF_CENTER );
729     maPortions[ HF_RIGHT  ].mfTotalHeight += getCurrHeight( HF_RIGHT );
730 
731     return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight,
732         ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) );
733 }
734 
735 // private --------------------------------------------------------------------
736 
getCurrHeight(HFPortionId ePortion) const737 double HeaderFooterParser::getCurrHeight( HFPortionId ePortion ) const
738 {
739     double fMaxHt = maPortions[ ePortion ].mfCurrHeight;
740     return (fMaxHt == 0.0) ? maFontModel.mfHeight : fMaxHt;
741 }
742 
updateCurrHeight(HFPortionId ePortion)743 void HeaderFooterParser::updateCurrHeight( HFPortionId ePortion )
744 {
745     double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight;
746     rfMaxHt = ::std::max( rfMaxHt, maFontModel.mfHeight );
747 }
748 
updateCurrHeight()749 void HeaderFooterParser::updateCurrHeight()
750 {
751     updateCurrHeight( meCurrPortion );
752 }
753 
setAttributes()754 void HeaderFooterParser::setAttributes()
755 {
756     Reference<text::XTextRange> xRange = getStartPos();
757     getEndPos()->gotoRange( xRange, false );
758     getEndPos()->gotoEnd( true );
759     if( !getEndPos()->isCollapsed() )
760     {
761         Font aFont( *this, maFontModel );
762         aFont.finalizeImport();
763         PropertySet aPropSet( getEndPos() );
764         aFont.writeToPropertySet( aPropSet );
765         getStartPos()->gotoEnd( false );
766         getEndPos()->gotoEnd( false );
767     }
768 }
769 
appendText()770 void HeaderFooterParser::appendText()
771 {
772     if( !maBuffer.isEmpty() )
773     {
774         getEndPos()->gotoEnd( false );
775         getEndPos()->setString( maBuffer.makeStringAndClear() );
776         updateCurrHeight();
777     }
778 }
779 
appendLineBreak()780 void HeaderFooterParser::appendLineBreak()
781 {
782     getEndPos()->gotoEnd( false );
783     getEndPos()->setString( OUString( '\n' ) );
784     getPortion().mfTotalHeight += getCurrHeight( meCurrPortion ); // add the current line height.
785     getPortion().mfCurrHeight = 0;
786 }
787 
createField(const OUString & rServiceName) const788 Reference<text::XTextContent> HeaderFooterParser::createField( const OUString& rServiceName ) const
789 {
790     Reference<text::XTextContent> xContent;
791     try
792     {
793         xContent.set( getBaseFilter().getModelFactory()->createInstance( rServiceName ), UNO_QUERY_THROW );
794     }
795     catch( Exception& )
796     {
797         OSL_FAIL( OStringBuffer( "HeaderFooterParser::createField - error while creating text field \"" ).
798             append( OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US ) ).
799             append( '"' ).getStr() );
800     }
801     return xContent;
802 }
803 
appendField(const Reference<text::XTextContent> & rxContent)804 void HeaderFooterParser::appendField( const Reference<text::XTextContent>& rxContent )
805 {
806     getEndPos()->gotoEnd( false );
807     try
808     {
809         Reference<text::XTextRange> xRange( getEndPos(), UNO_QUERY_THROW );
810         getPortion().mxText->insertTextContent( xRange, rxContent, false );
811         updateCurrHeight();
812     }
813     catch( Exception& )
814     {
815     }
816 }
817 
convertFontName(const OUString & rName)818 void HeaderFooterParser::convertFontName( const OUString& rName )
819 {
820     if( !rName.isEmpty() )
821     {
822         // single dash is document default font
823         if( (rName.getLength() == 1) && (rName[ 0 ] == '-') )
824             maFontModel.maName = getStyles().getDefaultFontModel().maName;
825         else
826             maFontModel.maName = rName;
827     }
828 }
829 
convertFontStyle(const OUString & rStyle)830 void HeaderFooterParser::convertFontStyle( const OUString& rStyle )
831 {
832     maFontModel.mbBold = maFontModel.mbItalic = false;
833     if (rStyle.isEmpty())
834         return;
835     for( sal_Int32 nPos{ 0 }; nPos>=0; )
836     {
837         OString aToken = OUStringToOString( rStyle.getToken( 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
838         if( !aToken.isEmpty() )
839         {
840             if( maBoldNames.count( aToken ) > 0 )
841                 maFontModel.mbBold = true;
842             else if( maItalicNames.count( aToken ) > 0 )
843                 maFontModel.mbItalic = true;
844         }
845     }
846 }
847 
convertFontColor(const OUString & rColor)848 void HeaderFooterParser::convertFontColor( const OUString& rColor )
849 {
850     OSL_ENSURE( rColor.getLength() == 6, "HeaderFooterParser::convertFontColor - invalid font color code" );
851     if( (rColor[ 2 ] == '+') || (rColor[ 2 ] == '-') )
852         // theme color: TTSNNN (TT = decimal theme index, S = +/-, NNN = decimal tint/shade in percent)
853         maFontModel.maColor.setTheme(
854             rColor.copy( 0, 2 ).toInt32(),
855             static_cast< double >( rColor.copy( 2 ).toInt32() ) / 100.0 );
856     else
857         // RGB color: RRGGBB
858         maFontModel.maColor.setRgb( ::Color(ColorTransparency, rColor.toUInt32( 16 )) );
859 }
860 
finalizePortion()861 void HeaderFooterParser::finalizePortion()
862 {
863     appendText();
864     setAttributes();
865 }
866 
setNewPortion(HFPortionId ePortion)867 void HeaderFooterParser::setNewPortion( HFPortionId ePortion )
868 {
869     if( ePortion != meCurrPortion )
870     {
871         finalizePortion();
872         meCurrPortion = ePortion;
873         maFontModel = getStyles().getDefaultFontModel();
874     }
875 }
876 
HFHelperData(sal_Int32 nLeftPropId,sal_Int32 nRightPropId,sal_Int32 nFirstPropId)877 PageSettingsConverter::HFHelperData::HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId, sal_Int32 nFirstPropId ) :
878     mnLeftPropId( nLeftPropId ),
879     mnRightPropId( nRightPropId ),
880     mnFirstPropId( nFirstPropId ),
881     mnHeight( 0 ),
882     mnBodyDist( 0 ),
883     mbHasContent( false ),
884     mbShareOddEven( false ),
885     mbShareFirst( false ),
886     mbDynamicHeight( false )
887 {
888 }
889 
PageSettingsConverter(const WorkbookHelper & rHelper)890 PageSettingsConverter::PageSettingsConverter( const WorkbookHelper& rHelper ) :
891     WorkbookHelper( rHelper ),
892     mxHFParser( new HeaderFooterParser( rHelper ) ),
893     maHeaderData( PROP_LeftPageHeaderContent, PROP_RightPageHeaderContent, PROP_FirstPageHeaderContent ),
894     maFooterData( PROP_LeftPageFooterContent, PROP_RightPageFooterContent, PROP_FirstPageFooterContent )
895 {
896 }
897 
~PageSettingsConverter()898 PageSettingsConverter::~PageSettingsConverter()
899 {
900 }
901 
writePageSettingsProperties(PropertySet & rPropSet,const PageSettingsModel & rModel,WorksheetType eSheetType)902 void PageSettingsConverter::writePageSettingsProperties(
903         PropertySet& rPropSet, const PageSettingsModel& rModel, WorksheetType eSheetType )
904 {
905     // special handling for chart sheets
906     bool bChartSheet = eSheetType == WorksheetType::Chart;
907 
908     // printout scaling
909     if( bChartSheet )
910     {
911         // always fit chart sheet to 1 page
912         rPropSet.setProperty< sal_Int16 >( PROP_ScaleToPages, 1 );
913     }
914     else if( rModel.mbFitToPages )
915     {
916         // fit to number of pages
917         rPropSet.setProperty( PROP_ScaleToPagesX, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToWidth, 0, 1000 ) );
918         rPropSet.setProperty( PROP_ScaleToPagesY, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToHeight, 0, 1000 ) );
919     }
920     else
921     {
922         // scale may be 0 which indicates uninitialized
923         sal_Int16 nScale = (rModel.mnScale > 0) ? getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnScale, 10, 400 ) : 100;
924         rPropSet.setProperty( PROP_PageScale, nScale );
925     }
926 
927     // paper orientation
928     bool bLandscape = rModel.mnOrientation == XML_landscape;
929     // default orientation for current sheet type (chart sheets default to landscape)
930     if( bChartSheet && ( !rModel.mbValidSettings || (rModel.mnOrientation == XML_default) ) )
931         bLandscape = true;
932 
933     // paper size
934     if( !rModel.mbValidSettings )
935     {
936         awt::Size aSize;
937         bool bValid = false;
938 
939         if( 0 < rModel.mnPaperSize )
940         {
941             const msfilter::util::ApiPaperSize& rPaperSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex(  rModel.mnPaperSize );
942             aSize = awt::Size( rPaperSize.mnWidth, rPaperSize.mnHeight );
943             bValid = ( rPaperSize.mnWidth != 0 && rPaperSize.mnHeight != 0 );
944         }
945         if( rModel.mnPaperWidth > 0 && rModel.mnPaperHeight > 0 )
946         {
947             aSize = awt::Size( rModel.mnPaperWidth, rModel.mnPaperHeight );
948             bValid = true;
949         }
950 
951         if( bValid )
952         {
953             if( bLandscape )
954                 ::std::swap( aSize.Width, aSize.Height );
955             rPropSet.setProperty( PROP_Size, aSize );
956         }
957     }
958 
959     // header/footer
960     convertHeaderFooterData( rPropSet, maHeaderData, rModel.maOddHeader, rModel.maEvenHeader, rModel.maFirstHeader, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfTopMargin,    rModel.mfHeaderMargin );
961     convertHeaderFooterData( rPropSet, maFooterData, rModel.maOddFooter, rModel.maEvenFooter, rModel.maFirstFooter, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfBottomMargin, rModel.mfFooterMargin );
962 
963     // write all properties to property set
964     const UnitConverter& rUnitConv = getUnitConverter();
965     PropertyMap aPropMap;
966     aPropMap.setProperty( PROP_IsLandscape, bLandscape);
967     aPropMap.setProperty( PROP_FirstPageNumber, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mbUseFirstPage ? rModel.mnFirstPage : 0, 0, 9999 ));
968     aPropMap.setProperty( PROP_PrintDownFirst, (rModel.mnPageOrder == XML_downThenOver));
969     aPropMap.setProperty( PROP_PrintAnnotations, (rModel.mnCellComments == XML_asDisplayed));
970     aPropMap.setProperty( PROP_CenterHorizontally, rModel.mbHorCenter);
971     aPropMap.setProperty( PROP_CenterVertically, rModel.mbVerCenter);
972     aPropMap.setProperty( PROP_PrintGrid, (!bChartSheet && rModel.mbPrintGrid));     // no gridlines in chart sheets
973     aPropMap.setProperty( PROP_PrintHeaders, (!bChartSheet && rModel.mbPrintHeadings)); // no column/row headings in chart sheets
974     aPropMap.setProperty( PROP_LeftMargin, rUnitConv.scaleToMm100( rModel.mfLeftMargin, Unit::Inch ));
975     aPropMap.setProperty( PROP_RightMargin, rUnitConv.scaleToMm100( rModel.mfRightMargin, Unit::Inch ));
976     // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled
977     aPropMap.setProperty( PROP_TopMargin, rUnitConv.scaleToMm100( maHeaderData.mbHasContent ? rModel.mfHeaderMargin : rModel.mfTopMargin, Unit::Inch ));
978     // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled
979     aPropMap.setProperty( PROP_BottomMargin, rUnitConv.scaleToMm100( maFooterData.mbHasContent ? rModel.mfFooterMargin : rModel.mfBottomMargin, Unit::Inch ));
980     aPropMap.setProperty( PROP_HeaderIsOn, maHeaderData.mbHasContent);
981     aPropMap.setProperty( PROP_HeaderIsShared, maHeaderData.mbShareOddEven);
982     aPropMap.setProperty( PROP_FirstPageHeaderIsShared, maHeaderData.mbShareFirst);
983     aPropMap.setProperty( PROP_HeaderIsDynamicHeight, maHeaderData.mbDynamicHeight);
984     aPropMap.setProperty( PROP_HeaderHeight, maHeaderData.mnHeight);
985     aPropMap.setProperty( PROP_HeaderBodyDistance, maHeaderData.mnBodyDist);
986     aPropMap.setProperty( PROP_FooterIsOn, maFooterData.mbHasContent);
987     aPropMap.setProperty( PROP_FooterIsShared, maFooterData.mbShareOddEven);
988     aPropMap.setProperty( PROP_FirstPageFooterIsShared, maFooterData.mbShareFirst);
989     aPropMap.setProperty( PROP_FooterIsDynamicHeight, maFooterData.mbDynamicHeight);
990     aPropMap.setProperty( PROP_FooterHeight, maFooterData.mnHeight);
991     aPropMap.setProperty( PROP_FooterBodyDistance, maFooterData.mnBodyDist);
992     // background image
993     if (rModel.mxGraphic.is())
994     {
995         aPropMap.setProperty(PROP_BackGraphic, rModel.mxGraphic);
996         aPropMap.setProperty(PROP_BackGraphicLocation, css::style::GraphicLocation_TILED);
997     }
998 
999     rPropSet.setProperties( aPropMap );
1000 }
1001 
convertHeaderFooterData(PropertySet & rPropSet,HFHelperData & orHFData,const OUString & rOddContent,const OUString & rEvenContent,const OUString & rFirstContent,bool bUseEvenContent,bool bUseFirstContent,double fPageMargin,double fContentMargin)1002 void PageSettingsConverter::convertHeaderFooterData(
1003         PropertySet& rPropSet, HFHelperData& orHFData,
1004         const OUString& rOddContent, const OUString& rEvenContent, const OUString& rFirstContent,
1005         bool bUseEvenContent, bool bUseFirstContent,
1006         double fPageMargin, double fContentMargin )
1007 {
1008     bool bHasOddContent  = !rOddContent.isEmpty();
1009     bool bHasEvenContent = bUseEvenContent && !rEvenContent.isEmpty();
1010     bool bHasFirstContent = bUseFirstContent && !rFirstContent.isEmpty();
1011 
1012     sal_Int32 nOddHeight   = bHasOddContent   ? writeHeaderFooter( rPropSet, orHFData.mnRightPropId, rOddContent   ) : 0;
1013     sal_Int32 nEvenHeight  = bHasEvenContent  ? writeHeaderFooter( rPropSet, orHFData.mnLeftPropId,  rEvenContent  ) : 0;
1014     sal_Int32 nFirstHeight = bHasFirstContent ? writeHeaderFooter( rPropSet, orHFData.mnFirstPropId, rFirstContent ) : 0;
1015 
1016     orHFData.mnHeight = 750;
1017     orHFData.mnBodyDist = 250;
1018     orHFData.mbHasContent = bHasOddContent || bHasEvenContent || bHasFirstContent;
1019     orHFData.mbShareOddEven = !bUseEvenContent;
1020     orHFData.mbShareFirst = !bUseFirstContent;
1021     orHFData.mbDynamicHeight = true;
1022 
1023     if( !orHFData.mbHasContent )
1024         return;
1025 
1026     // use maximum height of odd/even/first header/footer
1027     orHFData.mnHeight = ::std::max( ::std::max( nOddHeight, nEvenHeight ), nFirstHeight );
1028     /*  Calc contains distance between bottom of header and top of page
1029         body in "HeaderBodyDistance" property, and distance between bottom
1030         of page body and top of footer in "FooterBodyDistance" property */
1031     orHFData.mnBodyDist = getUnitConverter().scaleToMm100( fPageMargin - fContentMargin, Unit::Inch ) - orHFData.mnHeight;
1032     /*  #i23296# Distance less than 0 means, header or footer overlays page
1033         body. As this is not possible in Calc, set fixed header or footer
1034         height (crop header/footer) to get correct top position of page body. */
1035     orHFData.mbDynamicHeight = orHFData.mnBodyDist >= 0;
1036     /*  "HeaderHeight" property is in fact distance from top of header to
1037         top of page body (including "HeaderBodyDistance").
1038         "FooterHeight" property is in fact distance from bottom of page
1039         body to bottom of footer (including "FooterBodyDistance"). */
1040     orHFData.mnHeight += orHFData.mnBodyDist;
1041     // negative body distance not allowed
1042     orHFData.mnBodyDist = ::std::max< sal_Int32 >( orHFData.mnBodyDist, 0 );
1043 }
1044 
writeHeaderFooter(PropertySet & rPropSet,sal_Int32 nPropId,const OUString & rContent)1045 sal_Int32 PageSettingsConverter::writeHeaderFooter(
1046         PropertySet& rPropSet, sal_Int32 nPropId, const OUString& rContent )
1047 {
1048     OSL_ENSURE( !rContent.isEmpty(), "PageSettingsConverter::writeHeaderFooter - empty h/f string found" );
1049     sal_Int32 nHeight = 0;
1050     if( !rContent.isEmpty() )
1051     {
1052         Reference<sheet::XHeaderFooterContent> xHFContent(rPropSet.getAnyProperty(nPropId), UNO_QUERY);
1053         if( xHFContent.is() )
1054         {
1055             double fTotalHeight = mxHFParser->parse( xHFContent, rContent );
1056             rPropSet.setProperty( nPropId, xHFContent );
1057             nHeight = getUnitConverter().scaleToMm100( fTotalHeight, Unit::Point );
1058         }
1059     }
1060     return nHeight;
1061 }
1062 
1063 } // namespace oox
1064 
1065 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1066