Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / oox / pagesettings.cxx
blobf52d4f638d7409d597f5bd2c64effcb8a99a82d4
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 .
20 #include <pagesettings.hxx>
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/style/GraphicLocation.hpp>
28 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
29 #include <com/sun/star/text/XText.hpp>
30 #include <com/sun/star/text/XTextCursor.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <osl/diagnose.h>
33 #include <rtl/strbuf.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <sax/tools/converter.hxx>
36 #include <oox/helper/attributelist.hxx>
37 #include <oox/helper/binaryinputstream.hxx>
38 #include <oox/helper/graphichelper.hxx>
39 #include <oox/helper/propertymap.hxx>
40 #include <oox/helper/propertyset.hxx>
41 #include <oox/token/namespaces.hxx>
42 #include <oox/token/properties.hxx>
43 #include <oox/token/tokens.hxx>
44 #include <oox/core/filterbase.hxx>
45 #include <oox/core/relations.hxx>
46 #include <stylesbuffer.hxx>
47 #include <unitconverter.hxx>
48 #include <document.hxx>
49 #include <biffhelper.hxx>
50 #include <filter/msfilter/util.hxx>
52 namespace oox {
53 namespace xls {
55 using namespace ::com::sun::star;
56 using namespace ::com::sun::star::lang;
57 using namespace ::com::sun::star::uno;
59 using ::oox::core::Relations;
61 namespace {
63 const double OOX_MARGIN_DEFAULT_LR = 0.748; /// Left/right default margin in inches.
64 const double OOX_MARGIN_DEFAULT_TB = 0.984; /// Top/bottom default margin in inches.
65 const double OOX_MARGIN_DEFAULT_HF = 0.512; /// Header/footer default margin in inches.
67 const sal_uInt16 BIFF12_PRINTOPT_HORCENTER = 0x0001;
68 const sal_uInt16 BIFF12_PRINTOPT_VERCENTER = 0x0002;
69 const sal_uInt16 BIFF12_PRINTOPT_PRINTHEADING = 0x0004;
70 const sal_uInt16 BIFF12_PRINTOPT_PRINTGRID = 0x0008;
72 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFEVEN = 0x0001;
73 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFFIRST = 0x0002;
75 const sal_uInt16 BIFF12_PAGESETUP_INROWS = 0x0001;
76 const sal_uInt16 BIFF12_PAGESETUP_LANDSCAPE = 0x0002;
77 const sal_uInt16 BIFF12_PAGESETUP_INVALID = 0x0004;
78 const sal_uInt16 BIFF12_PAGESETUP_BLACKWHITE = 0x0008;
79 const sal_uInt16 BIFF12_PAGESETUP_DRAFTQUALITY = 0x0010;
80 const sal_uInt16 BIFF12_PAGESETUP_PRINTNOTES = 0x0020;
81 const sal_uInt16 BIFF12_PAGESETUP_DEFAULTORIENT = 0x0040;
82 const sal_uInt16 BIFF12_PAGESETUP_USEFIRSTPAGE = 0x0080;
83 const sal_uInt16 BIFF12_PAGESETUP_NOTES_END = 0x0100; // different to BIFF flag
85 const sal_uInt16 BIFF12_CHARTPAGESETUP_LANDSCAPE = 0x0001;
86 const sal_uInt16 BIFF12_CHARTPAGESETUP_INVALID = 0x0002;
87 const sal_uInt16 BIFF12_CHARTPAGESETUP_BLACKWHITE = 0x0004;
88 const sal_uInt16 BIFF12_CHARTPAGESETUP_DEFAULTORIENT= 0x0008;
89 const sal_uInt16 BIFF12_CHARTPAGESETUP_USEFIRSTPAGE = 0x0010;
90 const sal_uInt16 BIFF12_CHARTPAGESETUP_DRAFTQUALITY = 0x0020;
92 } // namespace
94 PageSettingsModel::PageSettingsModel() :
95 mfLeftMargin( OOX_MARGIN_DEFAULT_LR ),
96 mfRightMargin( OOX_MARGIN_DEFAULT_LR ),
97 mfTopMargin( OOX_MARGIN_DEFAULT_TB ),
98 mfBottomMargin( OOX_MARGIN_DEFAULT_TB ),
99 mfHeaderMargin( OOX_MARGIN_DEFAULT_HF ),
100 mfFooterMargin( OOX_MARGIN_DEFAULT_HF ),
101 mnPaperSize( 1 ),
102 mnPaperWidth( 0 ),
103 mnPaperHeight( 0 ),
104 mnCopies( 1 ),
105 mnScale( 100 ),
106 mnFirstPage( 1 ),
107 mnFitToWidth( 1 ),
108 mnFitToHeight( 1 ),
109 mnHorPrintRes( 600 ),
110 mnVerPrintRes( 600 ),
111 mnOrientation( XML_default ),
112 mnPageOrder( XML_downThenOver ),
113 mnCellComments( XML_none ),
114 mnPrintErrors( XML_displayed ),
115 mbUseEvenHF( false ),
116 mbUseFirstHF( false ),
117 mbValidSettings( true ),
118 mbUseFirstPage( false ),
119 mbBlackWhite( false ),
120 mbDraftQuality( false ),
121 mbFitToPages( false ),
122 mbHorCenter( false ),
123 mbVerCenter( false ),
124 mbPrintGrid( false ),
125 mbPrintHeadings( false )
129 void PageSettingsModel::setBiffPrintErrors( sal_uInt8 nPrintErrors )
131 static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA };
132 mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none );
135 PageSettings::PageSettings( const WorksheetHelper& rHelper ) :
136 WorksheetHelper( rHelper )
140 void PageSettings::importPrintOptions( const AttributeList& rAttribs )
142 maModel.mbHorCenter = rAttribs.getBool( XML_horizontalCentered, false );
143 maModel.mbVerCenter = rAttribs.getBool( XML_verticalCentered, false );
144 maModel.mbPrintGrid = rAttribs.getBool( XML_gridLines, false );
145 maModel.mbPrintHeadings = rAttribs.getBool( XML_headings, false );
148 void PageSettings::importPageMargins( const AttributeList& rAttribs )
150 maModel.mfLeftMargin = rAttribs.getDouble( XML_left, OOX_MARGIN_DEFAULT_LR );
151 maModel.mfRightMargin = rAttribs.getDouble( XML_right, OOX_MARGIN_DEFAULT_LR );
152 maModel.mfTopMargin = rAttribs.getDouble( XML_top, OOX_MARGIN_DEFAULT_TB );
153 maModel.mfBottomMargin = rAttribs.getDouble( XML_bottom, OOX_MARGIN_DEFAULT_TB );
154 maModel.mfHeaderMargin = rAttribs.getDouble( XML_header, OOX_MARGIN_DEFAULT_HF );
155 maModel.mfFooterMargin = rAttribs.getDouble( XML_footer, OOX_MARGIN_DEFAULT_HF );
158 void PageSettings::importPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
160 OUString aStr;
161 maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
162 maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
163 aStr = rAttribs.getString ( XML_paperWidth, OUString() );
164 ::sax::Converter::convertMeasure(
165 maModel.mnPaperWidth, aStr);
166 aStr = rAttribs.getString ( XML_paperHeight, OUString() );
167 ::sax::Converter::convertMeasure(
168 maModel.mnPaperHeight, aStr );
169 maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
170 maModel.mnScale = rAttribs.getInteger( XML_scale, 100 );
171 maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
172 maModel.mnFitToWidth = rAttribs.getInteger( XML_fitToWidth, 1 );
173 maModel.mnFitToHeight = rAttribs.getInteger( XML_fitToHeight, 1 );
174 maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
175 maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
176 maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
177 maModel.mnPageOrder = rAttribs.getToken( XML_pageOrder, XML_downThenOver );
178 maModel.mnCellComments = rAttribs.getToken( XML_cellComments, XML_none );
179 maModel.mnPrintErrors = rAttribs.getToken( XML_errors, XML_displayed );
180 maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
181 maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
182 maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
183 maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
186 void PageSettings::importChartPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
188 OUString aStr;
189 maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
190 maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
191 aStr = rAttribs.getString ( XML_paperWidth, OUString() );
192 ::sax::Converter::convertMeasure(
193 maModel.mnPaperWidth, aStr );
194 aStr = rAttribs.getString ( XML_paperHeight, OUString() );
195 ::sax::Converter::convertMeasure(
196 maModel.mnPaperHeight, aStr );
197 maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
198 maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
199 maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
200 maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
201 maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
202 maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
203 maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
204 maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
205 maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
208 void PageSettings::importHeaderFooter( const AttributeList& rAttribs )
210 maModel.mbUseEvenHF = rAttribs.getBool( XML_differentOddEven, false );
211 maModel.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false );
214 void PageSettings::importHeaderFooterCharacters( const OUString& rChars, sal_Int32 nElement )
216 switch( nElement )
218 case XLS_TOKEN( oddHeader ): maModel.maOddHeader += rChars; break;
219 case XLS_TOKEN( oddFooter ): maModel.maOddFooter += rChars; break;
220 case XLS_TOKEN( evenHeader ): maModel.maEvenHeader += rChars; break;
221 case XLS_TOKEN( evenFooter ): maModel.maEvenFooter += rChars; break;
222 case XLS_TOKEN( firstHeader ): maModel.maFirstHeader += rChars; break;
223 case XLS_TOKEN( firstFooter ): maModel.maFirstFooter += rChars; break;
227 void PageSettings::importPicture( const Relations& rRelations, const AttributeList& rAttribs )
229 importPictureData( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
232 void PageSettings::importPageMargins( SequenceInputStream& rStrm )
234 maModel.mfLeftMargin = rStrm.readDouble();
235 maModel.mfRightMargin = rStrm.readDouble();
236 maModel.mfTopMargin = rStrm.readDouble();
237 maModel.mfBottomMargin = rStrm.readDouble();
238 maModel.mfHeaderMargin = rStrm.readDouble();
239 maModel.mfFooterMargin = rStrm.readDouble();
242 void PageSettings::importPrintOptions( SequenceInputStream& rStrm )
244 sal_uInt16 nFlags;
245 nFlags = rStrm.readuInt16();
246 maModel.mbHorCenter = getFlag( nFlags, BIFF12_PRINTOPT_HORCENTER );
247 maModel.mbVerCenter = getFlag( nFlags, BIFF12_PRINTOPT_VERCENTER );
248 maModel.mbPrintGrid = getFlag( nFlags, BIFF12_PRINTOPT_PRINTGRID );
249 maModel.mbPrintHeadings = getFlag( nFlags, BIFF12_PRINTOPT_PRINTHEADING );
252 void PageSettings::importPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
254 OUString aRelId;
255 sal_uInt16 nFlags;
256 maModel.mnPaperSize = rStrm.readInt32();
257 maModel.mnScale = rStrm.readInt32();
258 maModel.mnHorPrintRes = rStrm.readInt32();
259 maModel.mnVerPrintRes = rStrm.readInt32();
260 maModel.mnCopies = rStrm.readInt32();
261 maModel.mnFirstPage = rStrm.readInt32();
262 maModel.mnFitToWidth = rStrm.readInt32();
263 maModel.mnFitToHeight = rStrm.readInt32();
264 nFlags = rStrm.readuInt16();
265 rStrm >> aRelId;
266 maModel.setBiffPrintErrors( extractValue< sal_uInt8 >( nFlags, 9, 2 ) );
267 maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
268 maModel.mnOrientation = getFlagValue( nFlags, BIFF12_PAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_PAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
269 maModel.mnPageOrder = getFlagValue( nFlags, BIFF12_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver );
270 maModel.mnCellComments = getFlagValue( nFlags, BIFF12_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, BIFF12_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none );
271 maModel.mbValidSettings = !getFlag( nFlags, BIFF12_PAGESETUP_INVALID );
272 maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_PAGESETUP_USEFIRSTPAGE );
273 maModel.mbBlackWhite = getFlag( nFlags, BIFF12_PAGESETUP_BLACKWHITE );
274 maModel.mbDraftQuality = getFlag( nFlags, BIFF12_PAGESETUP_DRAFTQUALITY );
277 void PageSettings::importChartPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
279 OUString aRelId;
280 sal_uInt16 nFirstPage, nFlags;
281 maModel.mnPaperSize = rStrm.readInt32();
282 maModel.mnHorPrintRes = rStrm.readInt32();
283 maModel.mnVerPrintRes = rStrm.readInt32();
284 maModel.mnCopies = rStrm.readInt32();
285 nFirstPage = rStrm.readuInt16();
286 nFlags = rStrm.readuInt16();
287 rStrm >> aRelId;
288 maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
289 maModel.mnFirstPage = nFirstPage; // 16-bit in CHARTPAGESETUP
290 maModel.mnOrientation = getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
291 maModel.mbValidSettings = !getFlag( nFlags, BIFF12_CHARTPAGESETUP_INVALID );
292 maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_CHARTPAGESETUP_USEFIRSTPAGE );
293 maModel.mbBlackWhite = getFlag( nFlags, BIFF12_CHARTPAGESETUP_BLACKWHITE );
294 maModel.mbDraftQuality = getFlag( nFlags, BIFF12_CHARTPAGESETUP_DRAFTQUALITY );
297 void PageSettings::importHeaderFooter( SequenceInputStream& rStrm )
299 sal_uInt16 nFlags;
300 nFlags = rStrm.readuInt16();
301 rStrm >> maModel.maOddHeader >> maModel.maOddFooter
302 >> maModel.maEvenHeader >> maModel.maEvenFooter
303 >> maModel.maFirstHeader >> maModel.maFirstFooter;
304 maModel.mbUseEvenHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFEVEN );
305 maModel.mbUseFirstHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFFIRST );
308 void PageSettings::importPicture( const Relations& rRelations, SequenceInputStream& rStrm )
310 importPictureData( rRelations, BiffHelper::readString( rStrm ) );
313 void PageSettings::setFitToPagesMode( bool bFitToPages )
315 maModel.mbFitToPages = bFitToPages;
318 void PageSettings::finalizeImport()
320 OUStringBuffer aStyleNameBuffer( "PageStyle_" );
321 Reference<container::XNamed> xSheetName(getSheet(), UNO_QUERY);
322 if( xSheetName.is() )
323 aStyleNameBuffer.append( xSheetName->getName() );
324 else
325 aStyleNameBuffer.append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) );
326 OUString aStyleName = aStyleNameBuffer.makeStringAndClear();
328 Reference<style::XStyle> xStyle = createStyleObject(aStyleName, true);
329 PropertySet aStyleProps( xStyle );
330 getPageSettingsConverter().writePageSettingsProperties( aStyleProps, maModel, getSheetType() );
332 // Set page style name to the sheet.
333 SCTAB nTab = getSheetIndex();
334 getScDocument().SetPageStyle(nTab, aStyleName);
337 void PageSettings::importPictureData( const Relations& rRelations, const OUString& rRelId )
339 OUString aPicturePath = rRelations.getFragmentPathFromRelId(rRelId);
340 if (!aPicturePath.isEmpty())
342 maModel.mxGraphic = getBaseFilter().getGraphicHelper().importEmbeddedGraphic(aPicturePath);
346 enum HFPortionId
348 HF_LEFT,
349 HF_CENTER,
350 HF_RIGHT,
351 HF_COUNT
354 struct HFPortionInfo
356 Reference<text::XText> mxText; /// XText interface of this portion.
357 Reference<text::XTextCursor> mxStart; /// Start position of current text range for formatting.
358 Reference<text::XTextCursor> mxEnd; /// End position of current text range for formatting.
359 double mfTotalHeight; /// Sum of heights of previous lines in points.
360 double mfCurrHeight; /// Height of the current text line in points.
362 bool initialize( const Reference<text::XText>& rxText );
365 bool HFPortionInfo::initialize( const Reference<text::XText>& rxText )
367 mfTotalHeight = mfCurrHeight = 0.0;
368 mxText = rxText;
369 if( mxText.is() )
371 mxStart = mxText->createTextCursor();
372 mxEnd = mxText->createTextCursor();
374 bool bRet = mxText.is() && mxStart.is() && mxEnd.is();
375 OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" );
376 return bRet;
379 class HeaderFooterParser : public WorkbookHelper
381 public:
382 explicit HeaderFooterParser( const WorkbookHelper& rHelper );
384 /** Parses the passed string and creates the header/footer contents.
385 @returns The total height of the converted header or footer in points. */
386 double parse(
387 const Reference<sheet::XHeaderFooterContent>& rxContext,
388 const OUString& rData );
390 private:
391 /** Returns the current edit engine text object. */
392 HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; }
393 /** Returns the start cursor of the current text range. */
394 const Reference<text::XTextCursor>& getStartPos() { return getPortion().mxStart; }
395 /** Returns the end cursor of the current text range. */
396 const Reference<text::XTextCursor>& getEndPos() { return getPortion().mxEnd; }
398 /** Returns the current line height of the specified portion. */
399 double getCurrHeight( HFPortionId ePortion ) const;
401 /** Updates the current line height of the specified portion, using the current font size. */
402 void updateCurrHeight( HFPortionId ePortion );
403 /** Updates the current line height, using the current font size. */
404 void updateCurrHeight();
406 /** Sets the font attributes at the current selection. */
407 void setAttributes();
408 /** Appends and clears internal string buffer. */
409 void appendText();
410 /** Appends a line break and adjusts internal text height data. */
411 void appendLineBreak();
413 /** Creates a text field from the passed service name. */
414 Reference<text::XTextContent> createField( const OUString& rServiceName ) const;
415 /** Appends the passed text field. */
416 void appendField( const Reference<text::XTextContent>& rxContent );
418 /** Sets the passed font name if it is valid. */
419 void convertFontName( const OUString& rStyle );
420 /** Converts a font style given as string. */
421 void convertFontStyle( const OUString& rStyle );
422 /** Converts a font color given as string. */
423 void convertFontColor( const OUString& rColor );
425 /** Finalizes current portion: sets font attributes and updates text height data. */
426 void finalizePortion();
427 /** Changes current header/footer portion. */
428 void setNewPortion( HFPortionId ePortion );
430 private:
431 typedef ::std::vector< HFPortionInfo > HFPortionInfoVec;
433 const std::set< OString > maBoldNames; /// All names for bold font style in lowercase UTF-8.
434 const std::set< OString > maItalicNames; /// All names for italic font style in lowercase UTF-8.
435 HFPortionInfoVec maPortions;
436 HFPortionId meCurrPortion; /// Identifier of current H/F portion.
437 OUStringBuffer maBuffer; /// Text data to append to current text range.
438 FontModel maFontModel; /// Font attributes of current text range.
441 namespace {
443 // different names for bold font style (lowercase)
444 static const sal_Char* const sppcBoldNames[] =
446 "bold",
447 "fett", // German 'bold'
448 "demibold",
449 "halbfett", // German 'demibold'
450 "black",
451 "heavy"
454 // different names for italic font style (lowercase)
455 static const sal_Char* const sppcItalicNames[] =
457 "italic",
458 "kursiv", // German 'italic'
459 "oblique",
460 "schr\303\204g", // German 'oblique' with uppercase A umlaut
461 "schr\303\244g" // German 'oblique' with lowercase A umlaut
464 } // namespace
466 static const OUStringLiteral gaPageNumberService( "com.sun.star.text.TextField.PageNumber" );
467 static const OUStringLiteral gaPageCountService( "com.sun.star.text.TextField.PageCount" );
468 static const OUStringLiteral gaSheetNameService( "com.sun.star.text.TextField.SheetName" );
469 static const OUStringLiteral gaFileNameService( "com.sun.star.text.TextField.FileName" );
470 static const OUStringLiteral gaDateTimeService( "com.sun.star.text.TextField.DateTime" );
472 HeaderFooterParser::HeaderFooterParser( const WorkbookHelper& rHelper ) :
473 WorkbookHelper( rHelper ),
474 maBoldNames( sppcBoldNames, sppcBoldNames + SAL_N_ELEMENTS(sppcBoldNames) ),
475 maItalicNames( sppcItalicNames, sppcItalicNames + SAL_N_ELEMENTS(sppcItalicNames) ),
476 maPortions( static_cast< size_t >( HF_COUNT ) ),
477 meCurrPortion( HF_CENTER )
481 double HeaderFooterParser::parse( const Reference<sheet::XHeaderFooterContent>& rxContext, const OUString& rData )
483 if( !rxContext.is() || rData.isEmpty() ||
484 !maPortions[ HF_LEFT ].initialize( rxContext->getLeftText() ) ||
485 !maPortions[ HF_CENTER ].initialize( rxContext->getCenterText() ) ||
486 !maPortions[ HF_RIGHT ].initialize( rxContext->getRightText() ) )
487 return 0.0;
489 meCurrPortion = HF_CENTER;
490 maBuffer.setLength( 0 );
491 maFontModel = getStyles().getDefaultFontModel();
492 OUStringBuffer aFontName; // current font name
493 OUStringBuffer aFontStyle; // current font style
494 sal_Int32 nFontHeight = 0; // current font height
496 /** State of the parser. */
497 enum
499 STATE_TEXT, /// Literal text data.
500 STATE_TOKEN, /// Control token following a '&' character.
501 STATE_FONTNAME, /// Font name ('&' is followed by '"', reads until next '"' or ',').
502 STATE_FONTSTYLE, /// Font style name (font part after ',', reads until next '"').
503 STATE_FONTHEIGHT /// Font height ('&' is followed by num. digits, reads until non-digit).
505 eState = STATE_TEXT;
507 const sal_Unicode* pcChar = rData.getStr();
508 const sal_Unicode* pcEnd = pcChar + rData.getLength();
509 for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar )
511 sal_Unicode cChar = *pcChar;
512 switch( eState )
514 case STATE_TEXT:
516 switch( cChar )
518 case '&': // new token
519 appendText();
520 eState = STATE_TOKEN;
521 break;
522 case '\n': // line break
523 appendText();
524 appendLineBreak();
525 break;
526 default:
527 maBuffer.append( cChar );
530 break;
532 case STATE_TOKEN:
534 // default: back to text mode, may be changed in specific cases
535 eState = STATE_TEXT;
536 // ignore case of token codes
537 if( ('a' <= cChar) && (cChar <= 'z') )
538 cChar = (cChar - 'a') + 'A';
539 switch( cChar )
541 case '&': maBuffer.append( cChar ); break; // the '&' character
543 case 'L': setNewPortion( HF_LEFT ); break; // left portion
544 case 'C': setNewPortion( HF_CENTER ); break; // center portion
545 case 'R': setNewPortion( HF_RIGHT ); break; // right portion
547 case 'P': // page number
548 appendField( createField( gaPageNumberService ) );
549 break;
550 case 'N': // total page count
551 appendField( createField( gaPageCountService ) );
552 break;
553 case 'A': // current sheet name
554 appendField( createField( gaSheetNameService ) );
555 break;
557 case 'F': // file name
559 Reference<text::XTextContent> xContent = createField( gaFileNameService );
560 PropertySet aPropSet( xContent );
561 aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::NAME_AND_EXT );
562 appendField( xContent );
564 break;
565 case 'Z': // file path (without file name), OOXML, BIFF12, and BIFF8 only
567 Reference<text::XTextContent> xContent = createField( gaFileNameService );
568 PropertySet aPropSet( xContent );
569 // FilenameDisplayFormat::PATH not supported by Calc
570 aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::FULL );
571 appendField( xContent );
572 /* path only is not supported -- if we find a '&Z&F'
573 combination for path/name, skip the '&F' part */
574 if( (pcChar + 2 < pcEnd) && (pcChar[ 1 ] == '&') && ((pcChar[ 2 ] == 'f') || (pcChar[ 2 ] == 'F')) )
575 pcChar += 2;
577 break;
578 case 'D': // date
580 Reference<text::XTextContent> xContent = createField( gaDateTimeService );
581 PropertySet aPropSet( xContent );
582 aPropSet.setProperty( PROP_IsDate, true );
583 appendField( xContent );
585 break;
586 case 'T': // time
588 Reference<text::XTextContent> xContent = createField( gaDateTimeService );
589 PropertySet aPropSet( xContent );
590 aPropSet.setProperty( PROP_IsDate, false );
591 appendField( xContent );
593 break;
595 case 'B': // bold
596 setAttributes();
597 maFontModel.mbBold = !maFontModel.mbBold;
598 break;
599 case 'I': // italic
600 setAttributes();
601 maFontModel.mbItalic = !maFontModel.mbItalic;
602 break;
603 case 'U': // underline
604 setAttributes();
605 maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_single) ? XML_none : XML_single;
606 break;
607 case 'E': // double underline
608 setAttributes();
609 maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_double) ? XML_none : XML_double;
610 break;
611 case 'S': // strikeout
612 setAttributes();
613 maFontModel.mbStrikeout = !maFontModel.mbStrikeout;
614 break;
615 case 'X': // superscript
616 setAttributes();
617 maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_superscript) ? XML_baseline : XML_superscript;
618 break;
619 case 'Y': // subsrcipt
620 setAttributes();
621 maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_subscript) ? XML_baseline : XML_subscript;
622 break;
623 case 'O': // outlined
624 setAttributes();
625 maFontModel.mbOutline = !maFontModel.mbOutline;
626 break;
627 case 'H': // shadow
628 setAttributes();
629 maFontModel.mbShadow = !maFontModel.mbShadow;
630 break;
632 case 'K': // text color (not in BIFF)
633 if( pcChar + 6 < pcEnd )
635 setAttributes();
636 // eat the following 6 characters
637 convertFontColor( OUString( pcChar + 1, 6 ) );
638 pcChar += 6;
640 break;
642 case '\"': // font name
643 aFontName.setLength( 0 );
644 aFontStyle.setLength( 0 );
645 eState = STATE_FONTNAME;
646 break;
647 default:
648 if( ('0' <= cChar) && (cChar <= '9') ) // font size
650 nFontHeight = cChar - '0';
651 eState = STATE_FONTHEIGHT;
655 break;
657 case STATE_FONTNAME:
659 switch( cChar )
661 case '\"':
662 setAttributes();
663 convertFontName( aFontName.makeStringAndClear() );
664 eState = STATE_TEXT;
665 break;
666 case ',':
667 eState = STATE_FONTSTYLE;
668 break;
669 default:
670 aFontName.append( cChar );
673 break;
675 case STATE_FONTSTYLE:
677 switch( cChar )
679 case '\"':
680 setAttributes();
681 convertFontName( aFontName.makeStringAndClear() );
682 convertFontStyle( aFontStyle.makeStringAndClear() );
683 eState = STATE_TEXT;
684 break;
685 default:
686 aFontStyle.append( cChar );
689 break;
691 case STATE_FONTHEIGHT:
693 if( ('0' <= cChar) && (cChar <= '9') )
695 if( nFontHeight >= 0 )
697 nFontHeight *= 10;
698 nFontHeight += (cChar - '0');
699 if( nFontHeight > 1000 )
700 nFontHeight = -1;
703 else
705 if( nFontHeight > 0 )
707 setAttributes();
708 maFontModel.mfHeight = nFontHeight;
710 --pcChar;
711 eState = STATE_TEXT;
714 break;
718 // finalize
719 finalizePortion();
720 maPortions[ HF_LEFT ].mfTotalHeight += getCurrHeight( HF_LEFT );
721 maPortions[ HF_CENTER ].mfTotalHeight += getCurrHeight( HF_CENTER );
722 maPortions[ HF_RIGHT ].mfTotalHeight += getCurrHeight( HF_RIGHT );
724 return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight,
725 ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) );
728 // private --------------------------------------------------------------------
730 double HeaderFooterParser::getCurrHeight( HFPortionId ePortion ) const
732 double fMaxHt = maPortions[ ePortion ].mfCurrHeight;
733 return (fMaxHt == 0.0) ? maFontModel.mfHeight : fMaxHt;
736 void HeaderFooterParser::updateCurrHeight( HFPortionId ePortion )
738 double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight;
739 rfMaxHt = ::std::max( rfMaxHt, maFontModel.mfHeight );
742 void HeaderFooterParser::updateCurrHeight()
744 updateCurrHeight( meCurrPortion );
747 void HeaderFooterParser::setAttributes()
749 Reference<text::XTextRange> xRange = getStartPos();
750 getEndPos()->gotoRange( xRange, false );
751 getEndPos()->gotoEnd( true );
752 if( !getEndPos()->isCollapsed() )
754 Font aFont( *this, maFontModel );
755 aFont.finalizeImport();
756 PropertySet aPropSet( getEndPos() );
757 aFont.writeToPropertySet( aPropSet );
758 getStartPos()->gotoEnd( false );
759 getEndPos()->gotoEnd( false );
763 void HeaderFooterParser::appendText()
765 if( !maBuffer.isEmpty() )
767 getEndPos()->gotoEnd( false );
768 getEndPos()->setString( maBuffer.makeStringAndClear() );
769 updateCurrHeight();
773 void HeaderFooterParser::appendLineBreak()
775 getEndPos()->gotoEnd( false );
776 getEndPos()->setString( OUString( '\n' ) );
777 getPortion().mfTotalHeight += getCurrHeight( meCurrPortion ); // add the current line height.
778 getPortion().mfCurrHeight = 0;
781 Reference<text::XTextContent> HeaderFooterParser::createField( const OUString& rServiceName ) const
783 Reference<text::XTextContent> xContent;
786 xContent.set( getBaseFilter().getModelFactory()->createInstance( rServiceName ), UNO_QUERY_THROW );
788 catch( Exception& )
790 OSL_FAIL( OStringBuffer( "HeaderFooterParser::createField - error while creating text field \"" ).
791 append( OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US ) ).
792 append( '"' ).getStr() );
794 return xContent;
797 void HeaderFooterParser::appendField( const Reference<text::XTextContent>& rxContent )
799 getEndPos()->gotoEnd( false );
802 Reference<text::XTextRange> xRange( getEndPos(), UNO_QUERY_THROW );
803 getPortion().mxText->insertTextContent( xRange, rxContent, false );
804 updateCurrHeight();
806 catch( Exception& )
811 void HeaderFooterParser::convertFontName( const OUString& rName )
813 if( !rName.isEmpty() )
815 // single dash is document default font
816 if( (rName.getLength() == 1) && (rName[ 0 ] == '-') )
817 maFontModel.maName = getStyles().getDefaultFontModel().maName;
818 else
819 maFontModel.maName = rName;
823 void HeaderFooterParser::convertFontStyle( const OUString& rStyle )
825 maFontModel.mbBold = maFontModel.mbItalic = false;
826 if (rStyle.isEmpty())
827 return;
828 for( sal_Int32 nPos{ 0 }; nPos>=0; )
830 OString aToken = OUStringToOString( rStyle.getToken( 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
831 if( !aToken.isEmpty() )
833 if( maBoldNames.count( aToken ) > 0 )
834 maFontModel.mbBold = true;
835 else if( maItalicNames.count( aToken ) > 0 )
836 maFontModel.mbItalic = true;
841 void HeaderFooterParser::convertFontColor( const OUString& rColor )
843 OSL_ENSURE( rColor.getLength() == 6, "HeaderFooterParser::convertFontColor - invalid font color code" );
844 if( (rColor[ 2 ] == '+') || (rColor[ 2 ] == '-') )
845 // theme color: TTSNNN (TT = decimal theme index, S = +/-, NNN = decimal tint/shade in percent)
846 maFontModel.maColor.setTheme(
847 rColor.copy( 0, 2 ).toInt32(),
848 static_cast< double >( rColor.copy( 2 ).toInt32() ) / 100.0 );
849 else
850 // RGB color: RRGGBB
851 maFontModel.maColor.setRgb( ::Color(rColor.toUInt32( 16 )) );
854 void HeaderFooterParser::finalizePortion()
856 appendText();
857 setAttributes();
860 void HeaderFooterParser::setNewPortion( HFPortionId ePortion )
862 if( ePortion != meCurrPortion )
864 finalizePortion();
865 meCurrPortion = ePortion;
866 maFontModel = getStyles().getDefaultFontModel();
870 PageSettingsConverter::HFHelperData::HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId ) :
871 mnLeftPropId( nLeftPropId ),
872 mnRightPropId( nRightPropId ),
873 mnHeight( 0 ),
874 mnBodyDist( 0 ),
875 mbHasContent( false ),
876 mbShareOddEven( false ),
877 mbDynamicHeight( false )
881 PageSettingsConverter::PageSettingsConverter( const WorkbookHelper& rHelper ) :
882 WorkbookHelper( rHelper ),
883 mxHFParser( new HeaderFooterParser( rHelper ) ),
884 maHeaderData( PROP_LeftPageHeaderContent, PROP_RightPageHeaderContent ),
885 maFooterData( PROP_LeftPageFooterContent, PROP_RightPageFooterContent )
889 PageSettingsConverter::~PageSettingsConverter()
893 void PageSettingsConverter::writePageSettingsProperties(
894 PropertySet& rPropSet, const PageSettingsModel& rModel, WorksheetType eSheetType )
896 // special handling for chart sheets
897 bool bChartSheet = eSheetType == WorksheetType::Chart;
899 // printout scaling
900 if( bChartSheet )
902 // always fit chart sheet to 1 page
903 rPropSet.setProperty< sal_Int16 >( PROP_ScaleToPages, 1 );
905 else if( rModel.mbFitToPages )
907 // fit to number of pages
908 rPropSet.setProperty( PROP_ScaleToPagesX, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToWidth, 0, 1000 ) );
909 rPropSet.setProperty( PROP_ScaleToPagesY, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToHeight, 0, 1000 ) );
911 else
913 // scale may be 0 which indicates uninitialized
914 sal_Int16 nScale = (rModel.mnScale > 0) ? getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnScale, 10, 400 ) : 100;
915 rPropSet.setProperty( PROP_PageScale, nScale );
918 // paper orientation
919 bool bLandscape = rModel.mnOrientation == XML_landscape;
920 // default orientation for current sheet type (chart sheets default to landscape)
921 if( bChartSheet && ( !rModel.mbValidSettings || (rModel.mnOrientation == XML_default) ) )
922 bLandscape = true;
924 // paper size
925 if( !rModel.mbValidSettings )
927 awt::Size aSize;
928 bool bValid = false;
930 if( 0 < rModel.mnPaperSize )
932 const msfilter::util::ApiPaperSize& rPaperSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex( rModel.mnPaperSize );
933 aSize = awt::Size( rPaperSize.mnWidth, rPaperSize.mnHeight );
934 bValid = ( rPaperSize.mnWidth != 0 && rPaperSize.mnHeight != 0 );
936 if( rModel.mnPaperWidth > 0 && rModel.mnPaperHeight > 0 )
938 aSize = awt::Size( rModel.mnPaperWidth, rModel.mnPaperHeight );
939 bValid = true;
942 if( bValid )
944 if( bLandscape )
945 ::std::swap( aSize.Width, aSize.Height );
946 rPropSet.setProperty( PROP_Size, aSize );
950 // header/footer
951 convertHeaderFooterData( rPropSet, maHeaderData, rModel.maOddHeader, rModel.maEvenHeader, rModel.mbUseEvenHF, rModel.mfTopMargin, rModel.mfHeaderMargin );
952 convertHeaderFooterData( rPropSet, maFooterData, rModel.maOddFooter, rModel.maEvenFooter, rModel.mbUseEvenHF, rModel.mfBottomMargin, rModel.mfFooterMargin );
954 // write all properties to property set
955 const UnitConverter& rUnitConv = getUnitConverter();
956 PropertyMap aPropMap;
957 aPropMap.setProperty( PROP_IsLandscape, bLandscape);
958 aPropMap.setProperty( PROP_FirstPageNumber, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mbUseFirstPage ? rModel.mnFirstPage : 0, 0, 9999 ));
959 aPropMap.setProperty( PROP_PrintDownFirst, (rModel.mnPageOrder == XML_downThenOver));
960 aPropMap.setProperty( PROP_PrintAnnotations, (rModel.mnCellComments == XML_asDisplayed));
961 aPropMap.setProperty( PROP_CenterHorizontally, rModel.mbHorCenter);
962 aPropMap.setProperty( PROP_CenterVertically, rModel.mbVerCenter);
963 aPropMap.setProperty( PROP_PrintGrid, (!bChartSheet && rModel.mbPrintGrid)); // no gridlines in chart sheets
964 aPropMap.setProperty( PROP_PrintHeaders, (!bChartSheet && rModel.mbPrintHeadings)); // no column/row headings in chart sheets
965 aPropMap.setProperty( PROP_LeftMargin, rUnitConv.scaleToMm100( rModel.mfLeftMargin, Unit::Inch ));
966 aPropMap.setProperty( PROP_RightMargin, rUnitConv.scaleToMm100( rModel.mfRightMargin, Unit::Inch ));
967 // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled
968 aPropMap.setProperty( PROP_TopMargin, rUnitConv.scaleToMm100( maHeaderData.mbHasContent ? rModel.mfHeaderMargin : rModel.mfTopMargin, Unit::Inch ));
969 // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled
970 aPropMap.setProperty( PROP_BottomMargin, rUnitConv.scaleToMm100( maFooterData.mbHasContent ? rModel.mfFooterMargin : rModel.mfBottomMargin, Unit::Inch ));
971 aPropMap.setProperty( PROP_HeaderIsOn, maHeaderData.mbHasContent);
972 aPropMap.setProperty( PROP_HeaderIsShared, maHeaderData.mbShareOddEven);
973 aPropMap.setProperty( PROP_HeaderIsDynamicHeight, maHeaderData.mbDynamicHeight);
974 aPropMap.setProperty( PROP_HeaderHeight, maHeaderData.mnHeight);
975 aPropMap.setProperty( PROP_HeaderBodyDistance, maHeaderData.mnBodyDist);
976 aPropMap.setProperty( PROP_FooterIsOn, maFooterData.mbHasContent);
977 aPropMap.setProperty( PROP_FooterIsShared, maFooterData.mbShareOddEven);
978 aPropMap.setProperty( PROP_FooterIsDynamicHeight, maFooterData.mbDynamicHeight);
979 aPropMap.setProperty( PROP_FooterHeight, maFooterData.mnHeight);
980 aPropMap.setProperty( PROP_FooterBodyDistance, maFooterData.mnBodyDist);
981 // background image
982 if (rModel.mxGraphic.is())
984 aPropMap.setProperty(PROP_BackGraphic, rModel.mxGraphic);
985 aPropMap.setProperty(PROP_BackGraphicLocation, css::style::GraphicLocation_TILED);
988 rPropSet.setProperties( aPropMap );
991 void PageSettingsConverter::convertHeaderFooterData(
992 PropertySet& rPropSet, HFHelperData& orHFData,
993 const OUString& rOddContent, const OUString& rEvenContent, bool bUseEvenContent,
994 double fPageMargin, double fContentMargin )
996 bool bHasOddContent = !rOddContent.isEmpty();
997 bool bHasEvenContent = bUseEvenContent && !rEvenContent.isEmpty();
999 sal_Int32 nOddHeight = bHasOddContent ? writeHeaderFooter( rPropSet, orHFData.mnRightPropId, rOddContent ) : 0;
1000 sal_Int32 nEvenHeight = bHasEvenContent ? writeHeaderFooter( rPropSet, orHFData.mnLeftPropId, rEvenContent ) : 0;
1002 orHFData.mnHeight = 750;
1003 orHFData.mnBodyDist = 250;
1004 orHFData.mbHasContent = bHasOddContent || bHasEvenContent;
1005 orHFData.mbShareOddEven = !bUseEvenContent;
1006 orHFData.mbDynamicHeight = true;
1008 if( orHFData.mbHasContent )
1010 // use maximum height of odd/even header/footer
1011 orHFData.mnHeight = ::std::max( nOddHeight, nEvenHeight );
1012 /* Calc contains distance between bottom of header and top of page
1013 body in "HeaderBodyDistance" property, and distance between bottom
1014 of page body and top of footer in "FooterBodyDistance" property */
1015 orHFData.mnBodyDist = getUnitConverter().scaleToMm100( fPageMargin - fContentMargin, Unit::Inch ) - orHFData.mnHeight;
1016 /* #i23296# Distance less than 0 means, header or footer overlays page
1017 body. As this is not possible in Calc, set fixed header or footer
1018 height (crop header/footer) to get correct top position of page body. */
1019 orHFData.mbDynamicHeight = orHFData.mnBodyDist >= 0;
1020 /* "HeaderHeight" property is in fact distance from top of header to
1021 top of page body (including "HeaderBodyDistance").
1022 "FooterHeight" property is in fact distance from bottom of page
1023 body to bottom of footer (including "FooterBodyDistance"). */
1024 orHFData.mnHeight += orHFData.mnBodyDist;
1025 // negative body distance not allowed
1026 orHFData.mnBodyDist = ::std::max< sal_Int32 >( orHFData.mnBodyDist, 0 );
1030 sal_Int32 PageSettingsConverter::writeHeaderFooter(
1031 PropertySet& rPropSet, sal_Int32 nPropId, const OUString& rContent )
1033 OSL_ENSURE( !rContent.isEmpty(), "PageSettingsConverter::writeHeaderFooter - empty h/f string found" );
1034 sal_Int32 nHeight = 0;
1035 if( !rContent.isEmpty() )
1037 Reference<sheet::XHeaderFooterContent> xHFContent(rPropSet.getAnyProperty(nPropId), UNO_QUERY);
1038 if( xHFContent.is() )
1040 double fTotalHeight = mxHFParser->parse( xHFContent, rContent );
1041 rPropSet.setProperty( nPropId, xHFContent );
1042 nHeight = getUnitConverter().scaleToMm100( fTotalHeight, Unit::Point );
1045 return nHeight;
1048 } // namespace xls
1049 } // namespace oox
1051 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */