nss: upgrade to release 3.73
[LibreOffice.git] / sc / source / filter / oox / pagesettings.cxx
blobb19caea836344778676ecb3ae662facf93e10812
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/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>
54 namespace oox::xls {
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::uno;
60 using ::oox::core::Relations;
62 namespace {
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.
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;
73 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFEVEN = 0x0001;
74 const sal_uInt16 BIFF12_HEADERFOOTER_DIFFFIRST = 0x0002;
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
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;
93 } // namespace
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 )
130 void PageSettingsModel::setBiffPrintErrors( sal_uInt8 nPrintErrors )
132 static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA };
133 mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none );
136 PageSettings::PageSettings( const WorksheetHelper& rHelper ) :
137 WorksheetHelper( rHelper )
141 void PageSettings::importPrintOptions( const AttributeList& rAttribs )
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 );
149 void PageSettings::importPageMargins( const AttributeList& rAttribs )
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 );
159 void PageSettings::importPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
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 );
187 void PageSettings::importChartPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
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 );
209 void PageSettings::importHeaderFooter( const AttributeList& rAttribs )
211 maModel.mbUseEvenHF = rAttribs.getBool( XML_differentOddEven, false );
212 maModel.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false );
215 void PageSettings::importHeaderFooterCharacters( const OUString& rChars, sal_Int32 nElement )
217 switch( nElement )
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;
228 void PageSettings::importPicture( const Relations& rRelations, const AttributeList& rAttribs )
230 importPictureData( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
233 void PageSettings::importPageMargins( SequenceInputStream& rStrm )
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();
243 void PageSettings::importPrintOptions( SequenceInputStream& rStrm )
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 );
253 void PageSettings::importPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
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 );
278 void PageSettings::importChartPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
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 );
298 void PageSettings::importHeaderFooter( SequenceInputStream& rStrm )
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 );
309 void PageSettings::importPicture( const Relations& rRelations, SequenceInputStream& rStrm )
311 importPictureData( rRelations, BiffHelper::readString( rStrm ) );
314 void PageSettings::setFitToPagesMode( bool bFitToPages )
316 maModel.mbFitToPages = bFitToPages;
319 void PageSettings::finalizeImport()
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();
329 Reference<style::XStyle> xStyle = createStyleObject(aStyleName, true);
330 PropertySet aStyleProps( xStyle );
331 getPageSettingsConverter().writePageSettingsProperties( aStyleProps, maModel, getSheetType() );
333 // Set page style name to the sheet.
334 SCTAB nTab = getSheetIndex();
335 getScDocument().SetPageStyle(nTab, aStyleName);
338 void PageSettings::importPictureData( const Relations& rRelations, const OUString& rRelId )
340 OUString aPicturePath = rRelations.getFragmentPathFromRelId(rRelId);
341 if (!aPicturePath.isEmpty())
343 maModel.mxGraphic = getBaseFilter().getGraphicHelper().importEmbeddedGraphic(aPicturePath);
347 namespace {
349 enum HFPortionId
351 HF_LEFT,
352 HF_CENTER,
353 HF_RIGHT,
354 HF_COUNT
357 struct HFPortionInfo
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.
365 bool initialize( const Reference<text::XText>& rxText );
370 bool HFPortionInfo::initialize( const Reference<text::XText>& rxText )
372 mfTotalHeight = mfCurrHeight = 0.0;
373 mxText = rxText;
374 if( mxText.is() )
376 mxStart = mxText->createTextCursor();
377 mxEnd = mxText->createTextCursor();
379 bool bRet = mxText.is() && mxStart.is() && mxEnd.is();
380 OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" );
381 return bRet;
384 class HeaderFooterParser : public WorkbookHelper
386 public:
387 explicit HeaderFooterParser( const WorkbookHelper& rHelper );
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 );
395 private:
396 /** Returns the current edit engine text object. */
397 HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; }
398 /** Returns the start cursor of the current text range. */
399 const Reference<text::XTextCursor>& getStartPos() { return getPortion().mxStart; }
400 /** Returns the end cursor of the current text range. */
401 const Reference<text::XTextCursor>& getEndPos() { return getPortion().mxEnd; }
403 /** Returns the current line height of the specified portion. */
404 double getCurrHeight( HFPortionId ePortion ) const;
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();
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();
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 );
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 );
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 );
435 private:
436 typedef ::std::vector< HFPortionInfo > HFPortionInfoVec;
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.
446 namespace {
448 // different names for bold font style (lowercase)
449 const char* const sppcBoldNames[] =
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'
460 // different names for italic font style (lowercase)
461 const char* const sppcItalicNames[] =
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'
471 } // namespace
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" );
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 )
488 double HeaderFooterParser::parse( const Reference<sheet::XHeaderFooterContent>& rxContext, const OUString& rData )
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;
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
503 /** State of the parser. */
504 enum
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).
512 eState = STATE_TEXT;
514 const sal_Unicode* pcChar = rData.getStr();
515 const sal_Unicode* pcEnd = pcChar + rData.getLength();
516 for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar )
518 sal_Unicode cChar = *pcChar;
519 switch( eState )
521 case STATE_TEXT:
523 switch( cChar )
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 );
537 break;
539 case STATE_TOKEN:
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 )
548 case '&': maBuffer.append( cChar ); break; // the '&' character
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
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;
564 case 'F': // file name
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 );
571 break;
572 case 'Z': // file path (without file name), OOXML, BIFF12, and BIFF8 only
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;
584 break;
585 case 'D': // date
587 Reference<text::XTextContent> xContent = createField( gaDateTimeService );
588 PropertySet aPropSet( xContent );
589 aPropSet.setProperty( PROP_IsDate, true );
590 appendField( xContent );
592 break;
593 case 'T': // time
595 Reference<text::XTextContent> xContent = createField( gaDateTimeService );
596 PropertySet aPropSet( xContent );
597 aPropSet.setProperty( PROP_IsDate, false );
598 appendField( xContent );
600 break;
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;
639 case 'K': // text color (not in BIFF)
640 if( pcChar + 6 < pcEnd )
642 setAttributes();
643 // eat the following 6 characters
644 convertFontColor( OUString( pcChar + 1, 6 ) );
645 pcChar += 6;
647 break;
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
657 nFontHeight = cChar - '0';
658 eState = STATE_FONTHEIGHT;
662 break;
664 case STATE_FONTNAME:
666 switch( cChar )
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 );
680 break;
682 case STATE_FONTSTYLE:
684 switch( cChar )
686 case '\"':
687 setAttributes();
688 convertFontName( aFontName.makeStringAndClear() );
689 convertFontStyle( aFontStyle.makeStringAndClear() );
690 eState = STATE_TEXT;
691 break;
692 default:
693 aFontStyle.append( cChar );
696 break;
698 case STATE_FONTHEIGHT:
700 if( ('0' <= cChar) && (cChar <= '9') )
702 if( nFontHeight >= 0 )
704 nFontHeight *= 10;
705 nFontHeight += (cChar - '0');
706 if( nFontHeight > 1000 )
707 nFontHeight = -1;
710 else
712 if( nFontHeight > 0 )
714 setAttributes();
715 maFontModel.mfHeight = nFontHeight;
717 --pcChar;
718 eState = STATE_TEXT;
721 break;
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 );
731 return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight,
732 ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) );
735 // private --------------------------------------------------------------------
737 double HeaderFooterParser::getCurrHeight( HFPortionId ePortion ) const
739 double fMaxHt = maPortions[ ePortion ].mfCurrHeight;
740 return (fMaxHt == 0.0) ? maFontModel.mfHeight : fMaxHt;
743 void HeaderFooterParser::updateCurrHeight( HFPortionId ePortion )
745 double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight;
746 rfMaxHt = ::std::max( rfMaxHt, maFontModel.mfHeight );
749 void HeaderFooterParser::updateCurrHeight()
751 updateCurrHeight( meCurrPortion );
754 void HeaderFooterParser::setAttributes()
756 Reference<text::XTextRange> xRange = getStartPos();
757 getEndPos()->gotoRange( xRange, false );
758 getEndPos()->gotoEnd( true );
759 if( !getEndPos()->isCollapsed() )
761 Font aFont( *this, maFontModel );
762 aFont.finalizeImport();
763 PropertySet aPropSet( getEndPos() );
764 aFont.writeToPropertySet( aPropSet );
765 getStartPos()->gotoEnd( false );
766 getEndPos()->gotoEnd( false );
770 void HeaderFooterParser::appendText()
772 if( !maBuffer.isEmpty() )
774 getEndPos()->gotoEnd( false );
775 getEndPos()->setString( maBuffer.makeStringAndClear() );
776 updateCurrHeight();
780 void HeaderFooterParser::appendLineBreak()
782 getEndPos()->gotoEnd( false );
783 getEndPos()->setString( OUString( '\n' ) );
784 getPortion().mfTotalHeight += getCurrHeight( meCurrPortion ); // add the current line height.
785 getPortion().mfCurrHeight = 0;
788 Reference<text::XTextContent> HeaderFooterParser::createField( const OUString& rServiceName ) const
790 Reference<text::XTextContent> xContent;
793 xContent.set( getBaseFilter().getModelFactory()->createInstance( rServiceName ), UNO_QUERY_THROW );
795 catch( Exception& )
797 OSL_FAIL( OStringBuffer( "HeaderFooterParser::createField - error while creating text field \"" ).
798 append( OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US ) ).
799 append( '"' ).getStr() );
801 return xContent;
804 void HeaderFooterParser::appendField( const Reference<text::XTextContent>& rxContent )
806 getEndPos()->gotoEnd( false );
809 Reference<text::XTextRange> xRange( getEndPos(), UNO_QUERY_THROW );
810 getPortion().mxText->insertTextContent( xRange, rxContent, false );
811 updateCurrHeight();
813 catch( Exception& )
818 void HeaderFooterParser::convertFontName( const OUString& rName )
820 if( !rName.isEmpty() )
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;
830 void HeaderFooterParser::convertFontStyle( const OUString& rStyle )
832 maFontModel.mbBold = maFontModel.mbItalic = false;
833 if (rStyle.isEmpty())
834 return;
835 for( sal_Int32 nPos{ 0 }; nPos>=0; )
837 OString aToken = OUStringToOString( rStyle.getToken( 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
838 if( !aToken.isEmpty() )
840 if( maBoldNames.count( aToken ) > 0 )
841 maFontModel.mbBold = true;
842 else if( maItalicNames.count( aToken ) > 0 )
843 maFontModel.mbItalic = true;
848 void HeaderFooterParser::convertFontColor( const OUString& rColor )
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(rColor.toUInt32( 16 )) );
861 void HeaderFooterParser::finalizePortion()
863 appendText();
864 setAttributes();
867 void HeaderFooterParser::setNewPortion( HFPortionId ePortion )
869 if( ePortion != meCurrPortion )
871 finalizePortion();
872 meCurrPortion = ePortion;
873 maFontModel = getStyles().getDefaultFontModel();
877 PageSettingsConverter::HFHelperData::HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId ) :
878 mnLeftPropId( nLeftPropId ),
879 mnRightPropId( nRightPropId ),
880 mnHeight( 0 ),
881 mnBodyDist( 0 ),
882 mbHasContent( false ),
883 mbShareOddEven( false ),
884 mbDynamicHeight( false )
888 PageSettingsConverter::PageSettingsConverter( const WorkbookHelper& rHelper ) :
889 WorkbookHelper( rHelper ),
890 mxHFParser( new HeaderFooterParser( rHelper ) ),
891 maHeaderData( PROP_LeftPageHeaderContent, PROP_RightPageHeaderContent ),
892 maFooterData( PROP_LeftPageFooterContent, PROP_RightPageFooterContent )
896 PageSettingsConverter::~PageSettingsConverter()
900 void PageSettingsConverter::writePageSettingsProperties(
901 PropertySet& rPropSet, const PageSettingsModel& rModel, WorksheetType eSheetType )
903 // special handling for chart sheets
904 bool bChartSheet = eSheetType == WorksheetType::Chart;
906 // printout scaling
907 if( bChartSheet )
909 // always fit chart sheet to 1 page
910 rPropSet.setProperty< sal_Int16 >( PROP_ScaleToPages, 1 );
912 else if( rModel.mbFitToPages )
914 // fit to number of pages
915 rPropSet.setProperty( PROP_ScaleToPagesX, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToWidth, 0, 1000 ) );
916 rPropSet.setProperty( PROP_ScaleToPagesY, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToHeight, 0, 1000 ) );
918 else
920 // scale may be 0 which indicates uninitialized
921 sal_Int16 nScale = (rModel.mnScale > 0) ? getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnScale, 10, 400 ) : 100;
922 rPropSet.setProperty( PROP_PageScale, nScale );
925 // paper orientation
926 bool bLandscape = rModel.mnOrientation == XML_landscape;
927 // default orientation for current sheet type (chart sheets default to landscape)
928 if( bChartSheet && ( !rModel.mbValidSettings || (rModel.mnOrientation == XML_default) ) )
929 bLandscape = true;
931 // paper size
932 if( !rModel.mbValidSettings )
934 awt::Size aSize;
935 bool bValid = false;
937 if( 0 < rModel.mnPaperSize )
939 const msfilter::util::ApiPaperSize& rPaperSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex( rModel.mnPaperSize );
940 aSize = awt::Size( rPaperSize.mnWidth, rPaperSize.mnHeight );
941 bValid = ( rPaperSize.mnWidth != 0 && rPaperSize.mnHeight != 0 );
943 if( rModel.mnPaperWidth > 0 && rModel.mnPaperHeight > 0 )
945 aSize = awt::Size( rModel.mnPaperWidth, rModel.mnPaperHeight );
946 bValid = true;
949 if( bValid )
951 if( bLandscape )
952 ::std::swap( aSize.Width, aSize.Height );
953 rPropSet.setProperty( PROP_Size, aSize );
957 // header/footer
958 convertHeaderFooterData( rPropSet, maHeaderData, rModel.maOddHeader, rModel.maEvenHeader, rModel.mbUseEvenHF, rModel.mfTopMargin, rModel.mfHeaderMargin );
959 convertHeaderFooterData( rPropSet, maFooterData, rModel.maOddFooter, rModel.maEvenFooter, rModel.mbUseEvenHF, rModel.mfBottomMargin, rModel.mfFooterMargin );
961 // write all properties to property set
962 const UnitConverter& rUnitConv = getUnitConverter();
963 PropertyMap aPropMap;
964 aPropMap.setProperty( PROP_IsLandscape, bLandscape);
965 aPropMap.setProperty( PROP_FirstPageNumber, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mbUseFirstPage ? rModel.mnFirstPage : 0, 0, 9999 ));
966 aPropMap.setProperty( PROP_PrintDownFirst, (rModel.mnPageOrder == XML_downThenOver));
967 aPropMap.setProperty( PROP_PrintAnnotations, (rModel.mnCellComments == XML_asDisplayed));
968 aPropMap.setProperty( PROP_CenterHorizontally, rModel.mbHorCenter);
969 aPropMap.setProperty( PROP_CenterVertically, rModel.mbVerCenter);
970 aPropMap.setProperty( PROP_PrintGrid, (!bChartSheet && rModel.mbPrintGrid)); // no gridlines in chart sheets
971 aPropMap.setProperty( PROP_PrintHeaders, (!bChartSheet && rModel.mbPrintHeadings)); // no column/row headings in chart sheets
972 aPropMap.setProperty( PROP_LeftMargin, rUnitConv.scaleToMm100( rModel.mfLeftMargin, Unit::Inch ));
973 aPropMap.setProperty( PROP_RightMargin, rUnitConv.scaleToMm100( rModel.mfRightMargin, Unit::Inch ));
974 // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled
975 aPropMap.setProperty( PROP_TopMargin, rUnitConv.scaleToMm100( maHeaderData.mbHasContent ? rModel.mfHeaderMargin : rModel.mfTopMargin, Unit::Inch ));
976 // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled
977 aPropMap.setProperty( PROP_BottomMargin, rUnitConv.scaleToMm100( maFooterData.mbHasContent ? rModel.mfFooterMargin : rModel.mfBottomMargin, Unit::Inch ));
978 aPropMap.setProperty( PROP_HeaderIsOn, maHeaderData.mbHasContent);
979 aPropMap.setProperty( PROP_HeaderIsShared, maHeaderData.mbShareOddEven);
980 aPropMap.setProperty( PROP_HeaderIsDynamicHeight, maHeaderData.mbDynamicHeight);
981 aPropMap.setProperty( PROP_HeaderHeight, maHeaderData.mnHeight);
982 aPropMap.setProperty( PROP_HeaderBodyDistance, maHeaderData.mnBodyDist);
983 aPropMap.setProperty( PROP_FooterIsOn, maFooterData.mbHasContent);
984 aPropMap.setProperty( PROP_FooterIsShared, maFooterData.mbShareOddEven);
985 aPropMap.setProperty( PROP_FooterIsDynamicHeight, maFooterData.mbDynamicHeight);
986 aPropMap.setProperty( PROP_FooterHeight, maFooterData.mnHeight);
987 aPropMap.setProperty( PROP_FooterBodyDistance, maFooterData.mnBodyDist);
988 // background image
989 if (rModel.mxGraphic.is())
991 aPropMap.setProperty(PROP_BackGraphic, rModel.mxGraphic);
992 aPropMap.setProperty(PROP_BackGraphicLocation, css::style::GraphicLocation_TILED);
995 rPropSet.setProperties( aPropMap );
998 void PageSettingsConverter::convertHeaderFooterData(
999 PropertySet& rPropSet, HFHelperData& orHFData,
1000 const OUString& rOddContent, const OUString& rEvenContent, bool bUseEvenContent,
1001 double fPageMargin, double fContentMargin )
1003 bool bHasOddContent = !rOddContent.isEmpty();
1004 bool bHasEvenContent = bUseEvenContent && !rEvenContent.isEmpty();
1006 sal_Int32 nOddHeight = bHasOddContent ? writeHeaderFooter( rPropSet, orHFData.mnRightPropId, rOddContent ) : 0;
1007 sal_Int32 nEvenHeight = bHasEvenContent ? writeHeaderFooter( rPropSet, orHFData.mnLeftPropId, rEvenContent ) : 0;
1009 orHFData.mnHeight = 750;
1010 orHFData.mnBodyDist = 250;
1011 orHFData.mbHasContent = bHasOddContent || bHasEvenContent;
1012 orHFData.mbShareOddEven = !bUseEvenContent;
1013 orHFData.mbDynamicHeight = true;
1015 if( !orHFData.mbHasContent )
1016 return;
1018 // use maximum height of odd/even header/footer
1019 orHFData.mnHeight = ::std::max( nOddHeight, nEvenHeight );
1020 /* Calc contains distance between bottom of header and top of page
1021 body in "HeaderBodyDistance" property, and distance between bottom
1022 of page body and top of footer in "FooterBodyDistance" property */
1023 orHFData.mnBodyDist = getUnitConverter().scaleToMm100( fPageMargin - fContentMargin, Unit::Inch ) - orHFData.mnHeight;
1024 /* #i23296# Distance less than 0 means, header or footer overlays page
1025 body. As this is not possible in Calc, set fixed header or footer
1026 height (crop header/footer) to get correct top position of page body. */
1027 orHFData.mbDynamicHeight = orHFData.mnBodyDist >= 0;
1028 /* "HeaderHeight" property is in fact distance from top of header to
1029 top of page body (including "HeaderBodyDistance").
1030 "FooterHeight" property is in fact distance from bottom of page
1031 body to bottom of footer (including "FooterBodyDistance"). */
1032 orHFData.mnHeight += orHFData.mnBodyDist;
1033 // negative body distance not allowed
1034 orHFData.mnBodyDist = ::std::max< sal_Int32 >( orHFData.mnBodyDist, 0 );
1037 sal_Int32 PageSettingsConverter::writeHeaderFooter(
1038 PropertySet& rPropSet, sal_Int32 nPropId, const OUString& rContent )
1040 OSL_ENSURE( !rContent.isEmpty(), "PageSettingsConverter::writeHeaderFooter - empty h/f string found" );
1041 sal_Int32 nHeight = 0;
1042 if( !rContent.isEmpty() )
1044 Reference<sheet::XHeaderFooterContent> xHFContent(rPropSet.getAnyProperty(nPropId), UNO_QUERY);
1045 if( xHFContent.is() )
1047 double fTotalHeight = mxHFParser->parse( xHFContent, rContent );
1048 rPropSet.setProperty( nPropId, xHFContent );
1049 nHeight = getUnitConverter().scaleToMm100( fTotalHeight, Unit::Point );
1052 return nHeight;
1055 } // namespace oox
1057 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */